ha_innodb.cc 213 KB
Newer Older
unknown's avatar
unknown committed
1
/* Copyright (C) 2000-2005 MySQL AB & Innobase Oy
2

3 4 5 6
   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.
7

8 9
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
unknown's avatar
unknown committed
10
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
11
   GNU General Public License for more details.
12

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

unknown's avatar
unknown committed
17
/* This file defines the InnoDB handler: the interface between MySQL and InnoDB
18 19
NOTE: You can only use noninlined InnoDB functions in this file, because we
have disables the InnoDB inlining in this file. */
20

unknown's avatar
unknown committed
21 22
/* TODO list for the InnoDB handler in 5.0:
  - Remove the flag trx->active_trans and look at the InnoDB
23
    trx struct state field
unknown's avatar
unknown committed
24
  - fix savepoint functions to use savepoint storage area
unknown's avatar
unknown committed
25 26 27
  - Find out what kind of problems the OS X case-insensitivity causes to
    table and database names; should we 'normalize' the names like we do
    in Windows?
28
*/
unknown's avatar
unknown committed
29

30
#ifdef USE_PRAGMA_IMPLEMENTATION
31 32 33 34
#pragma implementation				// gcc: Class implementation
#endif

#include "mysql_priv.h"
35 36
#ifdef WITH_INNOBASE_STORAGE_ENGINE

unknown's avatar
unknown committed
37
#include "slave.h"
unknown's avatar
unknown committed
38

39 40 41
#include <m_ctype.h>
#include <hash.h>
#include <myisampack.h>
unknown's avatar
unknown committed
42
#include <mysys_err.h>
43
#include <my_sys.h>
44

45 46
#define MAX_ULONG_BIT ((ulong) 1 << (sizeof(ulong)*8-1))

47
#include "ha_innodb.h"
unknown's avatar
unknown committed
48

unknown's avatar
unknown committed
49 50 51
pthread_mutex_t innobase_share_mutex,	/* to protect innobase_open_files */
		prepare_commit_mutex;	/* to force correct commit order in
					binlog */
52 53 54 55
ulong commit_threads= 0;
pthread_mutex_t commit_threads_m;
pthread_cond_t commit_cond;
pthread_mutex_t commit_cond_m;
56
bool innodb_inited= 0;
unknown's avatar
unknown committed
57

unknown's avatar
unknown committed
58 59 60 61
/*-----------------------------------------------------------------*/
/* These variables are used to implement (semi-)synchronous MySQL binlog
replication for InnoDB tables. */

unknown's avatar
unknown committed
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
pthread_cond_t	innobase_repl_cond;		/* Posix cond variable;
						this variable is signaled
						when enough binlog has been
						sent to slave, so that a
						waiting trx can return the
						'ok' message to the client
						for a commit */
pthread_mutex_t innobase_repl_cond_mutex;	/* Posix cond variable mutex
						that also protects the next
						innobase_repl_... variables */
uint		innobase_repl_state;		/* 1 if synchronous replication
						is switched on and is working
						ok; else 0 */
uint		innobase_repl_file_name_inited	= 0; /* This is set to 1 when
						innobase_repl_file_name
						contains meaningful data */
char*		innobase_repl_file_name;	/* The binlog name up to which
						we have sent some binlog to
						the slave */
my_off_t	innobase_repl_pos;		/* The position in that file
						up to which we have sent the
						binlog to the slave */
uint		innobase_repl_n_wait_threads	= 0; /* This tells how many
						transactions currently are
						waiting for the binlog to be
						sent to the client */
uint		innobase_repl_wait_file_name_inited = 0; /* This is set to 1
						when we know the 'smallest'
						wait position */
char*		innobase_repl_wait_file_name;	/* NULL, or the 'smallest'
						innobase_repl_file_name that
						a transaction is waiting for */
my_off_t	innobase_repl_wait_pos;		/* The smallest position in
						that file that a trx is
						waiting for: the trx can
						proceed and send an 'ok' to
						the client when MySQL has sent
						the binlog up to this position
						to the slave */
unknown's avatar
unknown committed
101 102 103 104
/*-----------------------------------------------------------------*/



105
/* Store MySQL definition of 'byte': in Linux it is char while InnoDB
106 107 108
uses unsigned char; the header univ.i which we include next defines
'byte' as a macro which expands to 'unsigned char' */

109
typedef byte	mysql_byte;
unknown's avatar
unknown committed
110

unknown's avatar
unknown committed
111 112
#define INSIDE_HA_INNOBASE_CC

113
/* Include necessary InnoDB headers */
114
extern "C" {
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
#include "../storage/innobase/include/univ.i"
#include "../storage/innobase/include/os0file.h"
#include "../storage/innobase/include/os0thread.h"
#include "../storage/innobase/include/srv0start.h"
#include "../storage/innobase/include/srv0srv.h"
#include "../storage/innobase/include/trx0roll.h"
#include "../storage/innobase/include/trx0trx.h"
#include "../storage/innobase/include/trx0sys.h"
#include "../storage/innobase/include/mtr0mtr.h"
#include "../storage/innobase/include/row0ins.h"
#include "../storage/innobase/include/row0mysql.h"
#include "../storage/innobase/include/row0sel.h"
#include "../storage/innobase/include/row0upd.h"
#include "../storage/innobase/include/log0log.h"
#include "../storage/innobase/include/lock0lock.h"
#include "../storage/innobase/include/dict0crea.h"
#include "../storage/innobase/include/btr0cur.h"
#include "../storage/innobase/include/btr0btr.h"
#include "../storage/innobase/include/fsp0fsp.h"
#include "../storage/innobase/include/sync0sync.h"
#include "../storage/innobase/include/fil0fil.h"
#include "../storage/innobase/include/trx0xa.h"
unknown's avatar
unknown committed
137
#include "../storage/innobase/include/thr0loc.h"
unknown's avatar
unknown committed
138
#include "../storage/innobase/include/ha_prototypes.h"
139 140 141 142 143
}

#define HA_INNOBASE_ROWS_IN_TABLE 10000 /* to get optimization right */
#define HA_INNOBASE_RANGE_COUNT	  100

unknown's avatar
unknown committed
144
ulong	innobase_large_page_size = 0;
145

unknown's avatar
unknown committed
146 147
/* The default values for the following, type long or longlong, start-up
parameters are declared in mysqld.cc: */
unknown's avatar
unknown committed
148

149
long innobase_mirrored_log_groups, innobase_log_files_in_group,
unknown's avatar
unknown committed
150 151 152 153
	innobase_log_buffer_size, innobase_buffer_pool_awe_mem_mb,
	innobase_additional_mem_pool_size, innobase_file_io_threads,
	innobase_lock_wait_timeout, innobase_force_recovery,
	innobase_open_files;
unknown's avatar
unknown committed
154 155

longlong innobase_buffer_pool_size, innobase_log_file_size;
unknown's avatar
unknown committed
156

unknown's avatar
unknown committed
157 158
/* The default values for the following char* start-up parameters
are determined in innobase_init below: */
unknown's avatar
unknown committed
159

unknown's avatar
unknown committed
160
char*	innobase_data_home_dir			= NULL;
unknown's avatar
unknown committed
161
char*	innobase_data_file_path			= NULL;
unknown's avatar
unknown committed
162
char*	innobase_log_group_home_dir		= NULL;
unknown's avatar
unknown committed
163
char*	innobase_log_arch_dir			= NULL;/* unused */
unknown's avatar
unknown committed
164 165
/* The following has a misleading name: starting from 4.0.5, this also
affects Windows: */
unknown's avatar
unknown committed
166 167 168 169 170
char*	innobase_unix_file_flush_method		= NULL;

/* Below we have boolean-valued start-up parameters, and their default
values */

171
ulong	innobase_fast_shutdown			= 1;
unknown's avatar
unknown committed
172
my_bool innobase_log_archive			= FALSE;/* unused */
unknown's avatar
unknown committed
173 174 175
my_bool innobase_use_doublewrite		= TRUE;
my_bool innobase_use_checksums			= TRUE;
my_bool innobase_use_large_pages		= FALSE;
unknown's avatar
unknown committed
176
my_bool	innobase_use_native_aio			= FALSE;
unknown's avatar
unknown committed
177
my_bool	innobase_file_per_table			= FALSE;
unknown's avatar
unknown committed
178
my_bool innobase_locks_unsafe_for_binlog	= FALSE;
179
my_bool innobase_create_status_file		= FALSE;
180

unknown's avatar
unknown committed
181
static char *internal_innobase_data_file_path	= NULL;
182

183
/* The following counter is used to convey information to InnoDB
184 185 186 187 188
about server activity: in selects it is not sensible to call
srv_active_wake_master_thread after each fetch or search, we only do
it every INNOBASE_WAKE_INTERVAL'th step. */

#define INNOBASE_WAKE_INTERVAL	32
189
ulong	innobase_active_counter	= 0;
190

unknown's avatar
unknown committed
191
static HASH	innobase_open_tables;
192

unknown's avatar
unknown committed
193
#ifdef __NETWARE__	/* some special cleanup for NetWare */
194 195 196
bool nw_panic = FALSE;
#endif

197
static mysql_byte* innobase_get_key(INNOBASE_SHARE *share,uint *length,
unknown's avatar
unknown committed
198
	my_bool not_used __attribute__((unused)));
199 200
static INNOBASE_SHARE *get_share(const char *table_name);
static void free_share(INNOBASE_SHARE *share);
unknown's avatar
unknown committed
201 202 203 204 205
static int innobase_close_connection(THD* thd);
static int innobase_commit(THD* thd, bool all);
static int innobase_rollback(THD* thd, bool all);
static int innobase_rollback_to_savepoint(THD* thd, void *savepoint);
static int innobase_savepoint(THD* thd, void *savepoint);
unknown's avatar
unknown committed
206
static int innobase_release_savepoint(THD* thd, void *savepoint);
207 208
static handler *innobase_create_handler(TABLE_SHARE *table,
                                        MEM_ROOT *mem_root);
unknown's avatar
unknown committed
209

unknown's avatar
unknown committed
210
static const char innobase_hton_name[]= "InnoDB";
unknown's avatar
unknown committed
211
handlerton innobase_hton;
212

213
static handler *innobase_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root)
214
{
215
  return new (mem_root) ha_innobase(table);
216 217 218
}


219 220 221 222 223 224 225 226
/*********************************************************************
Commits a transaction in an InnoDB database. */

void
innobase_commit_low(
/*================*/
	trx_t*	trx);	/* in: transaction handle */

227
SHOW_VAR innodb_status_variables[]= {
228
  {"buffer_pool_pages_data",
unknown's avatar
unknown committed
229
  (char*) &export_vars.innodb_buffer_pool_pages_data,	  SHOW_LONG},
230
  {"buffer_pool_pages_dirty",
unknown's avatar
unknown committed
231
  (char*) &export_vars.innodb_buffer_pool_pages_dirty,	  SHOW_LONG},
232 233 234
  {"buffer_pool_pages_flushed",
  (char*) &export_vars.innodb_buffer_pool_pages_flushed,  SHOW_LONG},
  {"buffer_pool_pages_free",
unknown's avatar
unknown committed
235
  (char*) &export_vars.innodb_buffer_pool_pages_free,	  SHOW_LONG},
236 237 238
  {"buffer_pool_pages_latched",
  (char*) &export_vars.innodb_buffer_pool_pages_latched,  SHOW_LONG},
  {"buffer_pool_pages_misc",
unknown's avatar
unknown committed
239
  (char*) &export_vars.innodb_buffer_pool_pages_misc,	  SHOW_LONG},
240
  {"buffer_pool_pages_total",
unknown's avatar
unknown committed
241
  (char*) &export_vars.innodb_buffer_pool_pages_total,	  SHOW_LONG},
242 243 244 245 246 247 248
  {"buffer_pool_read_ahead_rnd",
  (char*) &export_vars.innodb_buffer_pool_read_ahead_rnd, SHOW_LONG},
  {"buffer_pool_read_ahead_seq",
  (char*) &export_vars.innodb_buffer_pool_read_ahead_seq, SHOW_LONG},
  {"buffer_pool_read_requests",
  (char*) &export_vars.innodb_buffer_pool_read_requests,  SHOW_LONG},
  {"buffer_pool_reads",
unknown's avatar
unknown committed
249
  (char*) &export_vars.innodb_buffer_pool_reads,	  SHOW_LONG},
250
  {"buffer_pool_wait_free",
unknown's avatar
unknown committed
251
  (char*) &export_vars.innodb_buffer_pool_wait_free,	  SHOW_LONG},
252 253 254
  {"buffer_pool_write_requests",
  (char*) &export_vars.innodb_buffer_pool_write_requests, SHOW_LONG},
  {"data_fsyncs",
unknown's avatar
unknown committed
255
  (char*) &export_vars.innodb_data_fsyncs,		  SHOW_LONG},
256
  {"data_pending_fsyncs",
unknown's avatar
unknown committed
257
  (char*) &export_vars.innodb_data_pending_fsyncs,	  SHOW_LONG},
258
  {"data_pending_reads",
unknown's avatar
unknown committed
259
  (char*) &export_vars.innodb_data_pending_reads,	  SHOW_LONG},
260
  {"data_pending_writes",
unknown's avatar
unknown committed
261
  (char*) &export_vars.innodb_data_pending_writes,	  SHOW_LONG},
262
  {"data_read",
unknown's avatar
unknown committed
263
  (char*) &export_vars.innodb_data_read,		  SHOW_LONG},
264
  {"data_reads",
unknown's avatar
unknown committed
265
  (char*) &export_vars.innodb_data_reads,		  SHOW_LONG},
266
  {"data_writes",
unknown's avatar
unknown committed
267
  (char*) &export_vars.innodb_data_writes,		  SHOW_LONG},
268
  {"data_written",
unknown's avatar
unknown committed
269
  (char*) &export_vars.innodb_data_written,		  SHOW_LONG},
270
  {"dblwr_pages_written",
unknown's avatar
unknown committed
271
  (char*) &export_vars.innodb_dblwr_pages_written,	  SHOW_LONG},
272
  {"dblwr_writes",
unknown's avatar
unknown committed
273
  (char*) &export_vars.innodb_dblwr_writes,		  SHOW_LONG},
274
  {"log_waits",
unknown's avatar
unknown committed
275
  (char*) &export_vars.innodb_log_waits,		  SHOW_LONG},
276
  {"log_write_requests",
unknown's avatar
unknown committed
277
  (char*) &export_vars.innodb_log_write_requests,	  SHOW_LONG},
278
  {"log_writes",
unknown's avatar
unknown committed
279
  (char*) &export_vars.innodb_log_writes,		  SHOW_LONG},
280
  {"os_log_fsyncs",
unknown's avatar
unknown committed
281
  (char*) &export_vars.innodb_os_log_fsyncs,		  SHOW_LONG},
282
  {"os_log_pending_fsyncs",
unknown's avatar
unknown committed
283
  (char*) &export_vars.innodb_os_log_pending_fsyncs,	  SHOW_LONG},
284
  {"os_log_pending_writes",
unknown's avatar
unknown committed
285
  (char*) &export_vars.innodb_os_log_pending_writes,	  SHOW_LONG},
286
  {"os_log_written",
unknown's avatar
unknown committed
287
  (char*) &export_vars.innodb_os_log_written,		  SHOW_LONG},
288
  {"page_size",
unknown's avatar
unknown committed
289
  (char*) &export_vars.innodb_page_size,		  SHOW_LONG},
290
  {"pages_created",
unknown's avatar
unknown committed
291
  (char*) &export_vars.innodb_pages_created,		  SHOW_LONG},
292
  {"pages_read",
unknown's avatar
unknown committed
293
  (char*) &export_vars.innodb_pages_read,		  SHOW_LONG},
294
  {"pages_written",
unknown's avatar
unknown committed
295
  (char*) &export_vars.innodb_pages_written,		  SHOW_LONG},
unknown's avatar
unknown committed
296
  {"row_lock_current_waits",
unknown's avatar
unknown committed
297
  (char*) &export_vars.innodb_row_lock_current_waits,	  SHOW_LONG},
unknown's avatar
unknown committed
298
  {"row_lock_time",
unknown's avatar
unknown committed
299
  (char*) &export_vars.innodb_row_lock_time,		  SHOW_LONGLONG},
unknown's avatar
unknown committed
300
  {"row_lock_time_avg",
unknown's avatar
unknown committed
301
  (char*) &export_vars.innodb_row_lock_time_avg,	  SHOW_LONG},
unknown's avatar
unknown committed
302
  {"row_lock_time_max",
unknown's avatar
unknown committed
303
  (char*) &export_vars.innodb_row_lock_time_max,	  SHOW_LONG},
unknown's avatar
unknown committed
304
  {"row_lock_waits",
unknown's avatar
unknown committed
305
  (char*) &export_vars.innodb_row_lock_waits,		  SHOW_LONG},
306
  {"rows_deleted",
unknown's avatar
unknown committed
307
  (char*) &export_vars.innodb_rows_deleted,		  SHOW_LONG},
308
  {"rows_inserted",
unknown's avatar
unknown committed
309
  (char*) &export_vars.innodb_rows_inserted,		  SHOW_LONG},
310
  {"rows_read",
unknown's avatar
unknown committed
311
  (char*) &export_vars.innodb_rows_read,		  SHOW_LONG},
312
  {"rows_updated",
unknown's avatar
unknown committed
313
  (char*) &export_vars.innodb_rows_updated,		  SHOW_LONG},
unknown's avatar
unknown committed
314 315
  {NullS, NullS, SHOW_LONG}
};
316

317 318
/* General functions */

unknown's avatar
unknown committed
319 320 321 322 323 324 325 326 327
/**********************************************************************
Save some CPU by testing the value of srv_thread_concurrency in inline
functions. */
inline
void
innodb_srv_conc_enter_innodb(
/*=========================*/
	trx_t*	trx)	/* in: transaction handle */
{
unknown's avatar
unknown committed
328
	if (UNIV_LIKELY(!srv_thread_concurrency)) {
unknown's avatar
unknown committed
329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344

		return;
	}

	srv_conc_enter_innodb(trx);
}

/**********************************************************************
Save some CPU by testing the value of srv_thread_concurrency in inline
functions. */
inline
void
innodb_srv_conc_exit_innodb(
/*========================*/
	trx_t*	trx)	/* in: transaction handle */
{
unknown's avatar
unknown committed
345
	if (UNIV_LIKELY(!srv_thread_concurrency)) {
unknown's avatar
unknown committed
346 347 348 349 350 351 352

		return;
	}

	srv_conc_exit_innodb(trx);
}

unknown's avatar
unknown committed
353
/**********************************************************************
unknown's avatar
unknown committed
354
Releases possible search latch and InnoDB thread FIFO ticket. These should
unknown's avatar
unknown committed
355 356 357 358
be released at each SQL statement end, and also when mysqld passes the
control to the client. It does no harm to release these also in the middle
of an SQL statement. */
inline
unknown's avatar
unknown committed
359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374
void
innobase_release_stat_resources(
/*============================*/
	trx_t*	trx)	/* in: transaction object */
{
	if (trx->has_search_latch) {
		trx_search_latch_release_if_reserved(trx);
	}

	if (trx->declared_to_be_inside_innodb) {
		/* Release our possible ticket in the FIFO */

		srv_conc_force_exit_innodb(trx);
	}
}

375 376 377 378 379
/************************************************************************
Call this function when mysqld passes control to the client. That is to
avoid deadlocks on the adaptive hash S-latch possibly held by thd. For more
documentation, see handler.cc. */

380
int
381 382
innobase_release_temporary_latches(
/*===============================*/
unknown's avatar
unknown committed
383
	THD *thd)
384
{
unknown's avatar
unknown committed
385 386
	trx_t*	trx;

unknown's avatar
unknown committed
387
	if (!innodb_inited) {
unknown's avatar
unknown committed
388

389
		return 0;
unknown's avatar
unknown committed
390 391
	}

unknown's avatar
unknown committed
392 393 394
	trx = (trx_t*) thd->ha_data[innobase_hton.slot];

	if (trx) {
unknown's avatar
unknown committed
395
		innobase_release_stat_resources(trx);
unknown's avatar
unknown committed
396
	}
397
	return 0;
398 399
}

400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416
/************************************************************************
Increments innobase_active_counter and every INNOBASE_WAKE_INTERVALth
time calls srv_active_wake_master_thread. This function should be used
when a single database operation may introduce a small need for
server utility activity, like checkpointing. */
inline
void
innobase_active_small(void)
/*=======================*/
{
	innobase_active_counter++;

	if ((innobase_active_counter % INNOBASE_WAKE_INTERVAL) == 0) {
		srv_active_wake_master_thread();
	}
}

417
/************************************************************************
unknown's avatar
unknown committed
418 419 420
Converts an InnoDB error code to a MySQL error code and also tells to MySQL
about a possible transaction rollback inside InnoDB caused by a lock wait
timeout or a deadlock. */
421 422 423 424 425
static
int
convert_error_code_to_mysql(
/*========================*/
			/* out: MySQL error code */
unknown's avatar
unknown committed
426 427
	int	error,	/* in: InnoDB error code */
	THD*	thd)	/* in: user thread handle or NULL */
428 429 430 431 432
{
	if (error == DB_SUCCESS) {

		return(0);

unknown's avatar
unknown committed
433
	} else if (error == (int) DB_DUPLICATE_KEY) {
434

unknown's avatar
unknown committed
435
		return(HA_ERR_FOUND_DUPP_KEY);
436

unknown's avatar
unknown committed
437
	} else if (error == (int) DB_FOREIGN_DUPLICATE_KEY) {
438 439 440

		return(HA_ERR_FOREIGN_DUPLICATE_KEY);

unknown's avatar
unknown committed
441
	} else if (error == (int) DB_RECORD_NOT_FOUND) {
442

unknown's avatar
unknown committed
443
		return(HA_ERR_NO_ACTIVE_RECORD);
444

unknown's avatar
unknown committed
445
	} else if (error == (int) DB_ERROR) {
446

unknown's avatar
unknown committed
447
		return(-1); /* unspecified error */
448

unknown's avatar
unknown committed
449 450 451 452
	} else if (error == (int) DB_DEADLOCK) {
		/* Since we rolled back the whole transaction, we must
		tell it also to MySQL so that MySQL knows to empty the
		cached binlog for this transaction */
unknown's avatar
unknown committed
453

unknown's avatar
unknown committed
454 455 456
		if (thd) {
			ha_rollback(thd);
		}
457

unknown's avatar
unknown committed
458
		return(HA_ERR_LOCK_DEADLOCK);
459

unknown's avatar
unknown committed
460
	} else if (error == (int) DB_LOCK_WAIT_TIMEOUT) {
461

unknown's avatar
unknown committed
462 463 464
		/* Starting from 5.0.13, we let MySQL just roll back the
		latest SQL statement in a lock wait timeout. Previously, we
		rolled back the whole transaction. */
unknown's avatar
unknown committed
465

unknown's avatar
unknown committed
466
		return(HA_ERR_LOCK_WAIT_TIMEOUT);
467

unknown's avatar
unknown committed
468
	} else if (error == (int) DB_NO_REFERENCED_ROW) {
469

unknown's avatar
unknown committed
470
		return(HA_ERR_NO_REFERENCED_ROW);
471

unknown's avatar
unknown committed
472
	} else if (error == (int) DB_ROW_IS_REFERENCED) {
473

unknown's avatar
unknown committed
474
		return(HA_ERR_ROW_IS_REFERENCED);
475

unknown's avatar
unknown committed
476
	} else if (error == (int) DB_CANNOT_ADD_CONSTRAINT) {
477

unknown's avatar
unknown committed
478
		return(HA_ERR_CANNOT_ADD_FOREIGN);
479

unknown's avatar
unknown committed
480
	} else if (error == (int) DB_CANNOT_DROP_CONSTRAINT) {
unknown's avatar
unknown committed
481

unknown's avatar
unknown committed
482
		return(HA_ERR_ROW_IS_REFERENCED); /* TODO: This is a bit
unknown's avatar
unknown committed
483 484
						misleading, a new MySQL error
						code should be introduced */
unknown's avatar
unknown committed
485
	} else if (error == (int) DB_COL_APPEARS_TWICE_IN_INDEX) {
486

unknown's avatar
unknown committed
487
		return(HA_ERR_CRASHED);
488

unknown's avatar
unknown committed
489
	} else if (error == (int) DB_OUT_OF_FILE_SPACE) {
490

unknown's avatar
unknown committed
491
		return(HA_ERR_RECORD_FILE_FULL);
492

unknown's avatar
unknown committed
493
	} else if (error == (int) DB_TABLE_IS_BEING_USED) {
494

unknown's avatar
unknown committed
495
		return(HA_ERR_WRONG_COMMAND);
496

unknown's avatar
unknown committed
497
	} else if (error == (int) DB_TABLE_NOT_FOUND) {
498

unknown's avatar
unknown committed
499
		return(HA_ERR_KEY_NOT_FOUND);
500

unknown's avatar
unknown committed
501
	} else if (error == (int) DB_TOO_BIG_RECORD) {
502

unknown's avatar
unknown committed
503
		return(HA_ERR_TO_BIG_ROW);
unknown's avatar
unknown committed
504

unknown's avatar
unknown committed
505
	} else if (error == (int) DB_CORRUPTION) {
unknown's avatar
unknown committed
506

unknown's avatar
unknown committed
507 508
		return(HA_ERR_CRASHED);
	} else if (error == (int) DB_NO_SAVEPOINT) {
unknown's avatar
unknown committed
509

unknown's avatar
unknown committed
510 511 512 513 514
		return(HA_ERR_NO_SAVEPOINT);
	} else if (error == (int) DB_LOCK_TABLE_FULL) {
 		/* Since we rolled back the whole transaction, we must
 		tell it also to MySQL so that MySQL knows to empty the
 		cached binlog for this transaction */
515

unknown's avatar
unknown committed
516 517 518
 		if (thd) {
 			ha_rollback(thd);
 		}
unknown's avatar
unknown committed
519 520

    		return(HA_ERR_LOCK_TABLE_FULL);
521
    	} else {
unknown's avatar
unknown committed
522
    		return(-1);			// Unknown error
523 524 525
    	}
}

526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543
/*****************************************************************
If you want to print a thd that is not associated with the current thread,
you must call this function before reserving the InnoDB kernel_mutex, to
protect MySQL from setting thd->query NULL. If you print a thd of the current
thread, we know that MySQL cannot modify thd->query, and it is not necessary
to call this. Call innobase_mysql_end_print_arbitrary_thd() after you release
the kernel_mutex.
NOTE that /mysql/innobase/lock/lock0lock.c must contain the prototype for this
function! */
extern "C"
void
innobase_mysql_prepare_print_arbitrary_thd(void)
/*============================================*/
{
	VOID(pthread_mutex_lock(&LOCK_thread_count));
}

/*****************************************************************
unknown's avatar
unknown committed
544
Releases the mutex reserved by innobase_mysql_prepare_print_arbitrary_thd().
545 546 547 548 549 550 551 552 553 554
NOTE that /mysql/innobase/lock/lock0lock.c must contain the prototype for this
function! */
extern "C"
void
innobase_mysql_end_print_arbitrary_thd(void)
/*========================================*/
{
	VOID(pthread_mutex_unlock(&LOCK_thread_count));
}

555
/*****************************************************************
556 557 558
Prints info of a THD object (== user session thread) to the given file.
NOTE that /mysql/innobase/trx/trx0trx.c must contain the prototype for
this function! */
559
extern "C"
560 561 562
void
innobase_mysql_print_thd(
/*=====================*/
unknown's avatar
unknown committed
563 564
	FILE*	f,		/* in: output stream */
	void*	input_thd,	/* in: pointer to a MySQL THD object */
565 566
	uint	max_query_len)	/* in: max query length to print, or 0 to
				   use the default max length */
567
{
568
	const THD*	thd;
unknown's avatar
unknown committed
569
	const Security_context *sctx;
570
	const char*	s;
571

unknown's avatar
unknown committed
572 573 574
	thd = (const THD*) input_thd;
	/* We probably want to have original user as part of debug output. */
	sctx = &thd->main_security_ctx;
575

576

unknown's avatar
unknown committed
577
	fprintf(f, "MySQL thread id %lu, query id %lu",
578
		thd->thread_id, (ulong) thd->query_id);
579
	if (sctx->host) {
580
		putc(' ', f);
581
		fputs(sctx->host, f);
582
	}
583

584
	if (sctx->ip) {
585
		putc(' ', f);
586
		fputs(sctx->ip, f);
587
	}
588

unknown's avatar
unknown committed
589
	if (sctx->user) {
590
		putc(' ', f);
591
		fputs(sctx->user, f);
unknown's avatar
unknown committed
592
	}
593

594
	if ((s = thd->proc_info)) {
595
		putc(' ', f);
596
		fputs(s, f);
597
	}
598

599
	if ((s = thd->query)) {
600 601 602 603 604 605 606 607 608 609 610
		/* 3100 is chosen because currently 3000 is the maximum
		   max_query_len we ever give this. */
		char	buf[3100];
		uint	len;

		/* If buf is too small, we dynamically allocate storage
		   in this. */
		char*	dyn_str = NULL;

		/* Points to buf or dyn_str. */
		char*	str = buf;
unknown's avatar
unknown committed
611 612

		if (max_query_len == 0) {
613 614 615 616 617 618 619
			/* ADDITIONAL SAFETY: the default is to print at
			   most 300 chars to reduce the probability of a
			   seg fault if there is a race in
			   thd->query_length in MySQL; after May 14, 2004
			   probably no race any more, but better be
			   safe */
			max_query_len = 300;
unknown's avatar
unknown committed
620
		}
unknown's avatar
unknown committed
621

622
		len = min(thd->query_length, max_query_len);
unknown's avatar
unknown committed
623

unknown's avatar
unknown committed
624
		if (len > (sizeof(buf) - 1)) {
625 626
			dyn_str = my_malloc(len + 1, MYF(0));
			str = dyn_str;
unknown's avatar
unknown committed
627
		}
unknown's avatar
unknown committed
628

unknown's avatar
unknown committed
629 630
		/* Use strmake to reduce the timeframe for a race,
		   compared to fwrite() */
631
		len = (uint) (strmake(str, s, len) - str);
unknown's avatar
unknown committed
632
		putc('\n', f);
633 634
		fwrite(str, 1, len, f);

unknown's avatar
unknown committed
635
		if (dyn_str) {
636 637
			my_free(dyn_str, MYF(0));
		}
638
	}
639

640
	putc('\n', f);
641 642
}

643
/**********************************************************************
644
Get the variable length bounds of the given character set.
645 646 647 648

NOTE that the exact prototype of this function has to be in
/innobase/data/data0type.ic! */
extern "C"
649
void
650 651
innobase_get_cset_width(
/*====================*/
652 653 654
	ulint	cset,		/* in: MySQL charset-collation code */
	ulint*	mbminlen,	/* out: minimum length of a char (in bytes) */
	ulint*	mbmaxlen)	/* out: maximum length of a char (in bytes) */
655 656 657
{
	CHARSET_INFO*	cs;
	ut_ad(cset < 256);
658 659
	ut_ad(mbminlen);
	ut_ad(mbmaxlen);
660 661

	cs = all_charsets[cset];
662 663 664 665 666 667 668
	if (cs) {
		*mbminlen = cs->mbminlen;
		*mbmaxlen = cs->mbmaxlen;
	} else {
		ut_a(cset == 0);
		*mbminlen = *mbmaxlen = 0;
	}
669 670
}

unknown's avatar
unknown committed
671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725
/**********************************************************************
Converts an identifier to a table name.

NOTE that the exact prototype of this function has to be in
/innobase/dict/dict0dict.c! */
extern "C"
void
innobase_convert_from_table_id(
/*===========================*/
	char*		to,	/* out: converted identifier */
	const char*	from,	/* in: identifier to convert */
	ulint		len)	/* in: length of 'to', in bytes */
{
	uint	errors;

	strconvert(current_thd->charset(), from,
		   &my_charset_filename, to, len, &errors);
}

/**********************************************************************
Converts an identifier to UTF-8.

NOTE that the exact prototype of this function has to be in
/innobase/dict/dict0dict.c! */
extern "C"
void
innobase_convert_from_id(
/*=====================*/
	char*		to,	/* out: converted identifier */
	const char*	from,	/* in: identifier to convert */
	ulint		len)	/* in: length of 'to', in bytes */
{
	uint	errors;

	strconvert(current_thd->charset(), from,
		   system_charset_info, to, len, &errors);
}

/**********************************************************************
Removes the filename encoding of a table or database name.

NOTE that the exact prototype of this function has to be in
/innobase/dict/dict0dict.c! */
extern "C"
void
innobase_convert_from_filename(
/*===========================*/
	char*		s)	/* in: identifier; out: decoded identifier */
{
	uint	errors;

	strconvert(&my_charset_filename, s,
		   system_charset_info, s, strlen(s), &errors);
}

726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755
/**********************************************************************
Compares NUL-terminated UTF-8 strings case insensitively.

NOTE that the exact prototype of this function has to be in
/innobase/dict/dict0dict.c! */
extern "C"
int
innobase_strcasecmp(
/*================*/
				/* out: 0 if a=b, <0 if a<b, >1 if a>b */
	const char*	a,	/* in: first string to compare */
	const char*	b)	/* in: second string to compare */
{
	return(my_strcasecmp(system_charset_info, a, b));
}

/**********************************************************************
Makes all characters in a NUL-terminated UTF-8 string lower case.

NOTE that the exact prototype of this function has to be in
/innobase/dict/dict0dict.c! */
extern "C"
void
innobase_casedn_str(
/*================*/
	char*	a)	/* in/out: string to put in lower case */
{
	my_casedn_str(system_charset_info, a);
}

unknown's avatar
unknown committed
756 757 758 759 760 761 762 763 764 765 766 767 768 769 770
/**************************************************************************
Determines the connection character set.

NOTE that the exact prototype of this function has to be in
/innobase/dict/dict0dict.c! */
extern "C"
struct charset_info_st*
innobase_get_charset(
/*=================*/
				/* out: connection character set */
	void*	mysql_thd)	/* in: MySQL thread handle */
{
	return(((THD*) mysql_thd)->charset());
}

771 772 773 774 775 776 777 778 779
/*************************************************************************
Creates a temporary file. */
extern "C"
int
innobase_mysql_tmpfile(void)
/*========================*/
			/* out: temporary file descriptor, or < 0 on error */
{
	char	filename[FN_REFLEN];
unknown's avatar
unknown committed
780
	int	fd2 = -1;
781
	File	fd = create_temp_file(filename, mysql_tmpdir, "ib",
782 783 784 785 786 787 788
#ifdef __WIN__
				O_BINARY | O_TRUNC | O_SEQUENTIAL |
				O_TEMPORARY | O_SHORT_LIVED |
#endif /* __WIN__ */
				O_CREAT | O_EXCL | O_RDWR,
				MYF(MY_WME));
	if (fd >= 0) {
unknown's avatar
unknown committed
789 790 791 792
#ifndef __WIN__
		/* On Windows, open files cannot be removed, but files can be
		created with the O_TEMPORARY flag to the same effect
		("delete on close"). */
793 794
		unlink(filename);
#endif /* !__WIN__ */
unknown's avatar
unknown committed
795 796 797 798 799 800 801 802 803 804 805 806
		/* Copy the file descriptor, so that the additional resources
		allocated by create_temp_file() can be freed by invoking
		my_close().

		Because the file descriptor returned by this function
		will be passed to fdopen(), it will be closed by invoking
		fclose(), which in turn will invoke close() instead of
		my_close(). */
		fd2 = dup(fd);
		if (fd2 < 0) {
			DBUG_PRINT("error",("Got error %d on dup",fd2));
			my_errno=errno;
unknown's avatar
unknown committed
807 808 809 810
			my_error(EE_OUT_OF_FILERESOURCES,
				 MYF(ME_BELL+ME_WAITTANG),
				 filename, my_errno);
		}
unknown's avatar
unknown committed
811 812 813
		my_close(fd, MYF(MY_WME));
	}
	return(fd2);
814 815
}

unknown's avatar
unknown committed
816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834
/*************************************************************************
Wrapper around MySQL's copy_and_convert function, see it for
documentation. */
extern "C"
ulint
innobase_convert_string(
/*====================*/
	void*		to,
	ulint		to_length,
	CHARSET_INFO*	to_cs,
	const void*	from,
	ulint		from_length,
	CHARSET_INFO*	from_cs,
	uint*		errors)
{
	return(copy_and_convert((char*)to, to_length, to_cs,
		       (const char*)from, from_length, from_cs, errors));
}

835
/*************************************************************************
836 837
Gets the InnoDB transaction handle for a MySQL handler object, creates
an InnoDB transaction struct if the corresponding MySQL thread struct still
838
lacks one. */
839
static
840 841 842
trx_t*
check_trx_exists(
/*=============*/
843
			/* out: InnoDB transaction handle */
844 845 846 847
	THD*	thd)	/* in: user thread handle */
{
	trx_t*	trx;

unknown's avatar
unknown committed
848
	ut_ad(thd == current_thd);
unknown's avatar
unknown committed
849

unknown's avatar
unknown committed
850
	trx = (trx_t*) thd->ha_data[innobase_hton.slot];
851 852

	if (trx == NULL) {
unknown's avatar
unknown committed
853
		DBUG_ASSERT(thd != NULL);
854
		trx = trx_allocate_for_mysql();
855

856
		trx->mysql_thd = thd;
unknown's avatar
unknown committed
857
		trx->mysql_query_str = &(thd->query);
unknown's avatar
unknown committed
858
		trx->active_trans = 0;
859

860 861 862 863
		/* Update the info whether we should skip XA steps that eat
		CPU time */
		trx->support_xa = (ibool)(thd->variables.innodb_support_xa);

unknown's avatar
unknown committed
864
		thd->ha_data[innobase_hton.slot] = trx;
unknown's avatar
unknown committed
865
	} else {
unknown's avatar
unknown committed
866
		if (trx->magic_n != TRX_MAGIC_N) {
unknown's avatar
unknown committed
867
			mem_analyze_corruption(trx);
unknown's avatar
unknown committed
868 869 870 871 872 873 874 875 876 877 878 879 880 881 882

			ut_a(0);
		}
	}

	if (thd->options & OPTION_NO_FOREIGN_KEY_CHECKS) {
		trx->check_foreigns = FALSE;
	} else {
		trx->check_foreigns = TRUE;
	}

	if (thd->options & OPTION_RELAXED_UNIQUE_CHECKS) {
		trx->check_unique_secondary = FALSE;
	} else {
		trx->check_unique_secondary = TRUE;
883 884 885 886 887
	}

	return(trx);
}

888 889 890 891

/*************************************************************************
Construct ha_innobase handler. */

unknown's avatar
unknown committed
892
ha_innobase::ha_innobase(TABLE_SHARE *table_arg)
893 894
  :handler(&innobase_hton, table_arg),
  int_table_flags(HA_REC_NOT_IN_SEQ |
unknown's avatar
unknown committed
895 896 897
		  HA_NULL_IN_KEY |
		  HA_CAN_INDEX_BLOBS |
		  HA_CAN_SQL_HANDLER |
898
		  HA_PRIMARY_KEY_REQUIRED_FOR_POSITION |
unknown's avatar
unknown committed
899
		  HA_PRIMARY_KEY_IN_READ_INDEX |
900
		  HA_CAN_GEOMETRY | HA_PARTIAL_COLUMN_READ |
unknown's avatar
unknown committed
901
		  HA_TABLE_SCAN_ON_INDEX),
902 903 904 905
  start_of_scan(0),
  num_write_row(0)
{}

906
/*************************************************************************
907
Updates the user_thd field in a handle and also allocates a new InnoDB
908 909
transaction handle if needed, and updates the transaction fields in the
prebuilt struct. */
910
inline
911 912 913 914 915 916
int
ha_innobase::update_thd(
/*====================*/
			/* out: 0 or error code */
	THD*	thd)	/* in: thd to use the handle */
{
917 918
	row_prebuilt_t*	prebuilt = (row_prebuilt_t*) innobase_prebuilt;
	trx_t*		trx;
unknown's avatar
unknown committed
919

920 921
	trx = check_trx_exists(thd);

922
	if (prebuilt->trx != trx) {
923

924
		row_update_prebuilt_trx(prebuilt, trx);
925 926 927
	}

	user_thd = thd;
928

929 930 931
	return(0);
}

unknown's avatar
unknown committed
932
/*************************************************************************
unknown's avatar
unknown committed
933 934 935 936 937
Registers that InnoDB takes part in an SQL statement, so that MySQL knows to
roll back the statement if the statement results in an error. This MUST be
called for every SQL statement that may be rolled back by MySQL. Calling this
several times to register the same statement is allowed, too. */
inline
unknown's avatar
unknown committed
938
void
unknown's avatar
unknown committed
939 940 941
innobase_register_stmt(
/*===================*/
	THD*	thd)	/* in: MySQL thd (connection) object */
unknown's avatar
unknown committed
942
{
unknown's avatar
unknown committed
943 944
	/* Register the statement */
	trans_register_ha(thd, FALSE, &innobase_hton);
unknown's avatar
unknown committed
945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963
}

/*************************************************************************
Registers an InnoDB transaction in MySQL, so that the MySQL XA code knows
to call the InnoDB prepare and commit, or rollback for the transaction. This
MUST be called for every transaction for which the user may call commit or
rollback. Calling this several times to register the same transaction is
allowed, too.
This function also registers the current SQL statement. */
inline
void
innobase_register_trx_and_stmt(
/*===========================*/
	THD*	thd)	/* in: MySQL thd (connection) object */
{
	/* NOTE that actually innobase_register_stmt() registers also
	the transaction in the AUTOCOMMIT=1 mode. */

	innobase_register_stmt(thd);
unknown's avatar
unknown committed
964

unknown's avatar
unknown committed
965
	if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) {
unknown's avatar
unknown committed
966

unknown's avatar
unknown committed
967 968 969
		/* No autocommit mode, register for a transaction */
		trans_register_ha(thd, TRUE, &innobase_hton);
	}
unknown's avatar
unknown committed
970
}
unknown's avatar
unknown committed
971 972 973 974 975 976 977 978 979 980

/*   BACKGROUND INFO: HOW THE MYSQL QUERY CACHE WORKS WITH INNODB
     ------------------------------------------------------------

1) The use of the query cache for TBL is disabled when there is an
uncommitted change to TBL.

2) When a change to TBL commits, InnoDB stores the current value of
its global trx id counter, let us denote it by INV_TRX_ID, to the table object
in the InnoDB data dictionary, and does only allow such transactions whose
unknown's avatar
unknown committed
981
id <= INV_TRX_ID to use the query cache.
unknown's avatar
unknown committed
982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024

3) When InnoDB does an INSERT/DELETE/UPDATE to a table TBL, or an implicit
modification because an ON DELETE CASCADE, we invalidate the MySQL query cache
of TBL immediately.

How this is implemented inside InnoDB:

1) Since every modification always sets an IX type table lock on the InnoDB
table, it is easy to check if there can be uncommitted modifications for a
table: just check if there are locks in the lock list of the table.

2) When a transaction inside InnoDB commits, it reads the global trx id
counter and stores the value INV_TRX_ID to the tables on which it had a lock.

3) If there is an implicit table change from ON DELETE CASCADE or SET NULL,
InnoDB calls an invalidate method for the MySQL query cache for that table.

How this is implemented inside sql_cache.cc:

1) The query cache for an InnoDB table TBL is invalidated immediately at an
INSERT/UPDATE/DELETE, just like in the case of MyISAM. No need to delay
invalidation to the transaction commit.

2) To store or retrieve a value from the query cache of an InnoDB table TBL,
any query must first ask InnoDB's permission. We must pass the thd as a
parameter because InnoDB will look at the trx id, if any, associated with
that thd.

3) Use of the query cache for InnoDB tables is now allowed also when
AUTOCOMMIT==0 or we are inside BEGIN ... COMMIT. Thus transactions no longer
put restrictions on the use of the query cache.
*/

/**********************************************************************
The MySQL query cache uses this to check from InnoDB if the query cache at
the moment is allowed to operate on an InnoDB table. The SQL query must
be a non-locking SELECT.

The query cache is allowed to operate on certain query only if this function
returns TRUE for all tables in the query.

If thd is not in the autocommit state, this function also starts a new
transaction for thd if there is no active trx yet, and assigns a consistent
unknown's avatar
unknown committed
1025 1026 1027 1028 1029 1030 1031 1032
read view to it if there is no read view yet.

Why a deadlock of threads is not possible: the query cache calls this function
at the start of a SELECT processing. Then the calling thread cannot be
holding any InnoDB semaphores. The calling thread is holding the
query cache mutex, and this function will reserver the InnoDB kernel mutex.
Thus, the 'rank' in sync0sync.h of the MySQL query cache mutex is above
the InnoDB kernel mutex. */
unknown's avatar
unknown committed
1033

unknown's avatar
unknown committed
1034
my_bool
unknown's avatar
unknown committed
1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046
innobase_query_caching_of_table_permitted(
/*======================================*/
				/* out: TRUE if permitted, FALSE if not;
				note that the value FALSE does not mean
				we should invalidate the query cache:
				invalidation is called explicitly */
	THD*	thd,		/* in: thd of the user who is trying to
				store a result to the query cache or
				retrieve it */
	char*	full_name,	/* in: concatenation of database name,
				the null character '\0', and the table
				name */
unknown's avatar
unknown committed
1047
	uint	full_name_len,	/* in: length of the full name, i.e.
unknown's avatar
unknown committed
1048
				len(dbname) + len(tablename) + 1 */
unknown's avatar
unknown committed
1049
	ulonglong *unused)	/* unused for this engine */
unknown's avatar
unknown committed
1050 1051 1052 1053 1054 1055 1056 1057 1058
{
	ibool	is_autocommit;
	trx_t*	trx;
	char	norm_name[1000];

	ut_a(full_name_len < 999);

	if (thd->variables.tx_isolation == ISO_SERIALIZABLE) {
		/* In the SERIALIZABLE mode we add LOCK IN SHARE MODE to every
unknown's avatar
unknown committed
1059
		plain SELECT if AUTOCOMMIT is not on. */
unknown's avatar
unknown committed
1060

unknown's avatar
unknown committed
1061 1062 1063
		return((my_bool)FALSE);
	}

unknown's avatar
unknown committed
1064
	trx = check_trx_exists(thd);
unknown's avatar
unknown committed
1065 1066
	if (trx->has_search_latch) {
		ut_print_timestamp(stderr);
1067 1068 1069
		sql_print_error("The calling thread is holding the adaptive "
				"search, latch though calling "
				"innobase_query_caching_of_table_permitted.");
unknown's avatar
unknown committed
1070 1071 1072 1073

		mutex_enter_noninline(&kernel_mutex);
		trx_print(stderr, trx, 1024);
		mutex_exit_noninline(&kernel_mutex);
unknown's avatar
unknown committed
1074 1075
	}

unknown's avatar
unknown committed
1076 1077 1078 1079 1080 1081 1082 1083 1084 1085
	innobase_release_stat_resources(trx);

	if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) {

		is_autocommit = TRUE;
	} else {
		is_autocommit = FALSE;

	}

unknown's avatar
unknown committed
1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103
	if (is_autocommit && trx->n_mysql_tables_in_use == 0) {
		/* We are going to retrieve the query result from the query
		cache. This cannot be a store operation to the query cache
		because then MySQL would have locks on tables already.

		TODO: if the user has used LOCK TABLES to lock the table,
		then we open a transaction in the call of row_.. below.
		That trx can stay open until UNLOCK TABLES. The same problem
		exists even if we do not use the query cache. MySQL should be
		modified so that it ALWAYS calls some cleanup function when
		the processing of a query ends!

		We can imagine we instantaneously serialize this consistent
		read trx to the current trx id counter. If trx2 would have
		changed the tables of a query result stored in the cache, and
		trx2 would have already committed, making the result obsolete,
		then trx2 would have already invalidated the cache. Thus we
		can trust the result in the cache is ok for this query. */
unknown's avatar
unknown committed
1104 1105 1106

		return((my_bool)TRUE);
	}
unknown's avatar
unknown committed
1107

unknown's avatar
unknown committed
1108 1109 1110 1111 1112 1113 1114 1115
	/* Normalize the table name to InnoDB format */

	memcpy(norm_name, full_name, full_name_len);

	norm_name[strlen(norm_name)] = '/'; /* InnoDB uses '/' as the
					    separator between db and table */
	norm_name[full_name_len] = '\0';
#ifdef __WIN__
1116
	innobase_casedn_str(norm_name);
unknown's avatar
unknown committed
1117
#endif
unknown's avatar
unknown committed
1118 1119 1120
	/* The call of row_search_.. will start a new transaction if it is
	not yet started */

unknown's avatar
unknown committed
1121
	if (trx->active_trans == 0) {
unknown's avatar
unknown committed
1122

unknown's avatar
unknown committed
1123 1124 1125
		innobase_register_trx_and_stmt(thd);
		trx->active_trans = 1;
	}
unknown's avatar
unknown committed
1126

unknown's avatar
unknown committed
1127 1128
	if (row_search_check_if_query_cache_permitted(trx, norm_name)) {

unknown's avatar
unknown committed
1129
		/* printf("Query cache for %s permitted\n", norm_name); */
unknown's avatar
unknown committed
1130 1131 1132 1133

		return((my_bool)TRUE);
	}

unknown's avatar
unknown committed
1134
	/* printf("Query cache for %s NOT permitted\n", norm_name); */
unknown's avatar
unknown committed
1135 1136 1137 1138 1139 1140 1141 1142

	return((my_bool)FALSE);
}

/*********************************************************************
Invalidates the MySQL query cache for the table.
NOTE that the exact prototype of this function has to be in
/innobase/row/row0ins.c! */
1143
extern "C"
unknown's avatar
unknown committed
1144 1145 1146 1147 1148
void
innobase_invalidate_query_cache(
/*============================*/
	trx_t*	trx,		/* in: transaction which modifies the table */
	char*	full_name,	/* in: concatenation of database name, null
unknown's avatar
unknown committed
1149 1150 1151 1152 1153
				char '\0', table name, null char'\0';
				NOTE that in Windows this is always
				in LOWER CASE! */
	ulint	full_name_len)	/* in: full name length where also the null
				chars count */
unknown's avatar
unknown committed
1154
{
unknown's avatar
unknown committed
1155 1156 1157 1158
	/* Note that the sync0sync.h rank of the query cache mutex is just
	above the InnoDB kernel mutex. The caller of this function must not
	have latches of a lower rank. */

unknown's avatar
unknown committed
1159
	/* Argument TRUE below means we are using transactions */
unknown's avatar
unknown committed
1160
#ifdef HAVE_QUERY_CACHE
unknown's avatar
unknown committed
1161 1162 1163 1164
	query_cache.invalidate((THD*)(trx->mysql_thd),
					(const char*)full_name,
					(uint32)full_name_len,
					TRUE);
unknown's avatar
unknown committed
1165
#endif
unknown's avatar
unknown committed
1166
}
1167 1168

/*********************************************************************
unknown's avatar
unknown committed
1169
Display an SQL identifier.
1170
This definition must match the one in innobase/ut/ut0ut.c! */
1171
extern "C"
unknown's avatar
unknown committed
1172 1173 1174 1175
void
innobase_print_identifier(
/*======================*/
	FILE*		f,	/* in: output stream */
1176
	trx_t*		trx,	/* in: transaction */
unknown's avatar
unknown committed
1177 1178
	ibool		table_id,/* in: TRUE=print a table name,
				FALSE=print other identifier */
1179 1180 1181
	const char*	name,	/* in: name to print */
	ulint		namelen)/* in: length of name */
{
unknown's avatar
unknown committed
1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208
	const char*	s	= name;
	char*		qname	= NULL;
	int		q;

	if (table_id) {
		/* Decode the table name.  The filename_to_tablename()
		function expects a NUL-terminated string.  The input and
		output strings buffers must not be shared.  The function
		only produces more output when the name contains other
		characters than [0-9A-Z_a-z]. */
		char*	temp_name = my_malloc(namelen + 1, MYF(MY_WME));
		uint	qnamelen = namelen
				+ (1 + sizeof srv_mysql50_table_name_prefix);

		if (temp_name) {
			qname = my_malloc(qnamelen, MYF(MY_WME));
			if (qname) {
				memcpy(temp_name, name, namelen);
				temp_name[namelen] = 0;
				s = qname;
				namelen = filename_to_tablename(temp_name,
						qname, qnamelen);
			}
			my_free(temp_name, MYF(0));
		}
	}

1209
	if (!trx || !trx->mysql_thd) {
unknown's avatar
unknown committed
1210 1211 1212 1213 1214

		q = '"';
	} else {
		q = get_quote_char_for_identifier((THD*) trx->mysql_thd,
						s, (int) namelen);
1215
	}
unknown's avatar
unknown committed
1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232

	if (q == EOF) {
		fwrite(s, 1, namelen, f);
	} else {
		const char*	e = s + namelen;
		putc(q, f);
		while (s < e) {
			int	c = *s++;
			if (c == q) {
				putc(c, f);
			}
			putc(c, f);
		}
		putc(q, f);
	}

	my_free(qname, MYF(MY_ALLOW_ZERO_PTR));
1233 1234
}

1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246
/**************************************************************************
Determines if the currently running transaction has been interrupted. */
extern "C"
ibool
trx_is_interrupted(
/*===============*/
			/* out: TRUE if interrupted */
	trx_t*	trx)	/* in: transaction */
{
	return(trx && trx->mysql_thd && ((THD*) trx->mysql_thd)->killed);
}

1247
/**************************************************************************
unknown's avatar
unknown committed
1248
Obtain a pointer to the MySQL THD object, as in current_thd().	This
1249 1250 1251 1252 1253 1254
definition must match the one in sql/ha_innodb.cc! */
extern "C"
void*
innobase_current_thd(void)
/*======================*/
			/* out: MySQL THD object */
1255
{
1256
	return(current_thd);
unknown's avatar
unknown committed
1257 1258
}

1259 1260 1261 1262
/*********************************************************************
Call this when you have opened a new table handle in HANDLER, before you
call index_read_idx() etc. Actually, we can let the cursor stay open even
over a transaction commit! Then you should call this before every operation,
1263
fetch next etc. This function inits the necessary things even after a
1264 1265 1266 1267 1268 1269
transaction commit. */

void
ha_innobase::init_table_handle_for_HANDLER(void)
/*============================================*/
{
unknown's avatar
unknown committed
1270
	row_prebuilt_t* prebuilt;
1271

unknown's avatar
unknown committed
1272 1273 1274 1275
	/* If current thd does not yet have a trx struct, create one.
	If the current handle does not yet have a prebuilt struct, create
	one. Update the trx pointers in the prebuilt struct. Normally
	this operation is done in external_lock. */
1276

unknown's avatar
unknown committed
1277
	update_thd(current_thd);
1278

unknown's avatar
unknown committed
1279 1280
	/* Initialize the prebuilt struct much like it would be inited in
	external_lock */
1281

unknown's avatar
unknown committed
1282
	prebuilt = (row_prebuilt_t*)innobase_prebuilt;
1283

unknown's avatar
unknown committed
1284 1285
	innobase_release_stat_resources(prebuilt->trx);

unknown's avatar
unknown committed
1286
	/* If the transaction is not started yet, start it */
1287

unknown's avatar
unknown committed
1288
	trx_start_if_not_started_noninline(prebuilt->trx);
1289

unknown's avatar
unknown committed
1290
	/* Assign a read view if the transaction does not have it yet */
1291

unknown's avatar
unknown committed
1292
	trx_assign_read_view(prebuilt->trx);
1293

1294 1295
	/* Set the MySQL flag to mark that there is an active transaction */

unknown's avatar
unknown committed
1296
	if (prebuilt->trx->active_trans == 0) {
unknown's avatar
unknown committed
1297

unknown's avatar
unknown committed
1298
		innobase_register_trx_and_stmt(current_thd);
unknown's avatar
unknown committed
1299

unknown's avatar
unknown committed
1300 1301
		prebuilt->trx->active_trans = 1;
	}
1302

unknown's avatar
unknown committed
1303 1304
	/* We did the necessary inits in this function, no need to repeat them
	in row_search_for_mysql */
1305

unknown's avatar
unknown committed
1306
	prebuilt->sql_stat_start = FALSE;
1307

unknown's avatar
unknown committed
1308 1309
	/* We let HANDLER always to do the reads as consistent reads, even
	if the trx isolation level would have been specified as SERIALIZABLE */
1310

unknown's avatar
unknown committed
1311 1312
	prebuilt->select_lock_type = LOCK_NONE;
	prebuilt->stored_select_lock_type = LOCK_NONE;
1313

unknown's avatar
unknown committed
1314
	/* Always fetch all columns in the index record */
1315

unknown's avatar
unknown committed
1316
	prebuilt->hint_need_to_fetch_extra_cols = ROW_RETRIEVE_ALL_COLS;
1317

unknown's avatar
unknown committed
1318
	/* We want always to fetch all columns in the whole row? Or do
1319 1320
	we???? */

unknown's avatar
unknown committed
1321
	prebuilt->read_just_key = FALSE;
1322 1323

	prebuilt->used_in_HANDLER = TRUE;
1324 1325

	prebuilt->keep_other_fields_on_keyread = FALSE;
1326 1327
}

1328
/*************************************************************************
1329
Opens an InnoDB database. */
1330

unknown's avatar
unknown committed
1331
int
1332 1333 1334
innobase_init(void)
/*===============*/
{
unknown's avatar
unknown committed
1335
	static char	current_dir[3];		/* Set if using current lib */
1336 1337
	int		err;
	bool		ret;
unknown's avatar
unknown committed
1338
	char		*default_path;
unknown's avatar
merge  
unknown committed
1339

unknown's avatar
unknown committed
1340
	DBUG_ENTER("innobase_init");
1341

unknown's avatar
unknown committed
1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366
        innobase_hton.state=have_innodb;
        innobase_hton.db_type= DB_TYPE_INNODB;
        innobase_hton.savepoint_offset=sizeof(trx_named_savept_t);
        innobase_hton.close_connection=innobase_close_connection;
        innobase_hton.savepoint_set=innobase_savepoint;
        innobase_hton.savepoint_rollback=innobase_rollback_to_savepoint;
        innobase_hton.savepoint_release=innobase_release_savepoint;
        innobase_hton.commit=innobase_commit;
        innobase_hton.rollback=innobase_rollback;
        innobase_hton.prepare=innobase_xa_prepare;
        innobase_hton.recover=innobase_xa_recover;
        innobase_hton.commit_by_xid=innobase_commit_by_xid;
        innobase_hton.rollback_by_xid=innobase_rollback_by_xid;
        innobase_hton.create_cursor_read_view=innobase_create_cursor_view;
        innobase_hton.set_cursor_read_view=innobase_set_cursor_view;
        innobase_hton.close_cursor_read_view=innobase_close_cursor_view;
        innobase_hton.create=innobase_create_handler;
        innobase_hton.drop_database=innobase_drop_database;
        innobase_hton.panic=innobase_end;
        innobase_hton.start_consistent_snapshot=innobase_start_trx_and_assign_read_view;
        innobase_hton.flush_logs=innobase_flush_logs;
        innobase_hton.show_status=innobase_show_status;
        innobase_hton.flags=HTON_NO_FLAGS;
        innobase_hton.release_temporary_latches=innobase_release_temporary_latches;

unknown's avatar
unknown committed
1367
	 if (have_innodb != SHOW_OPTION_YES)
unknown's avatar
unknown committed
1368
	   DBUG_RETURN(0); // nothing else to do
1369

unknown's avatar
unknown committed
1370 1371
	ut_a(DATA_MYSQL_TRUE_VARCHAR == (ulint)MYSQL_TYPE_VARCHAR);

unknown's avatar
unknown committed
1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389
#ifdef UNIV_DEBUG
	static const char	test_filename[] = "-@";
	char			test_tablename[sizeof test_filename
				+ sizeof srv_mysql50_table_name_prefix];
	if ((sizeof test_tablename) - 1
			!= filename_to_tablename(test_filename, test_tablename,
			sizeof test_tablename)
			|| strncmp(test_tablename,
			srv_mysql50_table_name_prefix,
			sizeof srv_mysql50_table_name_prefix)
			|| strcmp(test_tablename
			+ sizeof srv_mysql50_table_name_prefix,
			test_filename)) {
		sql_print_error("tablename encoding has been changed");
		goto error;
	}
#endif /* UNIV_DEBUG */

unknown's avatar
unknown committed
1390 1391 1392 1393 1394 1395 1396
	/* Check that values don't overflow on 32-bit systems. */
	if (sizeof(ulint) == 4) {
		if (innobase_buffer_pool_size > UINT_MAX32) {
			sql_print_error(
				"innobase_buffer_pool_size can't be over 4GB"
				" on 32-bit systems");

1397
			goto error;
unknown's avatar
unknown committed
1398 1399 1400 1401 1402 1403 1404
		}

		if (innobase_log_file_size > UINT_MAX32) {
			sql_print_error(
				"innobase_log_file_size can't be over 4GB"
				" on 32-bit systems");

1405
			goto error;
unknown's avatar
unknown committed
1406 1407 1408
		}
	}

unknown's avatar
unknown committed
1409
	os_innodb_umask = (ulint)my_umask;
unknown's avatar
unknown committed
1410

unknown's avatar
unknown committed
1411 1412 1413 1414 1415 1416
	/* First calculate the default path for innodb_data_home_dir etc.,
	in case the user has not given any value.

	Note that when using the embedded server, the datadirectory is not
	necessarily the current directory of this program. */

1417
	if (mysqld_embedded) {
unknown's avatar
unknown committed
1418
		default_path = mysql_real_data_home;
unknown's avatar
unknown committed
1419
		fil_path_to_mysql_datadir = mysql_real_data_home;
unknown's avatar
unknown committed
1420
	} else {
unknown's avatar
unknown committed
1421 1422 1423 1424 1425
		/* It's better to use current lib, to keep paths short */
		current_dir[0] = FN_CURLIB;
		current_dir[1] = FN_LIBCHAR;
		current_dir[2] = 0;
		default_path = current_dir;
unknown's avatar
unknown committed
1426 1427
	}

unknown's avatar
unknown committed
1428 1429
	ut_a(default_path);

unknown's avatar
unknown committed
1430
	if (specialflag & SPECIAL_NO_PRIOR) {
unknown's avatar
unknown committed
1431
		srv_set_thread_priorities = FALSE;
unknown's avatar
unknown committed
1432
	} else {
unknown's avatar
unknown committed
1433 1434
		srv_set_thread_priorities = TRUE;
		srv_query_thread_priority = QUERY_PRIOR;
unknown's avatar
unknown committed
1435
	}
unknown's avatar
unknown committed
1436

unknown's avatar
unknown committed
1437 1438
	/* Set InnoDB initialization parameters according to the values
	read from MySQL .cnf file */
unknown's avatar
unknown committed
1439

unknown's avatar
unknown committed
1440
	/*--------------- Data files -------------------------*/
1441

unknown's avatar
unknown committed
1442
	/* The default dir for data files is the datadir of MySQL */
unknown's avatar
unknown committed
1443 1444

	srv_data_home = (innobase_data_home_dir ? innobase_data_home_dir :
unknown's avatar
unknown committed
1445
			 default_path);
unknown's avatar
unknown committed
1446

unknown's avatar
unknown committed
1447
	/* Set default InnoDB data file size to 10 MB and let it be
unknown's avatar
unknown committed
1448
	auto-extending. Thus users can use InnoDB in >= 4.0 without having
unknown's avatar
unknown committed
1449 1450 1451
	to specify any startup options. */

	if (!innobase_data_file_path) {
unknown's avatar
unknown committed
1452
		innobase_data_file_path = (char*) "ibdata1:10M:autoextend";
unknown's avatar
unknown committed
1453 1454 1455 1456 1457 1458
	}

	/* Since InnoDB edits the argument in the next call, we make another
	copy of it: */

	internal_innobase_data_file_path = my_strdup(innobase_data_file_path,
unknown's avatar
unknown committed
1459
						   MYF(MY_FAE));
unknown's avatar
unknown committed
1460 1461 1462

	ret = (bool) srv_parse_data_file_paths_and_sizes(
				internal_innobase_data_file_path,
unknown's avatar
unknown committed
1463 1464 1465 1466 1467 1468
				&srv_data_file_names,
				&srv_data_file_sizes,
				&srv_data_file_is_raw_partition,
				&srv_n_data_files,
				&srv_auto_extend_last_data_file,
				&srv_last_file_size_max);
1469
	if (ret == FALSE) {
unknown's avatar
unknown committed
1470
		sql_print_error(
unknown's avatar
unknown committed
1471
			"InnoDB: syntax error in innodb_data_file_path");
unknown's avatar
unknown committed
1472
		my_free(internal_innobase_data_file_path,
unknown's avatar
unknown committed
1473
						MYF(MY_ALLOW_ZERO_PTR));
unknown's avatar
unknown committed
1474
		goto error;
unknown's avatar
unknown committed
1475
	}
1476

unknown's avatar
unknown committed
1477 1478 1479
	/* -------------- Log files ---------------------------*/

	/* The default dir for log files is the datadir of MySQL */
unknown's avatar
unknown committed
1480

unknown's avatar
unknown committed
1481
	if (!innobase_log_group_home_dir) {
unknown's avatar
unknown committed
1482
		innobase_log_group_home_dir = default_path;
unknown's avatar
unknown committed
1483
	}
unknown's avatar
unknown committed
1484

unknown's avatar
unknown committed
1485
#ifdef UNIV_LOG_ARCHIVE
unknown's avatar
unknown committed
1486 1487 1488 1489 1490 1491 1492
	/* Since innodb_log_arch_dir has no relevance under MySQL,
	starting from 4.0.6 we always set it the same as
	innodb_log_group_home_dir: */

	innobase_log_arch_dir = innobase_log_group_home_dir;

	srv_arch_dir = innobase_log_arch_dir;
unknown's avatar
unknown committed
1493
#endif /* UNIG_LOG_ARCHIVE */
unknown's avatar
unknown committed
1494

unknown's avatar
unknown committed
1495 1496 1497
	ret = (bool)
		srv_parse_log_group_home_dirs(innobase_log_group_home_dir,
						&srv_log_group_home_dirs);
unknown's avatar
unknown committed
1498

unknown's avatar
unknown committed
1499
	if (ret == FALSE || innobase_mirrored_log_groups != 1) {
1500 1501
	  sql_print_error("syntax error in innodb_log_group_home_dir, or a "
			  "wrong number of mirrored log groups");
unknown's avatar
unknown committed
1502

unknown's avatar
unknown committed
1503
		my_free(internal_innobase_data_file_path,
unknown's avatar
unknown committed
1504
						MYF(MY_ALLOW_ZERO_PTR));
unknown's avatar
unknown committed
1505
		goto error;
unknown's avatar
unknown committed
1506
	}
unknown's avatar
unknown committed
1507

unknown's avatar
unknown committed
1508 1509 1510
	/* --------------------------------------------------*/

	srv_file_flush_method_str = innobase_unix_file_flush_method;
1511

unknown's avatar
unknown committed
1512
	srv_n_log_groups = (ulint) innobase_mirrored_log_groups;
1513
	srv_n_log_files = (ulint) innobase_log_files_in_group;
unknown's avatar
unknown committed
1514 1515
	srv_log_file_size = (ulint) innobase_log_file_size;

unknown's avatar
unknown committed
1516
#ifdef UNIV_LOG_ARCHIVE
unknown's avatar
unknown committed
1517
	srv_log_archive_on = (ulint) innobase_log_archive;
unknown's avatar
unknown committed
1518
#endif /* UNIV_LOG_ARCHIVE */
unknown's avatar
unknown committed
1519
	srv_log_buffer_size = (ulint) innobase_log_buffer_size;
1520

unknown's avatar
unknown committed
1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539
	/* We set srv_pool_size here in units of 1 kB. InnoDB internally
	changes the value so that it becomes the number of database pages. */

	if (innobase_buffer_pool_awe_mem_mb == 0) {
		/* Careful here: we first convert the signed long int to ulint
		and only after that divide */

		srv_pool_size = ((ulint) innobase_buffer_pool_size) / 1024;
	} else {
		srv_use_awe = TRUE;
		srv_pool_size = (ulint)
				(1024 * innobase_buffer_pool_awe_mem_mb);
		srv_awe_window_size = (ulint) innobase_buffer_pool_size;

		/* Note that what the user specified as
		innodb_buffer_pool_size is actually the AWE memory window
		size in this case, and the real buffer pool size is
		determined by .._awe_mem_mb. */
	}
unknown's avatar
unknown committed
1540

unknown's avatar
unknown committed
1541 1542 1543
	srv_mem_pool_size = (ulint) innobase_additional_mem_pool_size;

	srv_n_file_io_threads = (ulint) innobase_file_io_threads;
1544

1545
	srv_lock_wait_timeout = (ulint) innobase_lock_wait_timeout;
unknown's avatar
Merge  
unknown committed
1546 1547
	srv_force_recovery = (ulint) innobase_force_recovery;

unknown's avatar
unknown committed
1548 1549
	srv_use_doublewrite_buf = (ibool) innobase_use_doublewrite;
	srv_use_checksums = (ibool) innobase_use_checksums;
1550

unknown's avatar
unknown committed
1551 1552
	os_use_large_pages = (ibool) innobase_use_large_pages;
	os_large_page_size = (ulint) innobase_large_page_size;
unknown's avatar
unknown committed
1553

unknown's avatar
unknown committed
1554
	srv_file_per_table = (ibool) innobase_file_per_table;
unknown's avatar
unknown committed
1555
	srv_locks_unsafe_for_binlog = (ibool) innobase_locks_unsafe_for_binlog;
unknown's avatar
unknown committed
1556 1557

	srv_max_n_open_files = (ulint) innobase_open_files;
1558
	srv_innodb_status = (ibool) innobase_create_status_file;
unknown's avatar
unknown committed
1559

1560
	srv_print_verbose_log = mysqld_embedded ? 0 : 1;
unknown's avatar
unknown committed
1561

unknown's avatar
unknown committed
1562
	/* Store the default charset-collation number of this MySQL
1563
	installation */
unknown's avatar
unknown committed
1564

1565
	data_mysql_default_charset_coll = (ulint)default_charset_info->number;
unknown's avatar
unknown committed
1566

1567 1568
	ut_a(DATA_MYSQL_LATIN1_SWEDISH_CHARSET_COLL ==
					my_charset_latin1.number);
unknown's avatar
unknown committed
1569
	ut_a(DATA_MYSQL_BINARY_CHARSET_COLL == my_charset_bin.number);
1570

unknown's avatar
unknown committed
1571 1572 1573 1574
	/* Store the latin1_swedish_ci character ordering table to InnoDB. For
	non-latin1_swedish_ci charsets we use the MySQL comparison functions,
	and consequently we do not need to know the ordering internally in
	InnoDB. */
unknown's avatar
unknown committed
1575

1576
	ut_a(0 == strcmp((char*)my_charset_latin1.name,
unknown's avatar
unknown committed
1577 1578
						(char*)"latin1_swedish_ci"));
	memcpy(srv_latin1_ordering, my_charset_latin1.sort_order, 256);
1579

1580
	/* Since we in this module access directly the fields of a trx
unknown's avatar
unknown committed
1581
	struct, and due to different headers and flags it might happen that
1582 1583 1584 1585 1586 1587
	mutex_t has a different size in this module and in InnoDB
	modules, we check at run time that the size is the same in
	these compilation modules. */

	srv_sizeof_trx_t_in_ha_innodb_cc = sizeof(trx_t);

unknown's avatar
unknown committed
1588
	err = innobase_start_or_create_for_mysql();
1589 1590

	if (err != DB_SUCCESS) {
unknown's avatar
unknown committed
1591
		my_free(internal_innobase_data_file_path,
unknown's avatar
unknown committed
1592
						MYF(MY_ALLOW_ZERO_PTR));
unknown's avatar
unknown committed
1593
		goto error;
1594
	}
unknown's avatar
unknown committed
1595 1596

	(void) hash_init(&innobase_open_tables,system_charset_info, 32, 0, 0,
unknown's avatar
unknown committed
1597 1598 1599 1600 1601 1602
					(hash_get_key) innobase_get_key, 0, 0);
	pthread_mutex_init(&innobase_share_mutex, MY_MUTEX_INIT_FAST);
	pthread_mutex_init(&prepare_commit_mutex, MY_MUTEX_INIT_FAST);
	pthread_mutex_init(&commit_threads_m, MY_MUTEX_INIT_FAST);
	pthread_mutex_init(&commit_cond_m, MY_MUTEX_INIT_FAST);
	pthread_cond_init(&commit_cond, NULL);
1603
	innodb_inited= 1;
unknown's avatar
unknown committed
1604 1605 1606 1607 1608 1609 1610 1611 1612

	/* If this is a replication slave and we needed to do a crash recovery,
	set the master binlog position to what InnoDB internally knew about
	how far we got transactions durable inside InnoDB. There is a
	problem here: if the user used also MyISAM tables, InnoDB might not
	know the right position for them.

	THIS DOES NOT WORK CURRENTLY because replication seems to initialize
	glob_mi also after innobase_init. */
unknown's avatar
unknown committed
1613

unknown's avatar
unknown committed
1614 1615 1616 1617 1618 1619
/*	if (trx_sys_mysql_master_log_pos != -1) {
		ut_memcpy(glob_mi.log_file_name, trx_sys_mysql_master_log_name,
				1 + ut_strlen(trx_sys_mysql_master_log_name));
		glob_mi.pos = trx_sys_mysql_master_log_pos;
	}
*/
1620 1621
	DBUG_RETURN(FALSE);
error:
unknown's avatar
unknown committed
1622 1623
	have_innodb= SHOW_OPTION_DISABLED;	// If we couldn't use handler
	DBUG_RETURN(TRUE);
1624 1625 1626
}

/***********************************************************************
1627
Closes an InnoDB database. */
1628

1629 1630
int
innobase_end(ha_panic_function type)
1631
/*==============*/
1632
				/* out: TRUE if error */
1633
{
1634
	int	err= 0;
1635 1636 1637

	DBUG_ENTER("innobase_end");

unknown's avatar
unknown committed
1638
#ifdef __NETWARE__	/* some special cleanup for NetWare */
1639 1640 1641 1642
	if (nw_panic) {
		set_panic_flag_for_netware();
	}
#endif
unknown's avatar
unknown committed
1643
	if (innodb_inited) {
1644

unknown's avatar
unknown committed
1645 1646 1647 1648
		srv_fast_shutdown = (ulint) innobase_fast_shutdown;
		innodb_inited = 0;
		if (innobase_shutdown_for_mysql() != DB_SUCCESS) {
			err = 1;
unknown's avatar
unknown committed
1649
		}
unknown's avatar
unknown committed
1650 1651
		hash_free(&innobase_open_tables);
		my_free(internal_innobase_data_file_path,
unknown's avatar
unknown committed
1652
						MYF(MY_ALLOW_ZERO_PTR));
unknown's avatar
unknown committed
1653 1654 1655 1656 1657
		pthread_mutex_destroy(&innobase_share_mutex);
		pthread_mutex_destroy(&prepare_commit_mutex);
		pthread_mutex_destroy(&commit_threads_m);
		pthread_mutex_destroy(&commit_cond_m);
		pthread_cond_destroy(&commit_cond);
1658
	}
1659

unknown's avatar
unknown committed
1660
	DBUG_RETURN(err);
1661 1662 1663
}

/********************************************************************
unknown's avatar
unknown committed
1664 1665
Flushes InnoDB logs to disk and makes a checkpoint. Really, a commit flushes
the logs, and the name of this function should be innobase_checkpoint. */
1666

1667
bool
1668 1669
innobase_flush_logs(void)
/*=====================*/
1670
				/* out: TRUE if error */
1671
{
unknown's avatar
unknown committed
1672
	bool	result = 0;
1673

unknown's avatar
unknown committed
1674
	DBUG_ENTER("innobase_flush_logs");
1675

unknown's avatar
unknown committed
1676
	log_buffer_flush_to_disk();
1677

unknown's avatar
unknown committed
1678
	DBUG_RETURN(result);
1679 1680 1681
}

/*********************************************************************
1682
Commits a transaction in an InnoDB database. */
1683

unknown's avatar
unknown committed
1684 1685 1686 1687 1688
void
innobase_commit_low(
/*================*/
	trx_t*	trx)	/* in: transaction handle */
{
unknown's avatar
unknown committed
1689
	if (trx->conc_state == TRX_NOT_STARTED) {
unknown's avatar
unknown committed
1690

unknown's avatar
unknown committed
1691 1692
		return;
	}
unknown's avatar
unknown committed
1693

unknown's avatar
unknown committed
1694
#ifdef HAVE_REPLICATION
unknown's avatar
unknown committed
1695
	THD *thd=current_thd;
unknown's avatar
unknown committed
1696

unknown's avatar
unknown committed
1697 1698
	if (thd && thd->slave_thread) {
		/* Update the replication position info inside InnoDB */
unknown's avatar
unknown committed
1699

unknown's avatar
unknown committed
1700 1701 1702 1703 1704
		trx->mysql_master_log_file_name
					= active_mi->rli.group_master_log_name;
		trx->mysql_master_log_pos = ((ib_longlong)
				active_mi->rli.future_group_master_log_pos);
	}
unknown's avatar
SCRUM  
unknown committed
1705
#endif /* HAVE_REPLICATION */
unknown's avatar
unknown committed
1706

unknown's avatar
unknown committed
1707
	trx_commit_for_mysql(trx);
unknown's avatar
unknown committed
1708 1709
}

1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724
/*********************************************************************
Creates an InnoDB transaction struct for the thd if it does not yet have one.
Starts a new InnoDB transaction if a transaction is not yet started. And
assigns a new snapshot for a consistent read if the transaction does not yet
have one. */

int
innobase_start_trx_and_assign_read_view(
/*====================================*/
			/* out: 0 */
	THD*	thd)	/* in: MySQL thread handle of the user for whom
			the transaction should be committed */
{
	trx_t*	trx;

unknown's avatar
unknown committed
1725
	DBUG_ENTER("innobase_start_trx_and_assign_read_view");
1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746

	/* Create a new trx struct for thd, if it does not yet have one */

	trx = check_trx_exists(thd);

	/* This is just to play safe: release a possible FIFO ticket and
	search latch. Since we will reserve the kernel mutex, we have to
	release the search system latch first to obey the latching order. */

	innobase_release_stat_resources(trx);

	/* If the transaction is not started yet, start it */

	trx_start_if_not_started_noninline(trx);

	/* Assign a read view if the transaction does not have it yet */

	trx_assign_read_view(trx);

	/* Set the MySQL flag to mark that there is an active transaction */

unknown's avatar
unknown committed
1747
	if (trx->active_trans == 0) {
unknown's avatar
unknown committed
1748

unknown's avatar
unknown committed
1749
		innobase_register_trx_and_stmt(current_thd);
unknown's avatar
unknown committed
1750

unknown's avatar
unknown committed
1751 1752
		trx->active_trans = 1;
	}
1753 1754 1755 1756

	DBUG_RETURN(0);
}

unknown's avatar
unknown committed
1757
/*********************************************************************
unknown's avatar
unknown committed
1758 1759
Commits a transaction in an InnoDB database or marks an SQL statement
ended. */
unknown's avatar
unknown committed
1760 1761
static
int
1762 1763
innobase_commit(
/*============*/
unknown's avatar
unknown committed
1764
			/* out: 0 */
unknown's avatar
unknown committed
1765
	THD*	thd,	/* in: MySQL thread handle of the user for whom
1766
			the transaction should be committed */
unknown's avatar
unknown committed
1767 1768
	bool	all)	/* in:	TRUE - commit transaction
				FALSE - the current SQL statement ended */
1769
{
1770
	trx_t*		trx;
1771

unknown's avatar
unknown committed
1772 1773
	DBUG_ENTER("innobase_commit");
	DBUG_PRINT("trans", ("ending transaction"));
1774

unknown's avatar
unknown committed
1775
	trx = check_trx_exists(thd);
1776

1777 1778 1779
	/* Update the info whether we should skip XA steps that eat CPU time */
	trx->support_xa = (ibool)(thd->variables.innodb_support_xa);

unknown's avatar
unknown committed
1780 1781 1782
	/* Release a possible FIFO ticket and search latch. Since we will
	reserve the kernel mutex, we have to release the search system latch
	first to obey the latching order. */
unknown's avatar
unknown committed
1783

unknown's avatar
unknown committed
1784 1785 1786 1787 1788
	if (trx->has_search_latch) {
			  trx_search_latch_release_if_reserved(trx);
	}

	/* The flag trx->active_trans is set to 1 in
unknown's avatar
unknown committed
1789 1790 1791

	1. ::external_lock(),
	2. ::start_stmt(),
1792
	3. innobase_query_caching_of_table_permitted(),
unknown's avatar
unknown committed
1793
	4. innobase_savepoint(),
1794
	5. ::init_table_handle_for_HANDLER(),
1795 1796
	6. innobase_start_trx_and_assign_read_view(),
	7. ::transactional_table_lock()
unknown's avatar
unknown committed
1797 1798 1799 1800 1801

	and it is only set to 0 in a commit or a rollback. If it is 0 we know
	there cannot be resources to be freed and we could return immediately.
	For the time being, we play safe and do the cleanup though there should
	be nothing to clean up. */
unknown's avatar
unknown committed
1802

unknown's avatar
unknown committed
1803 1804 1805 1806 1807
	if (trx->active_trans == 0
		&& trx->conc_state != TRX_NOT_STARTED) {

		sql_print_error("trx->active_trans == 0, but"
			" trx->conc_state != TRX_NOT_STARTED");
unknown's avatar
unknown committed
1808
	}
unknown's avatar
unknown committed
1809 1810 1811 1812
	if (all
		|| (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))) {

		/* We were instructed to commit the whole transaction, or
unknown's avatar
unknown committed
1813 1814
		this is an SQL statement end and autocommit is on */

unknown's avatar
unknown committed
1815 1816 1817
		/* We need current binlog position for ibbackup to work.
		Note, the position is current because of
		prepare_commit_mutex */
1818
retry:
unknown's avatar
unknown committed
1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837
		if (srv_commit_concurrency > 0) {
			pthread_mutex_lock(&commit_cond_m);
			commit_threads++;

			if (commit_threads > srv_commit_concurrency) {
				commit_threads--;
				pthread_cond_wait(&commit_cond,
					&commit_cond_m);
				pthread_mutex_unlock(&commit_cond_m);
				goto retry;
			}
			else {
				pthread_mutex_unlock(&commit_cond_m);
			}
		}

		trx->mysql_log_file_name = mysql_bin_log.get_log_fname();
		trx->mysql_log_offset =
			(ib_longlong)mysql_bin_log.get_log_file()->pos_in_file;
unknown's avatar
unknown committed
1838

unknown's avatar
unknown committed
1839
		innobase_commit_low(trx);
unknown's avatar
unknown committed
1840

unknown's avatar
unknown committed
1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854
		if (srv_commit_concurrency > 0) {
			pthread_mutex_lock(&commit_cond_m);
			commit_threads--;
			pthread_cond_signal(&commit_cond);
			pthread_mutex_unlock(&commit_cond_m);
		}

		if (trx->active_trans == 2) {

			pthread_mutex_unlock(&prepare_commit_mutex);
		}

		trx->active_trans = 0;

unknown's avatar
unknown committed
1855
	} else {
unknown's avatar
unknown committed
1856
		/* We just mark the SQL statement ended and do not do a
unknown's avatar
unknown committed
1857 1858
		transaction commit */

unknown's avatar
unknown committed
1859 1860 1861
		if (trx->auto_inc_lock) {
			/* If we had reserved the auto-inc lock for some
			table in this SQL statement we release it now */
unknown's avatar
unknown committed
1862

unknown's avatar
unknown committed
1863 1864 1865 1866 1867 1868 1869
			row_unlock_table_autoinc_for_mysql(trx);
		}
		/* Store the current undo_no of the transaction so that we
		know where to roll back if we have to roll back the next
		SQL statement */

		trx_mark_sql_stat_end(trx);
unknown's avatar
unknown committed
1870
	}
1871

unknown's avatar
unknown committed
1872 1873
	/* Tell the InnoDB server that there might be work for utility
	threads: */
unknown's avatar
unknown committed
1874 1875
	if (trx->declared_to_be_inside_innodb) {
			  /* Release our possible ticket in the FIFO */
1876

unknown's avatar
unknown committed
1877 1878
			  srv_conc_force_exit_innodb(trx);
	}
1879 1880
	srv_active_wake_master_thread();

unknown's avatar
unknown committed
1881
	DBUG_RETURN(0);
1882 1883
}

unknown's avatar
unknown committed
1884
/* TODO: put the
unknown's avatar
unknown committed
1885 1886
MySQL-4.1 functionality back to 5.0. This is needed to get InnoDB Hot Backup
to work. */
unknown's avatar
unknown committed
1887

1888 1889 1890 1891
/*********************************************************************
This is called when MySQL writes the binlog entry for the current
transaction. Writes to the InnoDB tablespace info which tells where the
MySQL binlog entry for the current transaction ended. Also commits the
unknown's avatar
unknown committed
1892
transaction inside InnoDB but does NOT flush InnoDB log files to disk.
unknown's avatar
unknown committed
1893
To flush you have to call innobase_commit_complete(). We have separated
unknown's avatar
unknown committed
1894 1895
flushing to eliminate the bottleneck of LOCK_log in log.cc which disabled
InnoDB's group commit capability. */
1896 1897 1898 1899

int
innobase_report_binlog_offset_and_commit(
/*=====================================*/
unknown's avatar
unknown committed
1900 1901 1902 1903 1904 1905
				/* out: 0 */
	THD*	thd,		/* in: user thread */
	void*	trx_handle,	/* in: InnoDB trx handle */
	char*	log_file_name,	/* in: latest binlog file name */
	my_off_t end_offset)	/* in: the offset in the binlog file
				   up to which we wrote */
1906
{
unknown's avatar
unknown committed
1907 1908 1909
	trx_t*	trx;

	trx = (trx_t*)trx_handle;
1910

unknown's avatar
unknown committed
1911 1912
	ut_a(trx != NULL);

unknown's avatar
unknown committed
1913
	trx->mysql_log_file_name = log_file_name;
unknown's avatar
unknown committed
1914
	trx->mysql_log_offset = (ib_longlong)end_offset;
unknown's avatar
unknown committed
1915

unknown's avatar
unknown committed
1916 1917
	trx->flush_log_later = TRUE;

unknown's avatar
unknown committed
1918
	innobase_commit(thd, TRUE);
unknown's avatar
unknown committed
1919 1920 1921 1922 1923 1924

	trx->flush_log_later = FALSE;

	return(0);
}

unknown's avatar
unknown committed
1925
#if 0
unknown's avatar
unknown committed
1926 1927 1928
/***********************************************************************
This function stores the binlog offset and flushes logs. */

unknown's avatar
unknown committed
1929
void
unknown's avatar
unknown committed
1930 1931
innobase_store_binlog_offset_and_flush_log(
/*=======================================*/
unknown's avatar
unknown committed
1932 1933
	char*		binlog_name,	/* in: binlog name */
	longlong	offset)		/* in: binlog offset */
unknown's avatar
unknown committed
1934 1935
{
	mtr_t mtr;
unknown's avatar
unknown committed
1936

unknown's avatar
unknown committed
1937 1938 1939
	assert(binlog_name != NULL);

	/* Start a mini-transaction */
unknown's avatar
unknown committed
1940
	mtr_start_noninline(&mtr);
unknown's avatar
unknown committed
1941 1942

	/* Update the latest MySQL binlog name and offset info
unknown's avatar
unknown committed
1943
	in trx sys header */
unknown's avatar
unknown committed
1944

unknown's avatar
unknown committed
1945 1946 1947 1948
	trx_sys_update_mysql_binlog_offset(
		binlog_name,
		offset,
		TRX_SYS_MYSQL_LOG_INFO, &mtr);
unknown's avatar
unknown committed
1949

unknown's avatar
unknown committed
1950 1951
	/* Commits the mini-transaction */
	mtr_commit(&mtr);
unknown's avatar
unknown committed
1952

unknown's avatar
unknown committed
1953
	/* Synchronous flush of the log buffer to disk */
unknown's avatar
unknown committed
1954 1955 1956 1957
	log_buffer_flush_to_disk();
}
#endif

unknown's avatar
unknown committed
1958 1959 1960 1961 1962 1963 1964
/*********************************************************************
This is called after MySQL has written the binlog entry for the current
transaction. Flushes the InnoDB log files to disk if required. */

int
innobase_commit_complete(
/*=====================*/
unknown's avatar
unknown committed
1965 1966
				/* out: 0 */
	THD*	thd)		/* in: user thread */
unknown's avatar
unknown committed
1967 1968 1969
{
	trx_t*	trx;

unknown's avatar
unknown committed
1970
	trx = (trx_t*) thd->ha_data[innobase_hton.slot];
unknown's avatar
unknown committed
1971

unknown's avatar
unknown committed
1972
	if (trx && trx->active_trans) {
unknown's avatar
unknown committed
1973

unknown's avatar
unknown committed
1974
		trx->active_trans = 0;
unknown's avatar
unknown committed
1975

1976
		if (UNIV_UNLIKELY(srv_flush_log_at_trx_commit == 0)) {
unknown's avatar
unknown committed
1977

unknown's avatar
unknown committed
1978 1979
			return(0);
		}
unknown's avatar
unknown committed
1980

unknown's avatar
unknown committed
1981 1982
		trx_commit_complete_for_mysql(trx);
	}
unknown's avatar
unknown committed
1983 1984

	return(0);
1985 1986
}

1987
/*********************************************************************
unknown's avatar
unknown committed
1988
Rolls back a transaction or the latest SQL statement. */
1989

unknown's avatar
unknown committed
1990
static int
1991 1992 1993
innobase_rollback(
/*==============*/
			/* out: 0 or error number */
unknown's avatar
unknown committed
1994
	THD*	thd,	/* in: handle to the MySQL thread of the user
1995
			whose transaction should be rolled back */
unknown's avatar
unknown committed
1996 1997
	bool	all)	/* in:	TRUE - commit transaction
				FALSE - the current SQL statement ended */
1998 1999
{
	int	error = 0;
2000
	trx_t*	trx;
2001

2002 2003 2004
	DBUG_ENTER("innobase_rollback");
	DBUG_PRINT("trans", ("aborting transaction"));

2005
	trx = check_trx_exists(thd);
2006

2007 2008 2009
	/* Update the info whether we should skip XA steps that eat CPU time */
	trx->support_xa = (ibool)(thd->variables.innodb_support_xa);

unknown's avatar
unknown committed
2010 2011 2012 2013 2014 2015
	/* Release a possible FIFO ticket and search latch. Since we will
	reserve the kernel mutex, we have to release the search system latch
	first to obey the latching order. */

	innobase_release_stat_resources(trx);

unknown's avatar
unknown committed
2016
	if (trx->auto_inc_lock) {
unknown's avatar
unknown committed
2017 2018 2019
		/* If we had reserved the auto-inc lock for some table (if
		we come here to roll back the latest SQL statement) we
		release it now before a possibly lengthy rollback */
unknown's avatar
unknown committed
2020

unknown's avatar
unknown committed
2021 2022 2023
		row_unlock_table_autoinc_for_mysql(trx);
	}

unknown's avatar
unknown committed
2024 2025
	if (all
		|| (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))) {
unknown's avatar
Merge  
unknown committed
2026

2027
		error = trx_rollback_for_mysql(trx);
unknown's avatar
unknown committed
2028
		trx->active_trans = 0;
unknown's avatar
unknown committed
2029
	} else {
2030
		error = trx_rollback_last_sql_stat_for_mysql(trx);
unknown's avatar
unknown committed
2031
	}
2032

unknown's avatar
unknown committed
2033 2034 2035
	DBUG_RETURN(convert_error_code_to_mysql(error, NULL));
}

2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055
/*********************************************************************
Rolls back a transaction */

int
innobase_rollback_trx(
/*==================*/
			/* out: 0 or error number */
	trx_t*	trx)	/*  in: transaction */
{
	int	error = 0;

	DBUG_ENTER("innobase_rollback_trx");
	DBUG_PRINT("trans", ("aborting transaction"));

	/* Release a possible FIFO ticket and search latch. Since we will
	reserve the kernel mutex, we have to release the search system latch
	first to obey the latching order. */

	innobase_release_stat_resources(trx);

unknown's avatar
unknown committed
2056
	if (trx->auto_inc_lock) {
2057 2058 2059
		/* If we had reserved the auto-inc lock for some table (if
		we come here to roll back the latest SQL statement) we
		release it now before a possibly lengthy rollback */
unknown's avatar
unknown committed
2060

2061 2062 2063 2064 2065 2066 2067 2068
		row_unlock_table_autoinc_for_mysql(trx);
	}

	error = trx_rollback_for_mysql(trx);

	DBUG_RETURN(convert_error_code_to_mysql(error, NULL));
}

unknown's avatar
unknown committed
2069 2070 2071
/*********************************************************************
Rolls back a transaction to a savepoint. */

unknown's avatar
unknown committed
2072
static int
unknown's avatar
unknown committed
2073 2074 2075 2076 2077 2078
innobase_rollback_to_savepoint(
/*===========================*/
				/* out: 0 if success, HA_ERR_NO_SAVEPOINT if
				no savepoint with the given name */
	THD*	thd,		/* in: handle to the MySQL thread of the user
				whose transaction should be rolled back */
unknown's avatar
unknown committed
2079
	void*	savepoint)	/* in: savepoint data */
unknown's avatar
unknown committed
2080
{
unknown's avatar
unknown committed
2081 2082 2083 2084
	ib_longlong	mysql_binlog_cache_pos;
	int		error = 0;
	trx_t*		trx;
	char		name[64];
unknown's avatar
unknown committed
2085 2086 2087 2088 2089

	DBUG_ENTER("innobase_rollback_to_savepoint");

	trx = check_trx_exists(thd);

unknown's avatar
unknown committed
2090 2091 2092
	/* Release a possible FIFO ticket and search latch. Since we will
	reserve the kernel mutex, we have to release the search system latch
	first to obey the latching order. */
unknown's avatar
unknown committed
2093 2094

	innobase_release_stat_resources(trx);
unknown's avatar
unknown committed
2095

unknown's avatar
unknown committed
2096
	/* TODO: use provided savepoint data area to store savepoint data */
unknown's avatar
unknown committed
2097

unknown's avatar
unknown committed
2098
	longlong2str((ulint)savepoint, name, 36);
2099

unknown's avatar
unknown committed
2100
	error = (int) trx_rollback_to_savepoint_for_mysql(trx, name,
unknown's avatar
unknown committed
2101
						&mysql_binlog_cache_pos);
unknown's avatar
unknown committed
2102
	DBUG_RETURN(convert_error_code_to_mysql(error, NULL));
2103 2104
}

unknown's avatar
WL#1967  
unknown committed
2105 2106
/*********************************************************************
Release transaction savepoint name. */
unknown's avatar
unknown committed
2107 2108
static
int
unknown's avatar
unknown committed
2109
innobase_release_savepoint(
unknown's avatar
unknown committed
2110
/*=======================*/
unknown's avatar
WL#1967  
unknown committed
2111 2112 2113 2114
				/* out: 0 if success, HA_ERR_NO_SAVEPOINT if
				no savepoint with the given name */
	THD*	thd,		/* in: handle to the MySQL thread of the user
				whose transaction should be rolled back */
unknown's avatar
unknown committed
2115
	void*	savepoint)	/* in: savepoint data */
unknown's avatar
WL#1967  
unknown committed
2116
{
unknown's avatar
unknown committed
2117 2118 2119
	int		error = 0;
	trx_t*		trx;
	char		name[64];
unknown's avatar
WL#1967  
unknown committed
2120

unknown's avatar
unknown committed
2121
	DBUG_ENTER("innobase_release_savepoint");
unknown's avatar
WL#1967  
unknown committed
2122 2123 2124

	trx = check_trx_exists(thd);

unknown's avatar
unknown committed
2125
	/* TODO: use provided savepoint data area to store savepoint data */
unknown's avatar
unknown committed
2126

unknown's avatar
unknown committed
2127
	longlong2str((ulint)savepoint, name, 36);
2128

unknown's avatar
unknown committed
2129
	error = (int) trx_release_savepoint_for_mysql(trx, name);
unknown's avatar
WL#1967  
unknown committed
2130 2131 2132 2133

	DBUG_RETURN(convert_error_code_to_mysql(error, NULL));
}

2134
/*********************************************************************
unknown's avatar
unknown committed
2135
Sets a transaction savepoint. */
unknown's avatar
unknown committed
2136 2137
static
int
unknown's avatar
unknown committed
2138 2139 2140 2141
innobase_savepoint(
/*===============*/
				/* out: always 0, that is, always succeeds */
	THD*	thd,		/* in: handle to the MySQL thread */
unknown's avatar
unknown committed
2142
	void*	savepoint)	/* in: savepoint data */
unknown's avatar
unknown committed
2143 2144 2145 2146 2147 2148
{
	int	error = 0;
	trx_t*	trx;

	DBUG_ENTER("innobase_savepoint");

unknown's avatar
unknown committed
2149 2150 2151 2152 2153 2154 2155
	/*
	  In the autocommit mode there is no sense to set a savepoint
	  (unless we are in sub-statement), so SQL layer ensures that
	  this method is never called in such situation.
	*/
	DBUG_ASSERT(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN) ||
		thd->in_sub_stmt);
unknown's avatar
unknown committed
2156 2157 2158

	trx = check_trx_exists(thd);

unknown's avatar
unknown committed
2159 2160 2161 2162 2163 2164
	/* Release a possible FIFO ticket and search latch. Since we will
	reserve the kernel mutex, we have to release the search system latch
	first to obey the latching order. */

	innobase_release_stat_resources(trx);

unknown's avatar
unknown committed
2165 2166
	/* cannot happen outside of transaction */
	DBUG_ASSERT(trx->active_trans);
unknown's avatar
unknown committed
2167

unknown's avatar
unknown committed
2168 2169 2170
	/* TODO: use provided savepoint data area to store savepoint data */
	char name[64];
	longlong2str((ulint)savepoint,name,36);
2171

unknown's avatar
unknown committed
2172
	error = (int) trx_savepoint_for_mysql(trx, name, (ib_longlong)0);
unknown's avatar
unknown committed
2173 2174 2175 2176

	DBUG_RETURN(convert_error_code_to_mysql(error, NULL));
}

2177
/*********************************************************************
unknown's avatar
unknown committed
2178
Frees a possible InnoDB trx object associated with the current THD. */
unknown's avatar
unknown committed
2179 2180
static
int
2181 2182
innobase_close_connection(
/*======================*/
unknown's avatar
unknown committed
2183 2184
			/* out: 0 or error number */
	THD*	thd)	/* in: handle to the MySQL thread of the user
unknown's avatar
unknown committed
2185
			whose resources should be free'd */
2186
{
unknown's avatar
unknown committed
2187 2188 2189 2190 2191 2192
	trx_t*	trx;

	trx = (trx_t*)thd->ha_data[innobase_hton.slot];

	ut_a(trx);

unknown's avatar
unknown committed
2193 2194 2195 2196 2197
	if (trx->active_trans == 0
		&& trx->conc_state != TRX_NOT_STARTED) {

		sql_print_error("trx->active_trans == 0, but"
			" trx->conc_state != TRX_NOT_STARTED");
unknown's avatar
unknown committed
2198 2199
	}

2200

2201
	if (trx->conc_state != TRX_NOT_STARTED &&
unknown's avatar
unknown committed
2202 2203 2204 2205 2206 2207 2208
		global_system_variables.log_warnings) {
		sql_print_warning(
			"MySQL is closing a connection that has an active "
			"InnoDB transaction.  %lu row modifications will "
			"roll back.",
			(ulong) trx->undo_no.low);
	}
unknown's avatar
unknown committed
2209 2210 2211

	innobase_rollback_trx(trx);

unknown's avatar
unknown committed
2212
	thr_local_free(trx->mysql_thread_id);
unknown's avatar
unknown committed
2213
	trx_free_for_mysql(trx);
unknown's avatar
unknown committed
2214

unknown's avatar
unknown committed
2215
	return(0);
2216
}
2217 2218 2219


/*****************************************************************************
2220
** InnoDB database tables
2221 2222
*****************************************************************************/

2223 2224 2225 2226 2227 2228 2229 2230 2231 2232
/********************************************************************
Get the record format from the data dictionary. */
enum row_type
ha_innobase::get_row_type() const
/*=============================*/
			/* out: ROW_TYPE_REDUNDANT or ROW_TYPE_COMPACT */
{
	row_prebuilt_t*	prebuilt = (row_prebuilt_t*) innobase_prebuilt;

	if (prebuilt && prebuilt->table) {
unknown's avatar
unknown committed
2233
		if (dict_table_is_comp_noninline(prebuilt->table)) {
2234 2235 2236 2237 2238 2239 2240 2241 2242
			return(ROW_TYPE_COMPACT);
		} else {
			return(ROW_TYPE_REDUNDANT);
		}
	}
	ut_ad(0);
	return(ROW_TYPE_NOT_USED);
}

2243
/********************************************************************
unknown's avatar
unknown committed
2244
Gives the file extension of an InnoDB single-table tablespace. */
unknown's avatar
unknown committed
2245 2246 2247 2248
static const char* ha_innobase_exts[] = {
  ".ibd",
  NullS
};
2249 2250 2251 2252

const char**
ha_innobase::bas_ext() const
/*========================*/
unknown's avatar
unknown committed
2253
				/* out: file extension string */
2254
{
unknown's avatar
unknown committed
2255
  return ha_innobase_exts;
2256 2257
}

unknown's avatar
unknown committed
2258

2259 2260 2261
/*********************************************************************
Normalizes a table name string. A normalized name consists of the
database name catenated to '/' and table name. An example:
unknown's avatar
unknown committed
2262 2263
test/mytable. On Windows normalization puts both the database name and the
table name always to lower case. */
2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277
static
void
normalize_table_name(
/*=================*/
	char*		norm_name,	/* out: normalized name as a
					null-terminated string */
	const char*	name)		/* in: table name string */
{
	char*	name_ptr;
	char*	db_ptr;
	char*	ptr;

	/* Scan name from the end */

unknown's avatar
unknown committed
2278
	ptr = strend(name)-1;
2279 2280 2281 2282 2283 2284 2285

	while (ptr >= name && *ptr != '\\' && *ptr != '/') {
		ptr--;
	}

	name_ptr = ptr + 1;

unknown's avatar
unknown committed
2286
	DBUG_ASSERT(ptr > name);
2287 2288

	ptr--;
2289

2290 2291 2292 2293 2294 2295 2296 2297 2298
	while (ptr >= name && *ptr != '\\' && *ptr != '/') {
		ptr--;
	}

	db_ptr = ptr + 1;

	memcpy(norm_name, db_ptr, strlen(name) + 1 - (db_ptr - name));

	norm_name[name_ptr - db_ptr - 1] = '/';
unknown's avatar
unknown committed
2299 2300

#ifdef __WIN__
unknown's avatar
unknown committed
2301
	innobase_casedn_str(norm_name);
unknown's avatar
unknown committed
2302
#endif
2303
}
2304

2305
/*********************************************************************
unknown's avatar
unknown committed
2306
Creates and opens a handle to a table which already exists in an InnoDB
2307 2308 2309 2310 2311 2312 2313
database. */

int
ha_innobase::open(
/*==============*/
					/* out: 1 if error, 0 if success */
	const char*	name,		/* in: table name */
unknown's avatar
unknown committed
2314 2315
	int		mode,		/* in: not used */
	uint		test_if_locked)	/* in: not used */
2316
{
2317
	dict_table_t*	ib_table;
unknown's avatar
unknown committed
2318
	char		norm_name[1000];
2319
	THD*		thd;
2320 2321 2322 2323 2324 2325

	DBUG_ENTER("ha_innobase::open");

	UT_NOT_USED(mode);
	UT_NOT_USED(test_if_locked);

2326
	thd = current_thd;
2327 2328
	normalize_table_name(norm_name, name);

2329 2330
	user_thd = NULL;

unknown's avatar
unknown committed
2331 2332
	last_query_id = (ulong)-1;

unknown's avatar
unknown committed
2333 2334 2335 2336
	if (!(share=get_share(name))) {

		DBUG_RETURN(1);
	}
2337

2338 2339 2340 2341
	/* Create buffers for packing the fields of a record. Why
	table->reclength did not work here? Obviously, because char
	fields when packed actually became 1 byte longer, when we also
	stored the string length as the first byte. */
2342

unknown's avatar
unknown committed
2343 2344
	upd_and_key_val_buff_len =
				table->s->reclength + table->s->max_key_length
2345
							+ MAX_REF_PARTS * 3;
2346
	if (!(mysql_byte*) my_multi_malloc(MYF(MY_WME),
unknown's avatar
unknown committed
2347 2348 2349 2350
			&upd_buff, upd_and_key_val_buff_len,
			&key_val_buff, upd_and_key_val_buff_len,
			NullS)) {
		free_share(share);
unknown's avatar
unknown committed
2351

unknown's avatar
unknown committed
2352 2353
		DBUG_RETURN(1);
	}
2354

2355
	/* Get pointer to a table object in InnoDB dictionary cache */
2356

unknown's avatar
unknown committed
2357
	ib_table = dict_table_get_and_increment_handle_count(norm_name);
unknown's avatar
unknown committed
2358 2359 2360

	if (NULL == ib_table) {
		ut_print_timestamp(stderr);
2361 2362 2363 2364 2365 2366 2367
		sql_print_error("Cannot find table %s from the internal data "
				"dictionary\nof InnoDB though the .frm file "
				"for the table exists. Maybe you\nhave "
				"deleted and recreated InnoDB data files but "
				"have forgotten\nto delete the corresponding "
				".frm files of InnoDB tables, or you\n"
				"have moved .frm files to another database?\n"
unknown's avatar
unknown committed
2368
				"See http://dev.mysql.com/doc/refman/5.0/en/innodb-troubleshooting.html\n"
2369 2370
				"how you can resolve the problem.\n",
				norm_name);
unknown's avatar
unknown committed
2371 2372 2373
		free_share(share);
		my_free((gptr) upd_buff, MYF(0));
		my_errno = ENOENT;
2374

unknown's avatar
unknown committed
2375 2376
		DBUG_RETURN(HA_ERR_NO_SUCH_TABLE);
	}
unknown's avatar
unknown committed
2377

unknown's avatar
unknown committed
2378 2379
	if (ib_table->ibd_file_missing && !thd->tablespace_op) {
		ut_print_timestamp(stderr);
2380 2381 2382 2383 2384
		sql_print_error("MySQL is trying to open a table handle but "
				"the .ibd file for\ntable %s does not exist.\n"
				"Have you deleted the .ibd file from the "
				"database directory under\nthe MySQL datadir, "
				"or have you used DISCARD TABLESPACE?\n"
unknown's avatar
unknown committed
2385
				"See http://dev.mysql.com/doc/refman/5.0/en/innodb-troubleshooting.html\n"
2386 2387
				"how you can resolve the problem.\n",
				norm_name);
unknown's avatar
unknown committed
2388 2389 2390
		free_share(share);
		my_free((gptr) upd_buff, MYF(0));
		my_errno = ENOENT;
unknown's avatar
unknown committed
2391

unknown's avatar
unknown committed
2392
		dict_table_decrement_handle_count(ib_table);
unknown's avatar
unknown committed
2393 2394
		DBUG_RETURN(HA_ERR_NO_SUCH_TABLE);
	}
2395

2396
	innobase_prebuilt = row_create_prebuilt(ib_table);
2397

unknown's avatar
unknown committed
2398 2399
	((row_prebuilt_t*)innobase_prebuilt)->mysql_row_len =
							table->s->reclength;
2400

unknown's avatar
unknown committed
2401 2402
	/* Looks like MySQL-3.23 sometimes has primary key number != 0 */

unknown's avatar
unknown committed
2403
	primary_key = table->s->primary_key;
unknown's avatar
unknown committed
2404
	key_used_on_scan = primary_key;
2405

unknown's avatar
unknown committed
2406 2407
	/* Allocate a buffer for a 'row reference'. A row reference is
	a string of bytes of length ref_length which uniquely specifies
unknown's avatar
unknown committed
2408 2409 2410
	a row in our table. Note that MySQL may also compare two row
	references for equality by doing a simple memcmp on the strings
	of length ref_length! */
2411

unknown's avatar
unknown committed
2412 2413
	if (!row_table_got_default_clust_index(ib_table)) {
		if (primary_key >= MAX_KEY) {
2414 2415
		  sql_print_error("Table %s has a primary key in InnoDB data "
				  "dictionary, but not in MySQL!", name);
unknown's avatar
unknown committed
2416
		}
2417 2418 2419

		((row_prebuilt_t*)innobase_prebuilt)
				->clust_index_was_generated = FALSE;
unknown's avatar
unknown committed
2420
		/* MySQL allocates the buffer for ref. key_info->key_length
unknown's avatar
unknown committed
2421 2422 2423 2424
		includes space for all key columns + one byte for each column
		that may be NULL. ref_length must be as exact as possible to
		save space, because all row reference buffers are allocated
		based on ref_length. */
unknown's avatar
unknown committed
2425 2426

		ref_length = table->key_info[primary_key].key_length;
2427
	} else {
unknown's avatar
unknown committed
2428
		if (primary_key != MAX_KEY) {
2429 2430 2431 2432 2433 2434 2435 2436 2437
		  sql_print_error("Table %s has no primary key in InnoDB data "
				  "dictionary, but has one in MySQL! If you "
				  "created the table with a MySQL version < "
				  "3.23.54 and did not define a primary key, "
				  "but defined a unique key with all non-NULL "
				  "columns, then MySQL internally treats that "
				  "key as the primary key. You can fix this "
				  "error by dump + DROP + CREATE + reimport "
				  "of the table.", name);
unknown's avatar
unknown committed
2438 2439
		}

2440 2441 2442
		((row_prebuilt_t*)innobase_prebuilt)
				->clust_index_was_generated = TRUE;

unknown's avatar
unknown committed
2443
		ref_length = DATA_ROW_ID_LEN;
unknown's avatar
unknown committed
2444

unknown's avatar
unknown committed
2445 2446 2447 2448 2449 2450 2451
		/* If we automatically created the clustered index, then
		MySQL does not know about it, and MySQL must NOT be aware
		of the index used on scan, to make it avoid checking if we
		update the column of the index. That is why we assert below
		that key_used_on_scan is the undefined value MAX_KEY.
		The column is the row id in the automatical generation case,
		and it will never be updated anyway. */
unknown's avatar
unknown committed
2452

unknown's avatar
unknown committed
2453
		if (key_used_on_scan != MAX_KEY) {
unknown's avatar
unknown committed
2454 2455 2456 2457
			sql_print_warning(
				"Table %s key_used_on_scan is %lu even "
				"though there is no primary key inside "
				"InnoDB.", name, (ulong) key_used_on_scan);
unknown's avatar
unknown committed
2458
		}
2459
	}
2460

2461
	stats.block_size = 16 * 1024;	/* Index block size in InnoDB: used by MySQL
unknown's avatar
unknown committed
2462 2463
				in query optimization */

unknown's avatar
Merge  
unknown committed
2464
	/* Init table lock structure */
2465
	thr_lock_data_init(&share->lock,&lock,(void*) 0);
2466

unknown's avatar
unknown committed
2467
	info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST);
2468

unknown's avatar
unknown committed
2469
	DBUG_RETURN(0);
2470 2471
}

2472 2473 2474 2475 2476 2477
uint
ha_innobase::max_supported_key_part_length() const
{
	return(DICT_MAX_INDEX_COL_LEN - 1);
}

2478
/**********************************************************************
2479
Closes a handle to an InnoDB table. */
2480 2481 2482 2483

int
ha_innobase::close(void)
/*====================*/
2484
				/* out: 0 */
2485
{
unknown's avatar
unknown committed
2486
	DBUG_ENTER("ha_innobase::close");
2487 2488 2489

	row_prebuilt_free((row_prebuilt_t*) innobase_prebuilt);

unknown's avatar
unknown committed
2490 2491
	my_free((gptr) upd_buff, MYF(0));
	free_share(share);
2492

2493
	/* Tell InnoDB server that there might be work for
2494 2495 2496 2497
	utility threads: */

	srv_active_wake_master_thread();

unknown's avatar
unknown committed
2498
	DBUG_RETURN(0);
2499 2500
}

unknown's avatar
unknown committed
2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564
/* The following accessor functions should really be inside MySQL code! */

/******************************************************************
Gets field offset for a field in a table. */
inline
uint
get_field_offset(
/*=============*/
			/* out: offset */
	TABLE*	table,	/* in: MySQL table object */
	Field*	field)	/* in: MySQL field object */
{
	return((uint) (field->ptr - (char*) table->record[0]));
}

/******************************************************************
Checks if a field in a record is SQL NULL. Uses the record format
information in table to track the null bit in record. */
inline
uint
field_in_record_is_null(
/*====================*/
			/* out: 1 if NULL, 0 otherwise */
	TABLE*	table,	/* in: MySQL table object */
	Field*	field,	/* in: MySQL field object */
	char*	record)	/* in: a row in MySQL format */
{
	int	null_offset;

	if (!field->null_ptr) {

		return(0);
	}

	null_offset = (uint) ((char*) field->null_ptr
					- (char*) table->record[0]);

	if (record[null_offset] & field->null_bit) {

		return(1);
	}

	return(0);
}

/******************************************************************
Sets a field in a record to SQL NULL. Uses the record format
information in table to track the null bit in record. */
inline
void
set_field_in_record_to_null(
/*========================*/
	TABLE*	table,	/* in: MySQL table object */
	Field*	field,	/* in: MySQL field object */
	char*	record)	/* in: a row in MySQL format */
{
	int	null_offset;

	null_offset = (uint) ((char*) field->null_ptr
					- (char*) table->record[0]);

	record[null_offset] = record[null_offset] | field->null_bit;
}

2565 2566
extern "C" {
/*****************************************************************
unknown's avatar
unknown committed
2567 2568 2569 2570
InnoDB uses this function to compare two data fields for which the data type
is such that we must use MySQL code to compare them. NOTE that the prototype
of this function is in rem0cmp.c in InnoDB source code! If you change this
function, remember to update the prototype there! */
2571 2572 2573

int
innobase_mysql_cmp(
2574
/*===============*/
2575 2576
					/* out: 1, 0, -1, if a is greater,
					equal, less than b, respectively */
2577
	int		mysql_type,	/* in: MySQL type */
unknown's avatar
unknown committed
2578
	uint		charset_number,	/* in: number of the charset */
2579 2580 2581 2582 2583 2584 2585
	unsigned char*	a,		/* in: data field */
	unsigned int	a_length,	/* in: data field length,
					not UNIV_SQL_NULL */
	unsigned char*	b,		/* in: data field */
	unsigned int	b_length)	/* in: data field length,
					not UNIV_SQL_NULL */
{
unknown's avatar
unknown committed
2586
	CHARSET_INFO*		charset;
2587
	enum_field_types	mysql_tp;
unknown's avatar
unknown committed
2588
	int			ret;
2589

unknown's avatar
unknown committed
2590 2591
	DBUG_ASSERT(a_length != UNIV_SQL_NULL);
	DBUG_ASSERT(b_length != UNIV_SQL_NULL);
2592 2593 2594 2595 2596

	mysql_tp = (enum_field_types) mysql_type;

	switch (mysql_tp) {

unknown's avatar
unknown committed
2597
	case MYSQL_TYPE_BIT:
2598
	case MYSQL_TYPE_STRING:
2599
	case MYSQL_TYPE_VAR_STRING:
unknown's avatar
unknown committed
2600 2601 2602 2603
	case FIELD_TYPE_TINY_BLOB:
	case FIELD_TYPE_MEDIUM_BLOB:
	case FIELD_TYPE_BLOB:
	case FIELD_TYPE_LONG_BLOB:
unknown's avatar
unknown committed
2604
	case MYSQL_TYPE_VARCHAR:
unknown's avatar
unknown committed
2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617
		/* Use the charset number to pick the right charset struct for
		the comparison. Since the MySQL function get_charset may be
		slow before Bar removes the mutex operation there, we first
		look at 2 common charsets directly. */

		if (charset_number == default_charset_info->number) {
			charset = default_charset_info;
		} else if (charset_number == my_charset_latin1.number) {
			charset = &my_charset_latin1;
		} else {
			charset = get_charset(charset_number, MYF(MY_WME));

			if (charset == NULL) {
2618 2619 2620 2621
			  sql_print_error("InnoDB needs charset %lu for doing "
					  "a comparison, but MySQL cannot "
					  "find that charset.",
					  (ulong) charset_number);
unknown's avatar
unknown committed
2622 2623 2624 2625
				ut_a(0);
			}
		}

unknown's avatar
unknown committed
2626 2627 2628 2629
		/* Starting from 4.1.3, we use strnncollsp() in comparisons of
		non-latin1_swedish_ci strings. NOTE that the collation order
		changes then: 'b\0\0...' is ordered BEFORE 'b  ...'. Users
		having indexes on such data need to rebuild their tables! */
unknown's avatar
unknown committed
2630

unknown's avatar
unknown committed
2631 2632 2633
		ret = charset->coll->strnncollsp(charset,
				  a, a_length,
						 b, b_length, 0);
2634
		if (ret < 0) {
unknown's avatar
unknown committed
2635
			return(-1);
2636
		} else if (ret > 0) {
unknown's avatar
unknown committed
2637
			return(1);
2638
		} else {
unknown's avatar
unknown committed
2639 2640
			return(0);
		}
2641 2642 2643 2644 2645 2646 2647 2648 2649
	default:
		assert(0);
	}

	return(0);
}
}

/******************************************************************
unknown's avatar
unknown committed
2650 2651 2652
Converts a MySQL type to an InnoDB type. Note that this function returns
the 'mtype' of InnoDB. InnoDB differentiates between MySQL's old <= 4.1
VARCHAR and the new true VARCHAR in >= 5.0.3 by the 'prtype'. */
2653 2654
inline
ulint
2655 2656
get_innobase_type_from_mysql_type(
/*==============================*/
unknown's avatar
unknown committed
2657 2658 2659 2660 2661
				/* out: DATA_BINARY, DATA_VARCHAR, ... */
	ulint*	unsigned_flag,	/* out: DATA_UNSIGNED if an 'unsigned type';
				at least ENUM and SET, and unsigned integer
				types are 'unsigned types' */
	Field*	field)		/* in: MySQL field */
2662
{
unknown's avatar
unknown committed
2663 2664 2665
	/* The following asserts try to check that the MySQL type code fits in
	8 bits: this is used in ibuf and also when DATA_NOT_NULL is ORed to
	the type */
2666

unknown's avatar
unknown committed
2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680
	DBUG_ASSERT((ulint)FIELD_TYPE_STRING < 256);
	DBUG_ASSERT((ulint)FIELD_TYPE_VAR_STRING < 256);
	DBUG_ASSERT((ulint)FIELD_TYPE_DOUBLE < 256);
	DBUG_ASSERT((ulint)FIELD_TYPE_FLOAT < 256);
	DBUG_ASSERT((ulint)FIELD_TYPE_DECIMAL < 256);

	if (field->flags & UNSIGNED_FLAG) {

		*unsigned_flag = DATA_UNSIGNED;
	} else {
		*unsigned_flag = 0;
	}

	if (field->real_type() == FIELD_TYPE_ENUM
unknown's avatar
unknown committed
2681
		|| field->real_type() == FIELD_TYPE_SET) {
unknown's avatar
unknown committed
2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693

		/* MySQL has field->type() a string type for these, but the
		data is actually internally stored as an unsigned integer
		code! */

		*unsigned_flag = DATA_UNSIGNED; /* MySQL has its own unsigned
						flag set to zero, even though
						internally this is an unsigned
						integer type */
		return(DATA_INT);
	}

2694
	switch (field->type()) {
unknown's avatar
unknown committed
2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746
		/* NOTE that we only allow string types in DATA_MYSQL and
		DATA_VARMYSQL */
	case MYSQL_TYPE_VAR_STRING: /* old <= 4.1 VARCHAR */
	case MYSQL_TYPE_VARCHAR:    /* new >= 5.0.3 true VARCHAR */
		if (field->binary()) {
			return(DATA_BINARY);
		} else if (strcmp(
				   field->charset()->name,
				   "latin1_swedish_ci") == 0) {
			return(DATA_VARCHAR);
		} else {
			return(DATA_VARMYSQL);
		}
	case MYSQL_TYPE_BIT:
	case MYSQL_TYPE_STRING: if (field->binary()) {

			return(DATA_FIXBINARY);
		} else if (strcmp(
				   field->charset()->name,
				   "latin1_swedish_ci") == 0) {
			return(DATA_CHAR);
		} else {
			return(DATA_MYSQL);
		}
	case FIELD_TYPE_NEWDECIMAL:
		return(DATA_FIXBINARY);
	case FIELD_TYPE_LONG:
	case FIELD_TYPE_LONGLONG:
	case FIELD_TYPE_TINY:
	case FIELD_TYPE_SHORT:
	case FIELD_TYPE_INT24:
	case FIELD_TYPE_DATE:
	case FIELD_TYPE_DATETIME:
	case FIELD_TYPE_YEAR:
	case FIELD_TYPE_NEWDATE:
	case FIELD_TYPE_TIME:
	case FIELD_TYPE_TIMESTAMP:
		return(DATA_INT);
	case FIELD_TYPE_FLOAT:
		return(DATA_FLOAT);
	case FIELD_TYPE_DOUBLE:
		return(DATA_DOUBLE);
	case FIELD_TYPE_DECIMAL:
		return(DATA_DECIMAL);
	case FIELD_TYPE_GEOMETRY:
	case FIELD_TYPE_TINY_BLOB:
	case FIELD_TYPE_MEDIUM_BLOB:
	case FIELD_TYPE_BLOB:
	case FIELD_TYPE_LONG_BLOB:
		return(DATA_BLOB);
	default:
		assert(0);
2747 2748 2749 2750
	}

	return(0);
}
2751

unknown's avatar
unknown committed
2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777
/***********************************************************************
Writes an unsigned integer value < 64k to 2 bytes, in the little-endian
storage format. */
inline
void
innobase_write_to_2_little_endian(
/*==============================*/
	byte*	buf,	/* in: where to store */
	ulint	val)	/* in: value to write, must be < 64k */
{
	ut_a(val < 256 * 256);

	buf[0] = (byte)(val & 0xFF);
	buf[1] = (byte)(val / 256);
}

/***********************************************************************
Reads an unsigned integer value < 64k from 2 bytes, in the little-endian
storage format. */
inline
uint
innobase_read_from_2_little_endian(
/*===============================*/
			/* out: value */
	const mysql_byte*	buf)	/* in: from where to read */
{
unknown's avatar
unknown committed
2778
	return (uint) ((ulint)(buf[0]) + 256 * ((ulint)(buf[1])));
unknown's avatar
unknown committed
2779 2780
}

2781
/***********************************************************************
2782
Stores a key value for a row to a buffer. */
2783 2784 2785 2786 2787

uint
ha_innobase::store_key_val_for_row(
/*===============================*/
				/* out: key value length as stored in buff */
unknown's avatar
unknown committed
2788
	uint		keynr,	/* in: key number */
2789
	char*		buff,	/* in/out: buffer for the key value (in MySQL
2790 2791
				format) */
	uint		buff_len,/* in: buffer length */
2792
	const mysql_byte* record)/* in: row in MySQL format */
2793
{
unknown's avatar
unknown committed
2794 2795 2796
	KEY*		key_info	= table->key_info + keynr;
	KEY_PART_INFO*	key_part	= key_info->key_part;
	KEY_PART_INFO*	end		= key_part + key_info->key_parts;
2797
	char*		buff_start	= buff;
unknown's avatar
unknown committed
2798 2799 2800
	enum_field_types mysql_type;
	Field*		field;
	ibool		is_null;
2801

unknown's avatar
unknown committed
2802
	DBUG_ENTER("store_key_val_for_row");
2803

unknown's avatar
unknown committed
2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817
	/* The format for storing a key field in MySQL is the following:

	1. If the column can be NULL, then in the first byte we put 1 if the
	field value is NULL, 0 otherwise.

	2. If the column is of a BLOB type (it must be a column prefix field
	in this case), then we put the length of the data in the field to the
	next 2 bytes, in the little-endian format. If the field is SQL NULL,
	then these 2 bytes are set to 0. Note that the length of data in the
	field is <= column prefix length.

	3. In a column prefix field, prefix_len next bytes are reserved for
	data. In a normal field the max field length next bytes are reserved
	for data. For a VARCHAR(n) the max field length is n. If the stored
unknown's avatar
unknown committed
2818
	value is the SQL NULL then these data bytes are set to 0.
unknown's avatar
unknown committed
2819

unknown's avatar
unknown committed
2820 2821 2822 2823 2824 2825
	4. We always use a 2 byte length for a true >= 5.0.3 VARCHAR. Note that
	in the MySQL row format, the length is stored in 1 or 2 bytes,
	depending on the maximum allowed length. But in the MySQL key value
	format, the length always takes 2 bytes.

	We have to zero-fill the buffer so that MySQL is able to use a
2826 2827
	simple memcmp to compare two key values to determine if they are
	equal. MySQL does this to compare contents of two 'ref' values. */
unknown's avatar
unknown committed
2828

2829
	bzero(buff, buff_len);
unknown's avatar
unknown committed
2830

unknown's avatar
unknown committed
2831 2832
	for (; key_part != end; key_part++) {
		is_null = FALSE;
2833

unknown's avatar
unknown committed
2834 2835
		if (key_part->null_bit) {
			if (record[key_part->null_offset]
2836
						& key_part->null_bit) {
unknown's avatar
unknown committed
2837 2838
				*buff = 1;
				is_null = TRUE;
unknown's avatar
unknown committed
2839
			} else {
unknown's avatar
unknown committed
2840 2841 2842
				*buff = 0;
			}
			buff++;
unknown's avatar
unknown committed
2843
		}
2844

unknown's avatar
unknown committed
2845 2846 2847
		field = key_part->field;
		mysql_type = field->type();

unknown's avatar
unknown committed
2848 2849 2850 2851 2852
		if (mysql_type == MYSQL_TYPE_VARCHAR) {
						/* >= 5.0.3 true VARCHAR */
			ulint	lenlen;
			ulint	len;
			byte*	data;
2853
			ulint	key_len;
unknown's avatar
unknown committed
2854
			ulint	true_len;
2855 2856
			CHARSET_INFO*	cs;
			int	error=0;
unknown's avatar
unknown committed
2857

unknown's avatar
unknown committed
2858 2859
			key_len = key_part->length;

unknown's avatar
unknown committed
2860
			if (is_null) {
unknown's avatar
unknown committed
2861 2862
				buff += key_len + 2;

unknown's avatar
unknown committed
2863 2864
				continue;
			}
unknown's avatar
unknown committed
2865
			cs = field->charset();
unknown's avatar
unknown committed
2866 2867 2868 2869

			lenlen = (ulint)
				(((Field_varstring*)field)->length_bytes);

unknown's avatar
unknown committed
2870
			data = row_mysql_read_true_varchar(&len,
unknown's avatar
unknown committed
2871 2872 2873
				(byte*) (record
				+ (ulint)get_field_offset(table, field)),
				lenlen);
unknown's avatar
unknown committed
2874

unknown's avatar
unknown committed
2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887
			true_len = len;

			/* For multi byte character sets we need to calculate
			the true length of the key */

			if (len > 0 && cs->mbmaxlen > 1) {
				true_len = (ulint) cs->cset->well_formed_len(cs,
						(const char *) data,
						(const char *) data + len,
						key_len / cs->mbmaxlen,
						&error);
			}

unknown's avatar
unknown committed
2888 2889
			/* In a column prefix index, we may need to truncate
			the stored value: */
2890

unknown's avatar
unknown committed
2891 2892
			if (true_len > key_len) {
				true_len = key_len;
unknown's avatar
unknown committed
2893 2894
			}

unknown's avatar
unknown committed
2895 2896 2897
			/* The length in a key value is always stored in 2
			bytes */

unknown's avatar
unknown committed
2898
			row_mysql_store_true_var_len((byte*)buff, true_len, 2);
unknown's avatar
unknown committed
2899 2900
			buff += 2;

unknown's avatar
unknown committed
2901
			memcpy(buff, data, true_len);
unknown's avatar
unknown committed
2902 2903 2904 2905 2906 2907 2908

			/* Note that we always reserve the maximum possible
			length of the true VARCHAR in the key value, though
			only len first bytes after the 2 length bytes contain
			actual data. The rest of the space was reset to zero
			in the bzero() call above. */

unknown's avatar
unknown committed
2909
			buff += key_len;
unknown's avatar
unknown committed
2910 2911

		} else if (mysql_type == FIELD_TYPE_TINY_BLOB
unknown's avatar
unknown committed
2912 2913 2914
			|| mysql_type == FIELD_TYPE_MEDIUM_BLOB
			|| mysql_type == FIELD_TYPE_BLOB
			|| mysql_type == FIELD_TYPE_LONG_BLOB) {
2915

2916 2917
			CHARSET_INFO*	cs;
			ulint		key_len;
unknown's avatar
unknown committed
2918
			ulint		true_len;
2919
			int		error=0;
unknown's avatar
unknown committed
2920 2921
			ulint		blob_len;
			byte*		blob_data;
2922

2923
			ut_a(key_part->key_part_flag & HA_PART_KEY_SEG);
unknown's avatar
unknown committed
2924

unknown's avatar
unknown committed
2925 2926 2927 2928 2929
			key_len = key_part->length;

			if (is_null) {
				buff += key_len + 2;

unknown's avatar
unknown committed
2930
				continue;
unknown's avatar
unknown committed
2931
			}
unknown's avatar
unknown committed
2932 2933 2934 2935

			cs = field->charset();

			blob_data = row_mysql_read_blob_ref(&blob_len,
unknown's avatar
unknown committed
2936
				(byte*) (record
unknown's avatar
unknown committed
2937
				+ (ulint)get_field_offset(table, field)),
unknown's avatar
unknown committed
2938 2939
					(ulint) field->pack_length());

unknown's avatar
unknown committed
2940 2941
			true_len = blob_len;

unknown's avatar
unknown committed
2942
			ut_a(get_field_offset(table, field)
unknown's avatar
unknown committed
2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955
				== key_part->offset);

			/* For multi byte character sets we need to calculate
			the true length of the key */

			if (blob_len > 0 && cs->mbmaxlen > 1) {
				true_len = (ulint) cs->cset->well_formed_len(cs,
						(const char *) blob_data,
						(const char *) blob_data
							+ blob_len,
						key_len / cs->mbmaxlen,
						&error);
			}
unknown's avatar
unknown committed
2956 2957 2958

			/* All indexes on BLOB and TEXT are column prefix
			indexes, and we may need to truncate the data to be
unknown's avatar
unknown committed
2959
			stored in the key value: */
unknown's avatar
unknown committed
2960

unknown's avatar
unknown committed
2961 2962
			if (true_len > key_len) {
				true_len = key_len;
unknown's avatar
unknown committed
2963 2964 2965 2966 2967
			}

			/* MySQL reserves 2 bytes for the length and the
			storage of the number is little-endian */

unknown's avatar
unknown committed
2968
			innobase_write_to_2_little_endian(
unknown's avatar
unknown committed
2969
					(byte*)buff, true_len);
unknown's avatar
unknown committed
2970 2971
			buff += 2;

unknown's avatar
unknown committed
2972
			memcpy(buff, blob_data, true_len);
unknown's avatar
unknown committed
2973

unknown's avatar
unknown committed
2974 2975 2976
			/* Note that we always reserve the maximum possible
			length of the BLOB prefix in the key value. */

unknown's avatar
unknown committed
2977
			buff += key_len;
unknown's avatar
unknown committed
2978
		} else {
unknown's avatar
unknown committed
2979 2980 2981 2982 2983
			/* Here we handle all other data types except the
			true VARCHAR, BLOB and TEXT. Note that the column
			value we store may be also in a column prefix
			index. */

2984
			CHARSET_INFO*		cs;
unknown's avatar
unknown committed
2985 2986
			ulint			true_len;
			ulint			key_len;
2987 2988
			const mysql_byte*	src_start;
			int			error=0;
unknown's avatar
unknown committed
2989 2990 2991 2992 2993 2994
			enum_field_types	real_type;

			key_len = key_part->length;

			if (is_null) {
				 buff += key_len;
2995

unknown's avatar
unknown committed
2996 2997
				 continue;
			}
unknown's avatar
unknown committed
2998

2999
			src_start = record + key_part->offset;
unknown's avatar
unknown committed
3000 3001
			real_type = field->real_type();
			true_len = key_len;
3002

unknown's avatar
unknown committed
3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027
			/* Character set for the field is defined only
			to fields whose type is string and real field
			type is not enum or set. For these fields check
			if character set is multi byte. */

			if (real_type != FIELD_TYPE_ENUM
				&& real_type != FIELD_TYPE_SET
				&& ( mysql_type == MYSQL_TYPE_VAR_STRING
					|| mysql_type == MYSQL_TYPE_STRING)) {

				cs = field->charset();

				/* For multi byte character sets we need to
				calculate the true length of the key */

				if (key_len > 0 && cs->mbmaxlen > 1) {

					true_len = (ulint)
						cs->cset->well_formed_len(cs,
							(const char *)src_start,
							(const char *)src_start
								+ key_len,
							key_len / cs->mbmaxlen,
							&error);
				}
3028 3029
			}

unknown's avatar
unknown committed
3030 3031
			memcpy(buff, src_start, true_len);
			buff += true_len;
3032

unknown's avatar
unknown committed
3033 3034 3035
			/* Pad the unused space with spaces. Note that no
			padding is ever needed for UCS-2 because in MySQL,
			all UCS2 characters are 2 bytes, as MySQL does not
3036 3037
			support surrogate pairs, which are needed to represent
			characters in the range U+10000 to U+10FFFF. */
3038

unknown's avatar
unknown committed
3039 3040 3041 3042
			if (true_len < key_len) {
				ulint pad_len = key_len - true_len;
				memset(buff, ' ', pad_len);
				buff += pad_len;
3043
			}
unknown's avatar
unknown committed
3044
		}
unknown's avatar
unknown committed
3045
	}
3046

3047
	ut_a(buff <= buff_start + buff_len);
unknown's avatar
unknown committed
3048 3049

	DBUG_RETURN((uint)(buff - buff_start));
3050 3051 3052
}

/******************************************************************
unknown's avatar
unknown committed
3053 3054
Builds a 'template' to the prebuilt struct. The template is used in fast
retrieval of just those column values MySQL needs in its processing. */
3055
void
3056
ha_innobase::build_template(
3057 3058 3059 3060 3061 3062
/*===========*/
	row_prebuilt_t*	prebuilt,	/* in: prebuilt struct */
	THD*		thd,		/* in: current user thread, used
					only if templ_type is
					ROW_MYSQL_REC_FIELDS */
	TABLE*		table,		/* in: MySQL table */
3063
	uint		templ_type)	/* in: ROW_MYSQL_WHOLE_ROW or
3064
					ROW_MYSQL_REC_FIELDS */
3065
{
3066 3067
	dict_index_t*	index;
	dict_index_t*	clust_index;
3068
	mysql_row_templ_t* templ;
3069
	Field*		field;
3070 3071
	ulint		n_fields;
	ulint		n_requested_fields	= 0;
unknown's avatar
Merge  
unknown committed
3072
	ibool		fetch_all_in_key	= FALSE;
3073
	ibool		fetch_primary_key_cols	= FALSE;
3074
	ulint		i;
3075 3076
	/* byte offset of the end of last requested column */
	ulint		mysql_prefix_len	= 0;
3077

unknown's avatar
unknown committed
3078 3079 3080 3081
	if (prebuilt->select_lock_type == LOCK_X) {
		/* We always retrieve the whole clustered index record if we
		use exclusive row level locks, for example, if the read is
		done in an UPDATE statement. */
unknown's avatar
unknown committed
3082

unknown's avatar
unknown committed
3083
		templ_type = ROW_MYSQL_WHOLE_ROW;
unknown's avatar
unknown committed
3084 3085
	}

unknown's avatar
unknown committed
3086
	if (templ_type == ROW_MYSQL_REC_FIELDS) {
unknown's avatar
unknown committed
3087 3088
		if (prebuilt->hint_need_to_fetch_extra_cols
			== ROW_RETRIEVE_ALL_COLS) {
3089

unknown's avatar
unknown committed
3090 3091
			/* We know we must at least fetch all columns in the
			key, or all columns in the table */
unknown's avatar
unknown committed
3092

unknown's avatar
unknown committed
3093 3094 3095 3096 3097 3098 3099
			if (prebuilt->read_just_key) {
				/* MySQL has instructed us that it is enough
				to fetch the columns in the key; looks like
				MySQL can set this flag also when there is
				only a prefix of the column in the key: in
				that case we retrieve the whole column from
				the clustered index */
unknown's avatar
unknown committed
3100

unknown's avatar
unknown committed
3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114
				fetch_all_in_key = TRUE;
			} else {
				templ_type = ROW_MYSQL_WHOLE_ROW;
			}
		} else if (prebuilt->hint_need_to_fetch_extra_cols
			== ROW_RETRIEVE_PRIMARY_KEY) {
			/* We must at least fetch all primary key cols. Note
			   that if the clustered index was internally generated
			   by InnoDB on the row id (no primary key was
			   defined), then row_search_for_mysql() will always
			   retrieve the row id to a special buffer in the
			   prebuilt struct. */

			fetch_primary_key_cols = TRUE;
unknown's avatar
Merge  
unknown committed
3115
		}
3116 3117
	}

unknown's avatar
unknown committed
3118
	clust_index = dict_table_get_first_index_noninline(prebuilt->table);
unknown's avatar
unknown committed
3119

3120
	if (templ_type == ROW_MYSQL_REC_FIELDS) {
unknown's avatar
unknown committed
3121
		index = prebuilt->index;
3122 3123
	} else {
		index = clust_index;
3124
	}
3125

3126 3127 3128 3129 3130 3131 3132
	if (index == clust_index) {
		prebuilt->need_to_access_clustered = TRUE;
	} else {
		prebuilt->need_to_access_clustered = FALSE;
		/* Below we check column by column if we need to access
		the clustered index */
	}
3133

3134
	n_fields = (ulint)table->s->fields; /* number of columns */
3135 3136 3137 3138 3139 3140

	if (!prebuilt->mysql_template) {
		prebuilt->mysql_template = (mysql_row_templ_t*)
						mem_alloc_noninline(
					n_fields * sizeof(mysql_row_templ_t));
	}
3141

3142
	prebuilt->template_type = templ_type;
3143
	prebuilt->null_bitmap_len = table->s->null_bytes;
3144

3145 3146
	prebuilt->templ_contains_blob = FALSE;

unknown's avatar
unknown committed
3147 3148
	/* Note that in InnoDB, i is the column number. MySQL calls columns
	'fields'. */
3149
	for (i = 0; i < n_fields; i++) {
3150
		templ = prebuilt->mysql_template + n_requested_fields;
3151 3152
		field = table->field[i];

3153 3154 3155 3156 3157
		if (UNIV_LIKELY(templ_type == ROW_MYSQL_REC_FIELDS)) {
			/* Decide which columns we should fetch
			and which we can skip. */
			register const ibool	index_contains_field =
				dict_index_contains_col_or_prefix(index, i);
3158

3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171
			if (!index_contains_field && prebuilt->read_just_key) {
				/* If this is a 'key read', we do not need
				columns that are not in the key */

				goto skip_field;
			}

			if (index_contains_field && fetch_all_in_key) {
				/* This field is needed in the query */

				goto include_field;
			}

3172 3173
                        if (bitmap_is_set(table->read_set, i) ||
                            bitmap_is_set(table->write_set, i)) {
3174 3175 3176 3177
				/* This field is needed in the query */

				goto include_field;
			}
3178

3179
			if (fetch_primary_key_cols
unknown's avatar
unknown committed
3180 3181
				&& dict_table_col_in_clustered_key(
					index->table, i)) {
3182 3183 3184 3185
				/* This field is needed in the query */

				goto include_field;
			}
unknown's avatar
unknown committed
3186 3187

			/* This field is not needed in the query, skip it */
3188 3189 3190

			goto skip_field;
		}
3191
include_field:
3192
		n_requested_fields++;
3193

3194
		templ->col_no = i;
3195

3196 3197 3198
		if (index == clust_index) {
			templ->rec_field_no = (index->table->cols + i)
								->clust_pos;
3199
		} else {
3200 3201
			templ->rec_field_no = dict_index_get_nth_col_pos(
								index, i);
3202 3203
		}

3204 3205 3206 3207 3208 3209 3210 3211
		if (templ->rec_field_no == ULINT_UNDEFINED) {
			prebuilt->need_to_access_clustered = TRUE;
		}

		if (field->null_ptr) {
			templ->mysql_null_byte_offset =
				(ulint) ((char*) field->null_ptr
					- (char*) table->record[0]);
3212

3213 3214 3215 3216
			templ->mysql_null_bit_mask = (ulint) field->null_bit;
		} else {
			templ->mysql_null_bit_mask = 0;
		}
3217

unknown's avatar
unknown committed
3218 3219 3220
		templ->mysql_col_offset = (ulint)
					get_field_offset(table, field);

3221
		templ->mysql_col_len = (ulint) field->pack_length();
3222 3223 3224 3225 3226
		if (mysql_prefix_len < templ->mysql_col_offset
				+ templ->mysql_col_len) {
			mysql_prefix_len = templ->mysql_col_offset
				+ templ->mysql_col_len;
		}
unknown's avatar
unknown committed
3227
		templ->type = index->table->cols[i].type.mtype;
unknown's avatar
unknown committed
3228 3229 3230 3231
		templ->mysql_type = (ulint)field->type();

		if (templ->mysql_type == DATA_MYSQL_TRUE_VARCHAR) {
			templ->mysql_length_bytes = (ulint)
unknown's avatar
unknown committed
3232
				(((Field_varstring*)field)->length_bytes);
unknown's avatar
unknown committed
3233
		}
unknown's avatar
unknown committed
3234

3235 3236
		templ->charset = dtype_get_charset_coll_noninline(
				index->table->cols[i].type.prtype);
3237 3238
		templ->mbminlen = index->table->cols[i].type.mbminlen;
		templ->mbmaxlen = index->table->cols[i].type.mbmaxlen;
unknown's avatar
unknown committed
3239 3240
		templ->is_unsigned = index->table->cols[i].type.prtype
							& DATA_UNSIGNED;
3241 3242
		if (templ->type == DATA_BLOB) {
			prebuilt->templ_contains_blob = TRUE;
3243
		}
3244 3245 3246
skip_field:
		;
	}
3247

3248
	prebuilt->n_template = n_requested_fields;
3249
	prebuilt->mysql_prefix_len = mysql_prefix_len;
3250

unknown's avatar
unknown committed
3251
	if (index != clust_index && prebuilt->need_to_access_clustered) {
3252 3253 3254 3255
		/* Change rec_field_no's to correspond to the clustered index
		record */
		for (i = 0; i < n_requested_fields; i++) {
			templ = prebuilt->mysql_template + i;
3256

3257
			templ->rec_field_no =
unknown's avatar
unknown committed
3258
				(index->table->cols + templ->col_no)->clust_pos;
3259
		}
3260
	}
3261 3262 3263
}

/************************************************************************
3264
Stores a row in an InnoDB database, to the table specified in this
3265 3266 3267 3268 3269
handle. */

int
ha_innobase::write_row(
/*===================*/
3270
				/* out: error code */
unknown's avatar
unknown committed
3271
	mysql_byte*	record)	/* in: a row in MySQL format */
3272
{
3273
	row_prebuilt_t* prebuilt = (row_prebuilt_t*)innobase_prebuilt;
unknown's avatar
unknown committed
3274
	int		error;
3275
	longlong	auto_inc;
unknown's avatar
unknown committed
3276
	longlong	dummy;
unknown's avatar
unknown committed
3277
	ibool		auto_inc_used= FALSE;
unknown's avatar
unknown committed
3278

unknown's avatar
unknown committed
3279
	DBUG_ENTER("ha_innobase::write_row");
3280

unknown's avatar
unknown committed
3281
	if (prebuilt->trx !=
unknown's avatar
unknown committed
3282
			(trx_t*) current_thd->ha_data[innobase_hton.slot]) {
3283 3284 3285 3286
	  sql_print_error("The transaction object for the table handle is at "
			  "%p, but for the current thread it is at %p",
			  prebuilt->trx,
			  (trx_t*) current_thd->ha_data[innobase_hton.slot]);
3287

3288 3289 3290 3291 3292 3293
		fputs("InnoDB: Dump of 200 bytes around prebuilt: ", stderr);
		ut_print_buf(stderr, ((const byte*)prebuilt) - 100, 200);
		fputs("\n"
			"InnoDB: Dump of 200 bytes around transaction.all: ",
			stderr);
		ut_print_buf(stderr,
unknown's avatar
unknown committed
3294
		 ((byte*)(&(current_thd->ha_data[innobase_hton.slot]))) - 100,
unknown's avatar
unknown committed
3295
								200);
3296 3297
		putc('\n', stderr);
		ut_error;
unknown's avatar
unknown committed
3298
	}
unknown's avatar
unknown committed
3299

unknown's avatar
unknown committed
3300 3301
	statistic_increment(current_thd->status_var.ha_write_count,
		&LOCK_status);
3302

unknown's avatar
unknown committed
3303 3304
	if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
		table->timestamp_field->set_time();
3305

3306
	if ((user_thd->lex->sql_command == SQLCOM_ALTER_TABLE
unknown's avatar
unknown committed
3307 3308 3309 3310
			|| user_thd->lex->sql_command == SQLCOM_OPTIMIZE
			|| user_thd->lex->sql_command == SQLCOM_CREATE_INDEX
			|| user_thd->lex->sql_command == SQLCOM_DROP_INDEX)
		&& num_write_row >= 10000) {
3311 3312 3313 3314 3315 3316 3317 3318
		/* ALTER TABLE is COMMITted at every 10000 copied rows.
		The IX table lock for the original table has to be re-issued.
		As this method will be called on a temporary table where the
		contents of the original table is being copied to, it is
		a bit tricky to determine the source table.  The cursor
		position in the source table need not be adjusted after the
		intermediate COMMIT, since writes by other transactions are
		being blocked by a MySQL table lock TL_WRITE_ALLOW_READ. */
3319

3320
		dict_table_t*	src_table;
unknown's avatar
unknown committed
3321
		ulint		mode;
3322

3323
		num_write_row = 0;
3324

unknown's avatar
unknown committed
3325 3326
		/* Commit the transaction.  This will release the table
		locks, so they have to be acquired again. */
3327 3328 3329 3330 3331 3332

		/* Altering an InnoDB table */
		/* Get the source table. */
		src_table = lock_get_src_table(
				prebuilt->trx, prebuilt->table, &mode);
		if (!src_table) {
unknown's avatar
unknown committed
3333
no_commit:
3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346
			/* Unknown situation: do not commit */
			/*
			ut_print_timestamp(stderr);
			fprintf(stderr,
				"  InnoDB error: ALTER TABLE is holding lock"
				" on %lu tables!\n",
				prebuilt->trx->mysql_n_tables_locked);
			*/
			;
		} else if (src_table == prebuilt->table) {
			/* Source table is not in InnoDB format:
			no need to re-acquire locks on it. */

3347
			/* Altering to InnoDB format */
unknown's avatar
unknown committed
3348
			innobase_commit(user_thd, 1);
3349
			/* Note that this transaction is still active. */
unknown's avatar
unknown committed
3350
			prebuilt->trx->active_trans = 1;
3351
			/* We will need an IX lock on the destination table. */
unknown's avatar
unknown committed
3352
			prebuilt->sql_stat_start = TRUE;
3353 3354 3355
		} else {
			/* Ensure that there are no other table locks than
			LOCK_IX and LOCK_AUTO_INC on the destination table. */
unknown's avatar
unknown committed
3356

3357 3358
			if (!lock_is_table_exclusive(prebuilt->table,
							prebuilt->trx)) {
3359 3360 3361 3362 3363
				goto no_commit;
			}

			/* Commit the transaction.  This will release the table
			locks, so they have to be acquired again. */
unknown's avatar
unknown committed
3364
			innobase_commit(user_thd, 1);
3365
			/* Note that this transaction is still active. */
unknown's avatar
unknown committed
3366
			prebuilt->trx->active_trans = 1;
3367
			/* Re-acquire the table lock on the source table. */
3368
			row_lock_table_for_mysql(prebuilt, src_table, mode);
3369
			/* We will need an IX lock on the destination table. */
unknown's avatar
unknown committed
3370
			prebuilt->sql_stat_start = TRUE;
3371
		}
3372 3373
	}

unknown's avatar
unknown committed
3374 3375
	num_write_row++;

unknown's avatar
unknown committed
3376
	if (last_query_id != user_thd->query_id) {
unknown's avatar
unknown committed
3377 3378
		prebuilt->sql_stat_start = TRUE;
		last_query_id = user_thd->query_id;
unknown's avatar
unknown committed
3379 3380

		innobase_release_stat_resources(prebuilt->trx);
unknown's avatar
unknown committed
3381 3382
	}

unknown's avatar
unknown committed
3383
	if (table->next_number_field && record == table->record[0]) {
unknown's avatar
unknown committed
3384 3385
		/* This is the case where the table has an
		auto-increment column */
unknown's avatar
unknown committed
3386 3387 3388 3389 3390 3391 3392

		/* Initialize the auto-inc counter if it has not been
		initialized yet */

		if (0 == dict_table_autoinc_peek(prebuilt->table)) {

			/* This call initializes the counter */
unknown's avatar
unknown committed
3393
			error = innobase_read_and_init_auto_inc(&dummy);
unknown's avatar
unknown committed
3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410

			if (error) {
				/* Deadlock or lock wait timeout */

				goto func_exit;
			}

			/* We have to set sql_stat_start to TRUE because
			the above call probably has called a select, and
			has reset that flag; row_insert_for_mysql has to
			know to set the IX intention lock on the table,
			something it only does at the start of each
			statement */

			prebuilt->sql_stat_start = TRUE;
		}

unknown's avatar
unknown committed
3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428
		/* We have to use the transactional lock mechanism on the
		auto-inc counter of the table to ensure that replication and
		roll-forward of the binlog exactly imitates also the given
		auto-inc values. The lock is released at each SQL statement's
		end. This lock also prevents a race where two threads would
		call ::get_auto_increment() simultaneously. */

		error = row_lock_table_autoinc_for_mysql(prebuilt);

		if (error != DB_SUCCESS) {
			/* Deadlock or lock wait timeout */

			error = convert_error_code_to_mysql(error, user_thd);

			goto func_exit;
		}

		/* We must use the handler code to update the auto-increment
unknown's avatar
unknown committed
3429
		value to be sure that we increment it correctly. */
unknown's avatar
unknown committed
3430

unknown's avatar
unknown committed
3431 3432
		update_auto_increment();
		auto_inc_used = 1;
unknown's avatar
unknown committed
3433

3434
	}
3435

3436 3437 3438 3439
	if (prebuilt->mysql_template == NULL
			|| prebuilt->template_type != ROW_MYSQL_WHOLE_ROW) {
		/* Build the template used in converting quickly between
		the two database formats */
3440

3441 3442
		build_template(prebuilt, NULL, table, ROW_MYSQL_WHOLE_ROW);
	}
3443

3444
	innodb_srv_conc_enter_innodb(prebuilt->trx);
unknown's avatar
Merge  
unknown committed
3445

3446
	error = row_insert_for_mysql((byte*) record, prebuilt);
unknown's avatar
Merge  
unknown committed
3447

3448
	if (error == DB_SUCCESS && auto_inc_used) {
unknown's avatar
Merge  
unknown committed
3449

unknown's avatar
unknown committed
3450
		/* Fetch the value that was set in the autoincrement field */
unknown's avatar
unknown committed
3451

unknown's avatar
unknown committed
3452
		auto_inc = table->next_number_field->val_int();
3453

unknown's avatar
unknown committed
3454
		if (auto_inc != 0) {
unknown's avatar
unknown committed
3455 3456
			/* This call will update the counter according to the
			value that was inserted in the table */
3457

unknown's avatar
unknown committed
3458 3459 3460
			dict_table_autoinc_update(prebuilt->table, auto_inc);
		}
	}
3461

unknown's avatar
unknown committed
3462 3463 3464
	/* A REPLACE command and LOAD DATA INFILE REPLACE handle a duplicate
	key error themselves, and we must update the autoinc counter if we are
	performing those statements. */
unknown's avatar
unknown committed
3465

unknown's avatar
unknown committed
3466 3467 3468 3469 3470
	if (error == DB_DUPLICATE_KEY && auto_inc_used
		&& (user_thd->lex->sql_command == SQLCOM_REPLACE
			|| user_thd->lex->sql_command == SQLCOM_REPLACE_SELECT
			|| (user_thd->lex->sql_command == SQLCOM_LOAD
				&& user_thd->lex->duplicates == DUP_REPLACE))) {
unknown's avatar
unknown committed
3471

unknown's avatar
unknown committed
3472
		auto_inc = table->next_number_field->val_int();
unknown's avatar
unknown committed
3473

unknown's avatar
unknown committed
3474 3475 3476 3477
		if (auto_inc != 0) {
			dict_table_autoinc_update(prebuilt->table, auto_inc);
		}
	}
unknown's avatar
unknown committed
3478

unknown's avatar
unknown committed
3479
	innodb_srv_conc_exit_innodb(prebuilt->trx);
unknown's avatar
Merge  
unknown committed
3480

unknown's avatar
unknown committed
3481
	error = convert_error_code_to_mysql(error, user_thd);
3482

3483
	/* Tell InnoDB server that there might be work for
3484
	utility threads: */
3485
func_exit:
3486
	innobase_active_small();
3487

unknown's avatar
unknown committed
3488
	DBUG_RETURN(error);
3489 3490
}

3491 3492 3493 3494 3495 3496 3497 3498 3499
/**************************************************************************
Checks which fields have changed in a row and stores information
of them to an update vector. */
static
int
calc_row_difference(
/*================*/
					/* out: error number or 0 */
	upd_t*		uvect,		/* in/out: update vector */
unknown's avatar
unknown committed
3500 3501
	mysql_byte*	old_row,	/* in: old row in MySQL format */
	mysql_byte*	new_row,	/* in: new row in MySQL format */
unknown's avatar
unknown committed
3502 3503
	struct st_table* table,		/* in: table in MySQL data
					dictionary */
3504
	mysql_byte*	upd_buff,	/* in: buffer to use */
unknown's avatar
unknown committed
3505
	ulint		buff_len,	/* in: buffer length */
3506
	row_prebuilt_t*	prebuilt,	/* in: InnoDB prebuilt struct */
3507 3508
	THD*		thd)		/* in: user thread */
{
unknown's avatar
unknown committed
3509
	mysql_byte*	original_upd_buff = upd_buff;
3510
	Field*		field;
unknown's avatar
unknown committed
3511
	enum_field_types field_mysql_type;
3512 3513 3514
	uint		n_fields;
	ulint		o_len;
	ulint		n_len;
unknown's avatar
unknown committed
3515
	ulint		col_pack_len;
unknown's avatar
unknown committed
3516
	byte*		new_mysql_row_col;
unknown's avatar
unknown committed
3517 3518 3519
	byte*		o_ptr;
	byte*		n_ptr;
	byte*		buf;
3520
	upd_field_t*	ufield;
3521
	ulint		col_type;
3522
	ulint		n_changed = 0;
unknown's avatar
unknown committed
3523
	dfield_t	dfield;
3524
	uint		i;
3525

3526
	n_fields = table->s->fields;
3527

3528
	/* We use upd_buff to convert changed fields */
unknown's avatar
unknown committed
3529
	buf = (byte*) upd_buff;
3530

3531 3532 3533
	for (i = 0; i < n_fields; i++) {
		field = table->field[i];

3534
		/* if (thd->query_id != field->query_id) { */
3535 3536
			/* TODO: check that these fields cannot have
			changed! */
3537

3538 3539
		/*	goto skip_field;
		}*/
3540

unknown's avatar
unknown committed
3541 3542
		o_ptr = (byte*) old_row + get_field_offset(table, field);
		n_ptr = (byte*) new_row + get_field_offset(table, field);
unknown's avatar
unknown committed
3543

unknown's avatar
unknown committed
3544 3545 3546
		/* Use new_mysql_row_col and col_pack_len save the values */

		new_mysql_row_col = n_ptr;
unknown's avatar
unknown committed
3547
		col_pack_len = field->pack_length();
unknown's avatar
unknown committed
3548

unknown's avatar
unknown committed
3549 3550
		o_len = col_pack_len;
		n_len = col_pack_len;
3551

unknown's avatar
unknown committed
3552
		/* We use o_ptr and n_ptr to dig up the actual data for
unknown's avatar
unknown committed
3553
		comparison. */
unknown's avatar
unknown committed
3554

unknown's avatar
unknown committed
3555
		field_mysql_type = field->type();
unknown's avatar
unknown committed
3556

unknown's avatar
unknown committed
3557
		col_type = prebuilt->table->cols[i].type.mtype;
3558 3559 3560 3561 3562 3563

		switch (col_type) {

		case DATA_BLOB:
			o_ptr = row_mysql_read_blob_ref(&o_len, o_ptr, o_len);
			n_ptr = row_mysql_read_blob_ref(&n_len, n_ptr, n_len);
unknown's avatar
unknown committed
3564

3565
			break;
unknown's avatar
unknown committed
3566

3567 3568 3569
		case DATA_VARCHAR:
		case DATA_BINARY:
		case DATA_VARMYSQL:
unknown's avatar
unknown committed
3570 3571 3572 3573
			if (field_mysql_type == MYSQL_TYPE_VARCHAR) {
				/* This is a >= 5.0.3 type true VARCHAR where
				the real payload data length is stored in
				1 or 2 bytes */
unknown's avatar
unknown committed
3574

unknown's avatar
unknown committed
3575
				o_ptr = row_mysql_read_true_varchar(
unknown's avatar
unknown committed
3576 3577 3578 3579
					&o_len, o_ptr,
					(ulint)
					(((Field_varstring*)field)->length_bytes));

unknown's avatar
unknown committed
3580
				n_ptr = row_mysql_read_true_varchar(
unknown's avatar
unknown committed
3581 3582 3583
					&n_len, n_ptr,
					(ulint)
					(((Field_varstring*)field)->length_bytes));
unknown's avatar
unknown committed
3584 3585 3586
			}

			break;
3587 3588 3589
		default:
			;
		}
3590

3591
		if (field->null_ptr) {
unknown's avatar
unknown committed
3592 3593
			if (field_in_record_is_null(table, field,
							(char*) old_row)) {
3594 3595
				o_len = UNIV_SQL_NULL;
			}
3596

unknown's avatar
unknown committed
3597 3598
			if (field_in_record_is_null(table, field,
							(char*) new_row)) {
3599 3600 3601 3602 3603 3604 3605 3606 3607
				n_len = UNIV_SQL_NULL;
			}
		}

		if (o_len != n_len || (o_len != UNIV_SQL_NULL &&
					0 != memcmp(o_ptr, n_ptr, o_len))) {
			/* The field has changed */

			ufield = uvect->fields + n_changed;
unknown's avatar
unknown committed
3608

unknown's avatar
unknown committed
3609 3610 3611 3612 3613 3614 3615
			/* Let us use a dummy dfield to make the conversion
			from the MySQL column format to the InnoDB format */

			dfield.type = (prebuilt->table->cols + i)->type;

			if (n_len != UNIV_SQL_NULL) {
				buf = row_mysql_store_col_in_innobase_format(
unknown's avatar
unknown committed
3616 3617 3618 3619 3620
					&dfield,
					(byte*)buf,
					TRUE,
					new_mysql_row_col,
					col_pack_len,
unknown's avatar
unknown committed
3621 3622
					dict_table_is_comp_noninline(
							prebuilt->table));
unknown's avatar
unknown committed
3623 3624
				ufield->new_val.data = dfield.data;
				ufield->new_val.len = dfield.len;
unknown's avatar
unknown committed
3625 3626 3627 3628
			} else {
				ufield->new_val.data = NULL;
				ufield->new_val.len = UNIV_SQL_NULL;
			}
3629 3630

			ufield->exp = NULL;
unknown's avatar
unknown committed
3631
			ufield->field_no = prebuilt->table->cols[i].clust_pos;
3632 3633 3634 3635 3636 3637 3638
			n_changed++;
		}
	}

	uvect->n_fields = n_changed;
	uvect->info_bits = 0;

unknown's avatar
unknown committed
3639 3640
	ut_a(buf <= (byte*)original_upd_buff + buff_len);

3641 3642 3643 3644 3645 3646 3647
	return(0);
}

/**************************************************************************
Updates a row given as a parameter to a new value. Note that we are given
whole rows, not just the fields which are updated: this incurs some
overhead for CPU when we check which fields are actually updated.
3648
TODO: currently InnoDB does not prevent the 'Halloween problem':
3649 3650
in a searched update a single row can get updated several times
if its index columns are updated! */
3651

3652 3653 3654 3655
int
ha_innobase::update_row(
/*====================*/
					/* out: error number or 0 */
unknown's avatar
unknown committed
3656 3657
	const mysql_byte*	old_row,/* in: old row in MySQL format */
	mysql_byte*		new_row)/* in: new row in MySQL format */
3658 3659 3660 3661 3662
{
	row_prebuilt_t*	prebuilt = (row_prebuilt_t*) innobase_prebuilt;
	upd_t*		uvect;
	int		error = 0;

3663
	DBUG_ENTER("ha_innobase::update_row");
3664

unknown's avatar
unknown committed
3665
	ut_ad(prebuilt->trx ==
unknown's avatar
unknown committed
3666
		(trx_t*) current_thd->ha_data[innobase_hton.slot]);
unknown's avatar
unknown committed
3667

unknown's avatar
unknown committed
3668 3669
	if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
		table->timestamp_field->set_time();
3670

unknown's avatar
unknown committed
3671
	if (last_query_id != user_thd->query_id) {
unknown's avatar
unknown committed
3672 3673
		prebuilt->sql_stat_start = TRUE;
		last_query_id = user_thd->query_id;
unknown's avatar
unknown committed
3674 3675

		innobase_release_stat_resources(prebuilt->trx);
unknown's avatar
unknown committed
3676 3677
	}

3678 3679 3680 3681 3682
	if (prebuilt->upd_node) {
		uvect = prebuilt->upd_node->update;
	} else {
		uvect = row_get_prebuilt_update_vector(prebuilt);
	}
3683 3684 3685 3686

	/* Build an update vector from the modified fields in the rows
	(uses upd_buff of the handle) */

3687
	calc_row_difference(uvect, (mysql_byte*) old_row, new_row, table,
unknown's avatar
unknown committed
3688 3689 3690
			upd_buff, (ulint)upd_and_key_val_buff_len,
			prebuilt, user_thd);

3691 3692 3693
	/* This is not a delete */
	prebuilt->upd_node->is_delete = FALSE;

unknown's avatar
unknown committed
3694
	assert(prebuilt->template_type == ROW_MYSQL_WHOLE_ROW);
3695

unknown's avatar
unknown committed
3696
	innodb_srv_conc_enter_innodb(prebuilt->trx);
unknown's avatar
Merge  
unknown committed
3697

3698
	error = row_update_for_mysql((byte*) old_row, prebuilt);
3699

unknown's avatar
unknown committed
3700
	innodb_srv_conc_exit_innodb(prebuilt->trx);
unknown's avatar
Merge  
unknown committed
3701

unknown's avatar
unknown committed
3702
	error = convert_error_code_to_mysql(error, user_thd);
3703

3704
	/* Tell InnoDB server that there might be work for
3705 3706
	utility threads: */

3707
	innobase_active_small();
3708 3709 3710 3711 3712 3713 3714 3715 3716 3717

	DBUG_RETURN(error);
}

/**************************************************************************
Deletes a row given as the parameter. */

int
ha_innobase::delete_row(
/*====================*/
3718 3719
					/* out: error number or 0 */
	const mysql_byte* record)	/* in: a row in MySQL format */
3720 3721 3722 3723
{
	row_prebuilt_t*	prebuilt = (row_prebuilt_t*) innobase_prebuilt;
	int		error = 0;

3724
	DBUG_ENTER("ha_innobase::delete_row");
3725

unknown's avatar
unknown committed
3726
	ut_ad(prebuilt->trx ==
unknown's avatar
unknown committed
3727
		(trx_t*) current_thd->ha_data[innobase_hton.slot]);
unknown's avatar
unknown committed
3728

unknown's avatar
unknown committed
3729
	if (last_query_id != user_thd->query_id) {
unknown's avatar
unknown committed
3730 3731
		prebuilt->sql_stat_start = TRUE;
		last_query_id = user_thd->query_id;
unknown's avatar
unknown committed
3732 3733

		innobase_release_stat_resources(prebuilt->trx);
unknown's avatar
unknown committed
3734 3735
	}

3736 3737 3738
	if (!prebuilt->upd_node) {
		row_get_prebuilt_update_vector(prebuilt);
	}
3739 3740

	/* This is a delete */
3741

3742
	prebuilt->upd_node->is_delete = TRUE;
3743

unknown's avatar
unknown committed
3744
	innodb_srv_conc_enter_innodb(prebuilt->trx);
unknown's avatar
Merge  
unknown committed
3745

3746
	error = row_update_for_mysql((byte*) record, prebuilt);
3747

unknown's avatar
unknown committed
3748
	innodb_srv_conc_exit_innodb(prebuilt->trx);
unknown's avatar
Merge  
unknown committed
3749

unknown's avatar
unknown committed
3750
	error = convert_error_code_to_mysql(error, user_thd);
3751

3752
	/* Tell the InnoDB server that there might be work for
3753 3754
	utility threads: */

3755
	innobase_active_small();
3756 3757 3758 3759

	DBUG_RETURN(error);
}

3760
/**************************************************************************
unknown's avatar
unknown committed
3761
Removes a new lock set on a row, if it was not read optimistically. This can
3762 3763
be called after a row has been read in the processing of an UPDATE or a DELETE
query, if the option innodb_locks_unsafe_for_binlog is set. */
3764 3765 3766 3767 3768 3769 3770 3771 3772

void
ha_innobase::unlock_row(void)
/*=========================*/
{
	row_prebuilt_t*	prebuilt = (row_prebuilt_t*) innobase_prebuilt;

	DBUG_ENTER("ha_innobase::unlock_row");

3773
	if (UNIV_UNLIKELY(last_query_id != user_thd->query_id)) {
3774
		ut_print_timestamp(stderr);
3775 3776 3777
		sql_print_error("last_query_id is %lu != user_thd_query_id is "
				"%lu", (ulong) last_query_id,
				(ulong) user_thd->query_id);
3778 3779 3780
		mem_analyze_corruption((byte *) prebuilt->trx);
		ut_error;
	}
3781

unknown's avatar
unknown committed
3782 3783 3784 3785 3786 3787 3788
	/* Consistent read does not take any locks, thus there is
	nothing to unlock. */

	if (prebuilt->select_lock_type == LOCK_NONE) {
		DBUG_VOID_RETURN;
	}

3789 3790
	switch (prebuilt->row_read_type) {
	case ROW_READ_WITH_LOCKS:
unknown's avatar
unknown committed
3791 3792
		if (!srv_locks_unsafe_for_binlog
		|| prebuilt->trx->isolation_level == TRX_ISO_READ_COMMITTED) {
3793 3794 3795 3796
			break;
		}
		/* fall through */
	case ROW_READ_TRY_SEMI_CONSISTENT:
unknown's avatar
unknown committed
3797
		row_unlock_for_mysql(prebuilt, FALSE);
3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823
		break;
	case ROW_READ_DID_SEMI_CONSISTENT:
		prebuilt->row_read_type = ROW_READ_TRY_SEMI_CONSISTENT;
		break;
	}

	DBUG_VOID_RETURN;
}

/* See handler.h and row0mysql.h for docs on this function. */
bool
ha_innobase::was_semi_consistent_read(void)
/*=======================================*/
{
	row_prebuilt_t*	prebuilt = (row_prebuilt_t*) innobase_prebuilt;

	return(prebuilt->row_read_type == ROW_READ_DID_SEMI_CONSISTENT);
}

/* See handler.h and row0mysql.h for docs on this function. */
void
ha_innobase::try_semi_consistent_read(bool yes)
/*===========================================*/
{
	row_prebuilt_t*	prebuilt = (row_prebuilt_t*) innobase_prebuilt;

unknown's avatar
unknown committed
3824 3825 3826 3827 3828 3829 3830
	/* Row read type is set to semi consistent read if this was
	requested by the MySQL and either innodb_locks_unsafe_for_binlog
	option is used or this session is using READ COMMITTED isolation
	level. */

	if (yes &&  (srv_locks_unsafe_for_binlog
		|| prebuilt->trx->isolation_level == TRX_ISO_READ_COMMITTED)) {
3831 3832 3833
		prebuilt->row_read_type = ROW_READ_TRY_SEMI_CONSISTENT;
	} else {
		prebuilt->row_read_type = ROW_READ_WITH_LOCKS;
unknown's avatar
unknown committed
3834
	}
3835 3836
}

3837 3838 3839 3840 3841 3842 3843
/**********************************************************************
Initializes a handle to use an index. */

int
ha_innobase::index_init(
/*====================*/
			/* out: 0 or error number */
unknown's avatar
unknown committed
3844 3845
	uint	keynr,	/* in: key (index) number */
	bool sorted)	/* in: 1 if result MUST be sorted according to index */
3846
{
unknown's avatar
unknown committed
3847 3848
	int	error	= 0;
	DBUG_ENTER("index_init");
3849

unknown's avatar
Merge  
unknown committed
3850
	error = change_active_index(keynr);
3851

unknown's avatar
unknown committed
3852
	DBUG_RETURN(error);
3853 3854 3855
}

/**********************************************************************
3856
Currently does nothing. */
3857 3858 3859 3860 3861

int
ha_innobase::index_end(void)
/*========================*/
{
unknown's avatar
unknown committed
3862 3863 3864 3865
	int	error	= 0;
	DBUG_ENTER("index_end");
	active_index=MAX_KEY;
	DBUG_RETURN(error);
3866 3867 3868 3869
}

/*************************************************************************
Converts a search mode flag understood by MySQL to a flag understood
3870
by InnoDB. */
3871 3872 3873 3874 3875 3876 3877
inline
ulint
convert_search_mode_to_innobase(
/*============================*/
	enum ha_rkey_function	find_flag)
{
	switch (find_flag) {
unknown's avatar
unknown committed
3878 3879 3880
		case HA_READ_KEY_EXACT:		return(PAGE_CUR_GE);
			/* the above does not require the index to be UNIQUE */
		case HA_READ_KEY_OR_NEXT:	return(PAGE_CUR_GE);
3881 3882 3883 3884
		case HA_READ_KEY_OR_PREV:	return(PAGE_CUR_LE);
		case HA_READ_AFTER_KEY:		return(PAGE_CUR_G);
		case HA_READ_BEFORE_KEY:	return(PAGE_CUR_L);
		case HA_READ_PREFIX:		return(PAGE_CUR_GE);
unknown's avatar
unknown committed
3885 3886
		case HA_READ_PREFIX_LAST:	return(PAGE_CUR_LE);
		case HA_READ_PREFIX_LAST_OR_PREV:return(PAGE_CUR_LE);
unknown's avatar
unknown committed
3887 3888
		  /* In MySQL-4.0 HA_READ_PREFIX and HA_READ_PREFIX_LAST always
		  pass a complete-field prefix of a key value as the search
unknown's avatar
unknown committed
3889 3890 3891 3892 3893
		  tuple. I.e., it is not allowed that the last field would
		  just contain n first bytes of the full field value.
		  MySQL uses a 'padding' trick to convert LIKE 'abc%'
		  type queries so that it can use as a search tuple
		  a complete-field-prefix of a key value. Thus, the InnoDB
unknown's avatar
unknown committed
3894 3895 3896 3897 3898 3899 3900
		  search mode PAGE_CUR_LE_OR_EXTENDS is never used.
		  TODO: when/if MySQL starts to use also partial-field
		  prefixes, we have to deal with stripping of spaces
		  and comparison of non-latin1 char type fields in
		  innobase_mysql_cmp() to get PAGE_CUR_LE_OR_EXTENDS to
		  work correctly. */

3901 3902 3903 3904 3905
		default:			assert(0);
	}

	return(0);
}
3906

unknown's avatar
unknown committed
3907 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955
/*
   BACKGROUND INFO: HOW A SELECT SQL QUERY IS EXECUTED
   ---------------------------------------------------
The following does not cover all the details, but explains how we determine
the start of a new SQL statement, and what is associated with it.

For each table in the database the MySQL interpreter may have several
table handle instances in use, also in a single SQL query. For each table
handle instance there is an InnoDB  'prebuilt' struct which contains most
of the InnoDB data associated with this table handle instance.

  A) if the user has not explicitly set any MySQL table level locks:

  1) MySQL calls ::external_lock to set an 'intention' table level lock on
the table of the handle instance. There we set
prebuilt->sql_stat_start = TRUE. The flag sql_stat_start should be set
true if we are taking this table handle instance to use in a new SQL
statement issued by the user. We also increment trx->n_mysql_tables_in_use.

  2) If prebuilt->sql_stat_start == TRUE we 'pre-compile' the MySQL search
instructions to prebuilt->template of the table handle instance in
::index_read. The template is used to save CPU time in large joins.

  3) In row_search_for_mysql, if prebuilt->sql_stat_start is true, we
allocate a new consistent read view for the trx if it does not yet have one,
or in the case of a locking read, set an InnoDB 'intention' table level
lock on the table.

  4) We do the SELECT. MySQL may repeatedly call ::index_read for the
same table handle instance, if it is a join.

  5) When the SELECT ends, MySQL removes its intention table level locks
in ::external_lock. When trx->n_mysql_tables_in_use drops to zero,
 (a) we execute a COMMIT there if the autocommit is on,
 (b) we also release possible 'SQL statement level resources' InnoDB may
have for this SQL statement. The MySQL interpreter does NOT execute
autocommit for pure read transactions, though it should. That is why the
table handler in that case has to execute the COMMIT in ::external_lock.

  B) If the user has explicitly set MySQL table level locks, then MySQL
does NOT call ::external_lock at the start of the statement. To determine
when we are at the start of a new SQL statement we at the start of
::index_read also compare the query id to the latest query id where the
table handle instance was used. If it has changed, we know we are at the
start of a new SQL statement. Since the query id can theoretically
overwrap, we use this test only as a secondary way of determining the
start of a new SQL statement. */


3956 3957 3958 3959 3960 3961 3962 3963 3964
/**************************************************************************
Positions an index cursor to the index specified in the handle. Fetches the
row if any. */

int
ha_innobase::index_read(
/*====================*/
					/* out: 0, HA_ERR_KEY_NOT_FOUND,
					or error number */
3965
	mysql_byte*		buf,	/* in/out: buffer for the returned
3966
					row */
unknown's avatar
unknown committed
3967
	const mysql_byte*	key_ptr,/* in: key value; if this is NULL
3968
					we position the cursor at the
unknown's avatar
unknown committed
3969 3970 3971
					start or end of index; this can
					also contain an InnoDB row id, in
					which case key_len is the InnoDB
unknown's avatar
unknown committed
3972 3973 3974 3975
					row id length; the key value can
					also be a prefix of a full key value,
					and the last column can be a prefix
					of a full column */
3976
	uint			key_len,/* in: key value length */
3977 3978 3979 3980 3981
	enum ha_rkey_function find_flag)/* in: search flags from my_base.h */
{
	row_prebuilt_t* prebuilt	= (row_prebuilt_t*) innobase_prebuilt;
	ulint		mode;
	dict_index_t*	index;
unknown's avatar
unknown committed
3982 3983
	ulint		match_mode	= 0;
	int		error;
3984 3985
	ulint		ret;

unknown's avatar
unknown committed
3986
	DBUG_ENTER("index_read");
unknown's avatar
unknown committed
3987

unknown's avatar
unknown committed
3988
	ut_ad(prebuilt->trx ==
unknown's avatar
unknown committed
3989
		(trx_t*) current_thd->ha_data[innobase_hton.slot]);
unknown's avatar
unknown committed
3990

unknown's avatar
unknown committed
3991 3992
	statistic_increment(current_thd->status_var.ha_read_key_count,
		&LOCK_status);
3993

unknown's avatar
unknown committed
3994
	if (last_query_id != user_thd->query_id) {
unknown's avatar
unknown committed
3995 3996
		prebuilt->sql_stat_start = TRUE;
		last_query_id = user_thd->query_id;
unknown's avatar
unknown committed
3997 3998

		innobase_release_stat_resources(prebuilt->trx);
unknown's avatar
unknown committed
3999 4000
	}

4001
	index = prebuilt->index;
4002

unknown's avatar
unknown committed
4003
	/* Note that if the index for which the search template is built is not
unknown's avatar
unknown committed
4004
	necessarily prebuilt->index, but can also be the clustered index */
4005

4006 4007 4008 4009
	if (prebuilt->sql_stat_start) {
		build_template(prebuilt, user_thd, table,
							ROW_MYSQL_REC_FIELDS);
	}
4010 4011

	if (key_ptr) {
unknown's avatar
unknown committed
4012
		/* Convert the search key value to InnoDB format into
unknown's avatar
unknown committed
4013 4014
		prebuilt->search_tuple */

4015
		row_sel_convert_mysql_key_to_innobase(prebuilt->search_tuple,
unknown's avatar
unknown committed
4016 4017 4018 4019
					(byte*) key_val_buff,
					(ulint)upd_and_key_val_buff_len,
					index,
					(byte*) key_ptr,
4020
					(ulint) key_len, prebuilt->trx);
4021 4022 4023 4024
	} else {
		/* We position the cursor to the last or the first entry
		in the index */

unknown's avatar
unknown committed
4025
		dtuple_set_n_fields(prebuilt->search_tuple, 0);
4026
	}
4027

4028 4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039
	mode = convert_search_mode_to_innobase(find_flag);

	match_mode = 0;

	if (find_flag == HA_READ_KEY_EXACT) {
		match_mode = ROW_SEL_EXACT;

	} else if (find_flag == HA_READ_PREFIX
				|| find_flag == HA_READ_PREFIX_LAST) {
		match_mode = ROW_SEL_EXACT_PREFIX;
	}

unknown's avatar
unknown committed
4040
	last_match_mode = (uint) match_mode;
4041

unknown's avatar
unknown committed
4042
	innodb_srv_conc_enter_innodb(prebuilt->trx);
unknown's avatar
Merge  
unknown committed
4043

unknown's avatar
unknown committed
4044
	ret = row_search_for_mysql((byte*) buf, mode, prebuilt, match_mode, 0);
4045

unknown's avatar
unknown committed
4046
	innodb_srv_conc_exit_innodb(prebuilt->trx);
unknown's avatar
Merge  
unknown committed
4047

4048 4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059
	if (ret == DB_SUCCESS) {
		error = 0;
		table->status = 0;

	} else if (ret == DB_RECORD_NOT_FOUND) {
		error = HA_ERR_KEY_NOT_FOUND;
		table->status = STATUS_NOT_FOUND;

	} else if (ret == DB_END_OF_INDEX) {
		error = HA_ERR_KEY_NOT_FOUND;
		table->status = STATUS_NOT_FOUND;
	} else {
unknown's avatar
unknown committed
4060
		error = convert_error_code_to_mysql((int) ret, user_thd);
4061 4062
		table->status = STATUS_NOT_FOUND;
	}
4063

4064 4065 4066
	DBUG_RETURN(error);
}

unknown's avatar
unknown committed
4067 4068 4069
/***********************************************************************
The following functions works like index_read, but it find the last
row with the current key value or prefix. */
4070 4071

int
unknown's avatar
unknown committed
4072 4073
ha_innobase::index_read_last(
/*=========================*/
unknown's avatar
unknown committed
4074
				   /* out: 0, HA_ERR_KEY_NOT_FOUND, or an
unknown's avatar
unknown committed
4075
				   error code */
unknown's avatar
unknown committed
4076 4077
	mysql_byte*	  buf,	   /* out: fetched row */
	const mysql_byte* key_ptr, /* in: key value, or a prefix of a full
unknown's avatar
unknown committed
4078
				   key value */
unknown's avatar
unknown committed
4079
	uint		  key_len) /* in: length of the key val or prefix
unknown's avatar
unknown committed
4080
				   in bytes */
4081
{
unknown's avatar
unknown committed
4082
	return(index_read(buf, key_ptr, key_len, HA_READ_PREFIX_LAST));
4083 4084
}

4085
/************************************************************************
unknown's avatar
unknown committed
4086
Changes the active index of a handle. */
4087 4088 4089 4090

int
ha_innobase::change_active_index(
/*=============================*/
4091
			/* out: 0 or error code */
unknown's avatar
unknown committed
4092
	uint	keynr)	/* in: use this index; MAX_KEY means always clustered
4093
			index, even if it was internally generated by
4094
			InnoDB */
4095
{
unknown's avatar
unknown committed
4096 4097
	row_prebuilt_t* prebuilt	= (row_prebuilt_t*) innobase_prebuilt;
	KEY*		key=0;
4098
	statistic_increment(current_thd->status_var.ha_read_key_count,
unknown's avatar
unknown committed
4099
		&LOCK_status);
unknown's avatar
unknown committed
4100
	DBUG_ENTER("change_active_index");
4101

unknown's avatar
unknown committed
4102 4103
	ut_ad(user_thd == current_thd);
	ut_ad(prebuilt->trx ==
unknown's avatar
unknown committed
4104
		(trx_t*) current_thd->ha_data[innobase_hton.slot]);
unknown's avatar
unknown committed
4105

unknown's avatar
unknown committed
4106
	active_index = keynr;
4107

4108
	if (keynr != MAX_KEY && table->s->keys > 0) {
unknown's avatar
unknown committed
4109
		key = table->key_info + active_index;
4110

unknown's avatar
unknown committed
4111
		prebuilt->index = dict_table_get_index_noninline(
unknown's avatar
unknown committed
4112 4113
			prebuilt->table, key->name);
	} else {
unknown's avatar
unknown committed
4114
		prebuilt->index = dict_table_get_first_index_noninline(
unknown's avatar
unknown committed
4115
							   prebuilt->table);
unknown's avatar
unknown committed
4116
	}
4117

unknown's avatar
unknown committed
4118
	if (!prebuilt->index) {
unknown's avatar
unknown committed
4119 4120 4121 4122 4123 4124 4125
		sql_print_error(
			"Innodb could not find key n:o %u with name %s "
			"from dict cache for table %s",
			keynr, key ? key->name : "NULL",
			prebuilt->table->name);

		DBUG_RETURN(1);
unknown's avatar
unknown committed
4126
	}
4127

unknown's avatar
unknown committed
4128
	assert(prebuilt->search_tuple != 0);
unknown's avatar
Merge  
unknown committed
4129

unknown's avatar
unknown committed
4130
	dtuple_set_n_fields(prebuilt->search_tuple, prebuilt->index->n_fields);
4131

unknown's avatar
unknown committed
4132
	dict_index_copy_types(prebuilt->search_tuple, prebuilt->index,
4133
			prebuilt->index->n_fields);
4134

unknown's avatar
unknown committed
4135 4136 4137 4138 4139
	/* MySQL changes the active index for a handle also during some
	queries, for example SELECT MAX(a), SUM(a) first retrieves the MAX()
	and then calculates the sum. Previously we played safe and used
	the flag ROW_MYSQL_WHOLE_ROW below, but that caused unnecessary
	copying. Starting from MySQL-4.1 we use a more efficient flag here. */
4140

unknown's avatar
unknown committed
4141
	build_template(prebuilt, user_thd, table, ROW_MYSQL_REC_FIELDS);
4142

unknown's avatar
unknown committed
4143
	DBUG_RETURN(0);
4144 4145 4146 4147 4148 4149 4150 4151 4152 4153 4154
}

/**************************************************************************
Positions an index cursor to the index specified in keynr. Fetches the
row if any. */
/* ??? This is only used to read whole keys ??? */

int
ha_innobase::index_read_idx(
/*========================*/
					/* out: error number or 0 */
4155
	mysql_byte*	buf,		/* in/out: buffer for the returned
4156
					row */
unknown's avatar
unknown committed
4157
	uint		keynr,		/* in: use this index */
4158
	const mysql_byte* key,		/* in: key value; if this is NULL
4159 4160 4161 4162 4163
					we position the cursor at the
					start or end of index */
	uint		key_len,	/* in: key value length */
	enum ha_rkey_function find_flag)/* in: search flags from my_base.h */
{
unknown's avatar
Merge  
unknown committed
4164 4165 4166 4167
	if (change_active_index(keynr)) {

		return(1);
	}
4168 4169 4170 4171 4172 4173 4174 4175 4176 4177 4178 4179 4180

	return(index_read(buf, key, key_len, find_flag));
}

/***************************************************************************
Reads the next or previous row from a cursor, which must have previously been
positioned using index_read. */

int
ha_innobase::general_fetch(
/*=======================*/
				/* out: 0, HA_ERR_END_OF_FILE, or error
				number */
unknown's avatar
unknown committed
4181
	mysql_byte*	buf,	/* in/out: buffer for next row in MySQL
4182
				format */
unknown's avatar
unknown committed
4183
	uint	direction,	/* in: ROW_SEL_NEXT or ROW_SEL_PREV */
4184 4185 4186 4187 4188 4189
	uint	match_mode)	/* in: 0, ROW_SEL_EXACT, or
				ROW_SEL_EXACT_PREFIX */
{
	row_prebuilt_t*	prebuilt = (row_prebuilt_t*) innobase_prebuilt;
	ulint		ret;
	int		error	= 0;
4190

4191
	DBUG_ENTER("general_fetch");
4192

unknown's avatar
unknown committed
4193
	ut_ad(prebuilt->trx ==
unknown's avatar
unknown committed
4194
		(trx_t*) current_thd->ha_data[innobase_hton.slot]);
unknown's avatar
unknown committed
4195

unknown's avatar
unknown committed
4196
	innodb_srv_conc_enter_innodb(prebuilt->trx);
unknown's avatar
unknown committed
4197

unknown's avatar
Merge  
unknown committed
4198 4199
	ret = row_search_for_mysql((byte*)buf, 0, prebuilt, match_mode,
								direction);
unknown's avatar
unknown committed
4200
	innodb_srv_conc_exit_innodb(prebuilt->trx);
4201 4202 4203 4204 4205 4206 4207 4208 4209 4210 4211 4212 4213

	if (ret == DB_SUCCESS) {
		error = 0;
		table->status = 0;

	} else if (ret == DB_RECORD_NOT_FOUND) {
		error = HA_ERR_END_OF_FILE;
		table->status = STATUS_NOT_FOUND;

	} else if (ret == DB_END_OF_INDEX) {
		error = HA_ERR_END_OF_FILE;
		table->status = STATUS_NOT_FOUND;
	} else {
unknown's avatar
unknown committed
4214
		error = convert_error_code_to_mysql((int) ret, user_thd);
4215 4216
		table->status = STATUS_NOT_FOUND;
	}
4217

4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229
	DBUG_RETURN(error);
}

/***************************************************************************
Reads the next row from a cursor, which must have previously been
positioned using index_read. */

int
ha_innobase::index_next(
/*====================*/
				/* out: 0, HA_ERR_END_OF_FILE, or error
				number */
unknown's avatar
unknown committed
4230
	mysql_byte*	buf)	/* in/out: buffer for next row in MySQL
4231 4232
				format */
{
unknown's avatar
unknown committed
4233 4234
	statistic_increment(current_thd->status_var.ha_read_next_count,
		&LOCK_status);
4235

4236 4237 4238 4239 4240 4241 4242 4243 4244 4245 4246
	return(general_fetch(buf, ROW_SEL_NEXT, 0));
}

/***********************************************************************
Reads the next row matching to the key value given as the parameter. */

int
ha_innobase::index_next_same(
/*=========================*/
				/* out: 0, HA_ERR_END_OF_FILE, or error
				number */
unknown's avatar
unknown committed
4247
	mysql_byte*	buf,	/* in/out: buffer for the row */
4248
	const mysql_byte* key,	/* in: key value */
unknown's avatar
unknown committed
4249
	uint		keylen)	/* in: key value length */
4250
{
unknown's avatar
unknown committed
4251 4252
	statistic_increment(current_thd->status_var.ha_read_next_count,
		&LOCK_status);
4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265

	return(general_fetch(buf, ROW_SEL_NEXT, last_match_mode));
}

/***************************************************************************
Reads the previous row from a cursor, which must have previously been
positioned using index_read. */

int
ha_innobase::index_prev(
/*====================*/
				/* out: 0, HA_ERR_END_OF_FILE, or error
				number */
unknown's avatar
unknown committed
4266
	mysql_byte*	buf)	/* in/out: buffer for previous row in MySQL
4267 4268
				format */
{
unknown's avatar
unknown committed
4269 4270 4271
	statistic_increment(current_thd->status_var.ha_read_prev_count,
		&LOCK_status);

4272 4273 4274 4275 4276 4277 4278 4279 4280 4281
	return(general_fetch(buf, ROW_SEL_PREV, 0));
}

/************************************************************************
Positions a cursor on the first record in an index and reads the
corresponding row to buf. */

int
ha_innobase::index_first(
/*=====================*/
4282
				/* out: 0, HA_ERR_END_OF_FILE,
4283 4284
				or error code */
	mysql_byte*	buf)	/* in/out: buffer for the row */
4285 4286 4287
{
	int	error;

unknown's avatar
unknown committed
4288 4289 4290
	DBUG_ENTER("index_first");
	statistic_increment(current_thd->status_var.ha_read_first_count,
		&LOCK_status);
4291

unknown's avatar
unknown committed
4292
	error = index_read(buf, NULL, 0, HA_READ_AFTER_KEY);
4293

unknown's avatar
unknown committed
4294
	/* MySQL does not seem to allow this to return HA_ERR_KEY_NOT_FOUND */
4295

unknown's avatar
unknown committed
4296 4297 4298
	if (error == HA_ERR_KEY_NOT_FOUND) {
		error = HA_ERR_END_OF_FILE;
	}
4299

unknown's avatar
unknown committed
4300
	DBUG_RETURN(error);
4301 4302 4303 4304 4305 4306 4307 4308 4309
}

/************************************************************************
Positions a cursor on the last record in an index and reads the
corresponding row to buf. */

int
ha_innobase::index_last(
/*====================*/
4310 4311
				/* out: 0, HA_ERR_END_OF_FILE, or error code */
	mysql_byte*	buf)	/* in/out: buffer for the row */
4312 4313 4314
{
	int	error;

unknown's avatar
unknown committed
4315 4316 4317
	DBUG_ENTER("index_last");
	statistic_increment(current_thd->status_var.ha_read_last_count,
		&LOCK_status);
4318

unknown's avatar
unknown committed
4319
	error = index_read(buf, NULL, 0, HA_READ_BEFORE_KEY);
4320

unknown's avatar
unknown committed
4321
	/* MySQL does not seem to allow this to return HA_ERR_KEY_NOT_FOUND */
4322

unknown's avatar
unknown committed
4323 4324 4325
	if (error == HA_ERR_KEY_NOT_FOUND) {
		error = HA_ERR_END_OF_FILE;
	}
4326

unknown's avatar
unknown committed
4327
	DBUG_RETURN(error);
4328 4329 4330 4331 4332 4333 4334 4335 4336
}

/********************************************************************
Initialize a table scan. */

int
ha_innobase::rnd_init(
/*==================*/
			/* out: 0 or error number */
4337
	bool	scan)	/* in: ???????? */
4338
{
unknown's avatar
Merge  
unknown committed
4339
	int	err;
unknown's avatar
unknown committed
4340

4341
	row_prebuilt_t*	prebuilt = (row_prebuilt_t*) innobase_prebuilt;
4342

unknown's avatar
unknown committed
4343 4344 4345
	/* Store the active index value so that we can restore the original
	value after a scan */

4346
	if (prebuilt->clust_index_was_generated) {
unknown's avatar
Merge  
unknown committed
4347
		err = change_active_index(MAX_KEY);
4348
	} else {
unknown's avatar
Merge  
unknown committed
4349
		err = change_active_index(primary_key);
4350
	}
4351

4352 4353 4354 4355 4356 4357 4358
	/* Don't use semi-consistent read in random row reads (by position).
	This means we must disable semi_consistent_read if scan is false */

	if (!scan) {
		try_semi_consistent_read(0);
	}

unknown's avatar
unknown committed
4359
	start_of_scan = 1;
4360

unknown's avatar
unknown committed
4361
	return(err);
4362 4363 4364
}

/*********************************************************************
unknown's avatar
unknown committed
4365
Ends a table scan. */
4366 4367 4368 4369 4370 4371

int
ha_innobase::rnd_end(void)
/*======================*/
				/* out: 0 or error number */
{
unknown's avatar
unknown committed
4372
	return(index_end());
4373 4374 4375 4376 4377 4378 4379 4380 4381 4382
}

/*********************************************************************
Reads the next row in a table scan (also used to read the FIRST row
in a table scan). */

int
ha_innobase::rnd_next(
/*==================*/
			/* out: 0, HA_ERR_END_OF_FILE, or error number */
4383
	mysql_byte* buf)/* in/out: returns the row in this buffer,
4384 4385
			in MySQL format */
{
4386
	int	error;
4387

unknown's avatar
unknown committed
4388 4389 4390
	DBUG_ENTER("rnd_next");
	statistic_increment(current_thd->status_var.ha_read_rnd_next_count,
		&LOCK_status);
4391

unknown's avatar
unknown committed
4392
	if (start_of_scan) {
4393 4394 4395 4396
		error = index_first(buf);
		if (error == HA_ERR_KEY_NOT_FOUND) {
			error = HA_ERR_END_OF_FILE;
		}
4397
		start_of_scan = 0;
4398
	} else {
4399
		error = general_fetch(buf, ROW_SEL_NEXT, 0);
4400
	}
4401

unknown's avatar
unknown committed
4402
	DBUG_RETURN(error);
4403 4404 4405
}

/**************************************************************************
unknown's avatar
unknown committed
4406
Fetches a row from the table based on a row reference. */
4407

4408 4409 4410
int
ha_innobase::rnd_pos(
/*=================*/
4411 4412
				/* out: 0, HA_ERR_KEY_NOT_FOUND,
				or error code */
unknown's avatar
unknown committed
4413
	mysql_byte*	buf,	/* in/out: buffer for the row */
unknown's avatar
unknown committed
4414 4415 4416 4417 4418
	mysql_byte*	pos)	/* in: primary key value of the row in the
				MySQL format, or the row id if the clustered
				index was internally generated by InnoDB;
				the length of data in pos has to be
				ref_length */
4419
{
4420 4421 4422
	row_prebuilt_t*	prebuilt = (row_prebuilt_t*) innobase_prebuilt;
	int		error;
	uint		keynr	= active_index;
4423
	DBUG_ENTER("rnd_pos");
unknown's avatar
unknown committed
4424
	DBUG_DUMP("key", (char*) pos, ref_length);
unknown's avatar
unknown committed
4425

4426
	statistic_increment(current_thd->status_var.ha_read_rnd_count,
unknown's avatar
unknown committed
4427
		&LOCK_status);
4428

unknown's avatar
unknown committed
4429
	ut_ad(prebuilt->trx ==
unknown's avatar
unknown committed
4430
		(trx_t*) current_thd->ha_data[innobase_hton.slot]);
unknown's avatar
unknown committed
4431

4432 4433 4434 4435
	if (prebuilt->clust_index_was_generated) {
		/* No primary key was defined for the table and we
		generated the clustered index from the row id: the
		row reference is the row id, not any key value
unknown's avatar
unknown committed
4436
		that MySQL knows of */
4437

unknown's avatar
Merge  
unknown committed
4438
		error = change_active_index(MAX_KEY);
4439
	} else {
unknown's avatar
Merge  
unknown committed
4440
		error = change_active_index(primary_key);
4441
	}
4442

unknown's avatar
Merge  
unknown committed
4443
	if (error) {
unknown's avatar
unknown committed
4444
		DBUG_PRINT("error", ("Got error: %ld", error));
unknown's avatar
Merge  
unknown committed
4445 4446
		DBUG_RETURN(error);
	}
unknown's avatar
unknown committed
4447

unknown's avatar
unknown committed
4448
	/* Note that we assume the length of the row reference is fixed
unknown's avatar
unknown committed
4449
	for the table, and it is == ref_length */
unknown's avatar
unknown committed
4450 4451

	error = index_read(buf, pos, ref_length, HA_READ_KEY_EXACT);
unknown's avatar
unknown committed
4452 4453 4454

	if (error) {
		DBUG_PRINT("error", ("Got error: %ld", error));
unknown's avatar
unknown committed
4455
	}
unknown's avatar
unknown committed
4456

4457
	change_active_index(keynr);
4458

unknown's avatar
unknown committed
4459
	DBUG_RETURN(error);
4460 4461 4462
}

/*************************************************************************
4463
Stores a reference to the current row to 'ref' field of the handle. Note
unknown's avatar
unknown committed
4464 4465
that in the case where we have generated the clustered index for the
table, the function parameter is illogical: we MUST ASSUME that 'record'
unknown's avatar
unknown committed
4466
is the current 'position' of the handle, because if row ref is actually
4467
the row id internally generated in InnoDB, then 'record' does not contain
4468 4469
it. We just guess that the row id must be for the record where the handle
was positioned the last time. */
4470 4471 4472 4473

void
ha_innobase::position(
/*==================*/
4474
	const mysql_byte*	record)	/* in: row in MySQL format */
4475
{
4476 4477
	row_prebuilt_t*	prebuilt = (row_prebuilt_t*) innobase_prebuilt;
	uint		len;
4478

unknown's avatar
unknown committed
4479
	ut_ad(prebuilt->trx ==
unknown's avatar
unknown committed
4480
		(trx_t*) current_thd->ha_data[innobase_hton.slot]);
unknown's avatar
unknown committed
4481

4482 4483 4484 4485
	if (prebuilt->clust_index_was_generated) {
		/* No primary key was defined for the table and we
		generated the clustered index from row id: the
		row reference will be the row id, not any key value
unknown's avatar
unknown committed
4486
		that MySQL knows of */
4487 4488 4489 4490 4491

		len = DATA_ROW_ID_LEN;

		memcpy(ref, prebuilt->row_id, len);
	} else {
4492 4493
		len = store_key_val_for_row(primary_key, (char*)ref,
							 ref_length, record);
4494
	}
4495

unknown's avatar
unknown committed
4496 4497
	/* We assume that the 'ref' value len is always fixed for the same
	table. */
unknown's avatar
unknown committed
4498

unknown's avatar
unknown committed
4499
	if (len != ref_length) {
4500
	  sql_print_error("Stored ref len is %lu, but table ref len is %lu",
unknown's avatar
unknown committed
4501
			  (ulong) len, (ulong) ref_length);
unknown's avatar
unknown committed
4502
	}
4503 4504 4505
}

/*********************************************************************
4506
Creates a table definition to an InnoDB database. */
4507 4508 4509 4510
static
int
create_table_def(
/*=============*/
4511
	trx_t*		trx,		/* in: InnoDB transaction handle */
4512 4513
	TABLE*		form,		/* in: information on table
					columns and indexes */
unknown's avatar
unknown committed
4514
	const char*	table_name,	/* in: table name */
unknown's avatar
unknown committed
4515
	const char*	path_of_temp_table,/* in: if this is a table explicitly
unknown's avatar
unknown committed
4516 4517 4518 4519 4520 4521 4522
					created by the user with the
					TEMPORARY keyword, then this
					parameter is the dir path where the
					table should be placed if we create
					an .ibd file for it (no .ibd extension
					in the path, though); otherwise this
					is NULL */
unknown's avatar
unknown committed
4523
	ulint		flags)		/* in: table flags */
4524 4525 4526 4527
{
	Field*		field;
	dict_table_t*	table;
	ulint		n_cols;
unknown's avatar
unknown committed
4528 4529
	int		error;
	ulint		col_type;
unknown's avatar
unknown committed
4530
	ulint		col_len;
unknown's avatar
unknown committed
4531
	ulint		nulls_allowed;
4532
	ulint		unsigned_type;
unknown's avatar
unknown committed
4533
	ulint		binary_type;
unknown's avatar
unknown committed
4534
	ulint		long_true_varchar;
unknown's avatar
unknown committed
4535
	ulint		charset_no;
unknown's avatar
unknown committed
4536
	ulint		i;
4537

unknown's avatar
unknown committed
4538 4539
	DBUG_ENTER("create_table_def");
	DBUG_PRINT("enter", ("table_name: %s", table_name));
4540

4541
	n_cols = form->s->fields;
4542

unknown's avatar
unknown committed
4543 4544
	/* We pass 0 as the space id, and determine at a lower level the space
	id where to store the table */
4545

unknown's avatar
unknown committed
4546
	table = dict_mem_table_create(table_name, 0, n_cols, flags);
4547

unknown's avatar
unknown committed
4548 4549 4550 4551 4552
	if (path_of_temp_table) {
		table->dir_path_of_temp_table =
			mem_heap_strdup(table->heap, path_of_temp_table);
	}

4553 4554 4555
	for (i = 0; i < n_cols; i++) {
		field = form->field[i];

unknown's avatar
unknown committed
4556 4557
		col_type = get_innobase_type_from_mysql_type(&unsigned_type,
									field);
4558 4559 4560 4561 4562 4563
		if (field->null_ptr) {
			nulls_allowed = 0;
		} else {
			nulls_allowed = DATA_NOT_NULL;
		}

unknown's avatar
unknown committed
4564
		if (field->binary()) {
unknown's avatar
unknown committed
4565 4566 4567 4568 4569
			binary_type = DATA_BINARY_TYPE;
		} else {
			binary_type = 0;
		}

unknown's avatar
unknown committed
4570
		charset_no = 0;
unknown's avatar
unknown committed
4571 4572 4573 4574 4575

		if (dtype_is_string_type(col_type)) {

			charset_no = (ulint)field->charset()->number;

unknown's avatar
unknown committed
4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 4586 4587 4588
			ut_a(charset_no < 256); /* in data0type.h we assume
						that the number fits in one
						byte */
		}

		ut_a(field->type() < 256); /* we assume in dtype_form_prtype()
					   that this fits in one byte */
		col_len = field->pack_length();

		/* The MySQL pack length contains 1 or 2 bytes length field
		for a true VARCHAR. Let us subtract that, so that the InnoDB
		column length in the InnoDB data dictionary is the real
		maximum byte length of the actual data. */
unknown's avatar
unknown committed
4589

unknown's avatar
unknown committed
4590 4591 4592 4593 4594 4595 4596 4597
		long_true_varchar = 0;

		if (field->type() == MYSQL_TYPE_VARCHAR) {
			col_len -= ((Field_varstring*)field)->length_bytes;

			if (((Field_varstring*)field)->length_bytes == 2) {
				long_true_varchar = DATA_LONG_TRUE_VARCHAR;
			}
unknown's avatar
unknown committed
4598 4599
		}

unknown's avatar
unknown committed
4600
		dict_mem_table_add_col(table,
unknown's avatar
unknown committed
4601 4602 4603 4604 4605 4606 4607 4608 4609
			(char*) field->field_name,
			col_type,
			dtype_form_prtype(
				(ulint)field->type()
				| nulls_allowed | unsigned_type
				| binary_type | long_true_varchar,
				charset_no),
			col_len,
			0);
4610 4611 4612 4613
	}

	error = row_create_table_for_mysql(table, trx);

unknown's avatar
unknown committed
4614
	error = convert_error_code_to_mysql(error, NULL);
4615 4616 4617 4618 4619

	DBUG_RETURN(error);
}

/*********************************************************************
4620
Creates an index in an InnoDB database. */
4621 4622
static
int
4623 4624
create_index(
/*=========*/
4625
	trx_t*		trx,		/* in: InnoDB transaction handle */
4626 4627 4628 4629 4630
	TABLE*		form,		/* in: information on table
					columns and indexes */
	const char*	table_name,	/* in: table name */
	uint		key_num)	/* in: index number */
{
unknown's avatar
unknown committed
4631
	Field*		field;
4632
	dict_index_t*	index;
unknown's avatar
unknown committed
4633
	int		error;
4634 4635 4636 4637
	ulint		n_fields;
	KEY*		key;
	KEY_PART_INFO*	key_part;
	ulint		ind_type;
unknown's avatar
unknown committed
4638 4639
	ulint		col_type;
	ulint		prefix_len;
unknown's avatar
unknown committed
4640
	ulint		is_unsigned;
unknown's avatar
unknown committed
4641 4642
	ulint		i;
	ulint		j;
4643
	ulint*		field_lengths;
unknown's avatar
unknown committed
4644 4645

	DBUG_ENTER("create_index");
4646

4647 4648
	key = form->key_info + key_num;

unknown's avatar
unknown committed
4649
	n_fields = key->key_parts;
4650

unknown's avatar
unknown committed
4651
	ind_type = 0;
4652

unknown's avatar
unknown committed
4653
	if (key_num == form->s->primary_key) {
4654 4655
		ind_type = ind_type | DICT_CLUSTERED;
	}
4656

4657 4658 4659 4660
	if (key->flags & HA_NOSAME ) {
		ind_type = ind_type | DICT_UNIQUE;
	}

unknown's avatar
unknown committed
4661 4662
	/* We pass 0 as the space id, and determine at a lower level the space
	id where to store the table */
4663 4664 4665

	index = dict_mem_index_create((char*) table_name, key->name, 0,
						ind_type, n_fields);
4666 4667 4668

	field_lengths = (ulint*) my_malloc(sizeof(ulint) * n_fields,
		MYF(MY_FAE));
unknown's avatar
unknown committed
4669

4670 4671 4672
	for (i = 0; i < n_fields; i++) {
		key_part = key->key_part + i;

4673
		/* (The flag HA_PART_KEY_SEG denotes in MySQL a column prefix
unknown's avatar
unknown committed
4674 4675 4676 4677
		field in an index: we only store a specified number of first
		bytes of the column to the index field.) The flag does not
		seem to be properly set by MySQL. Let us fall back on testing
		the length of the key part versus the column. */
unknown's avatar
unknown committed
4678

unknown's avatar
unknown committed
4679
		field = NULL;
4680
		for (j = 0; j < form->s->fields; j++) {
unknown's avatar
unknown committed
4681 4682 4683

			field = form->field[j];

4684 4685 4686
			if (0 == innobase_strcasecmp(
					field->field_name,
					key_part->field->field_name)) {
unknown's avatar
unknown committed
4687 4688 4689 4690 4691 4692
				/* Found the corresponding column */

				break;
			}
		}

4693
		ut_a(j < form->s->fields);
unknown's avatar
unknown committed
4694

unknown's avatar
unknown committed
4695 4696
		col_type = get_innobase_type_from_mysql_type(
					&is_unsigned, key_part->field);
unknown's avatar
unknown committed
4697 4698

		if (DATA_BLOB == col_type
unknown's avatar
unknown committed
4699 4700 4701 4702 4703
			|| (key_part->length < field->pack_length()
				&& field->type() != MYSQL_TYPE_VARCHAR)
			|| (field->type() == MYSQL_TYPE_VARCHAR
				&& key_part->length < field->pack_length()
				- ((Field_varstring*)field)->length_bytes)) {
unknown's avatar
unknown committed
4704

unknown's avatar
unknown committed
4705
			prefix_len = key_part->length;
unknown's avatar
unknown committed
4706 4707

			if (col_type == DATA_INT
unknown's avatar
unknown committed
4708 4709 4710 4711 4712 4713 4714 4715 4716 4717 4718 4719
				|| col_type == DATA_FLOAT
				|| col_type == DATA_DOUBLE
				|| col_type == DATA_DECIMAL) {
				sql_print_error(
					"MySQL is trying to create a column "
					"prefix index field, on an "
					"inappropriate data type. Table "
					"name %s, column name %s.",
					table_name,
					key_part->field->field_name);

				prefix_len = 0;
unknown's avatar
unknown committed
4720 4721
			}
		} else {
unknown's avatar
unknown committed
4722
			prefix_len = 0;
unknown's avatar
unknown committed
4723 4724
		}

4725 4726
		field_lengths[i] = key_part->length;

4727
		dict_mem_index_add_field(index,
unknown's avatar
unknown committed
4728
			(char*) key_part->field->field_name, prefix_len);
4729 4730
	}

4731 4732 4733
	/* Even though we've defined max_supported_key_part_length, we
	still do our own checking using field_lengths to be absolutely
	sure we don't create too long indexes. */
4734
	error = row_create_index_for_mysql(index, trx, field_lengths);
4735

unknown's avatar
unknown committed
4736
	error = convert_error_code_to_mysql(error, NULL);
4737

4738
	my_free((gptr) field_lengths, MYF(0));
unknown's avatar
unknown committed
4739

4740 4741 4742 4743
	DBUG_RETURN(error);
}

/*********************************************************************
4744
Creates an index to an InnoDB table when the user has defined no
4745
primary index. */
4746 4747
static
int
4748 4749
create_clustered_index_when_no_primary(
/*===================================*/
4750
	trx_t*		trx,		/* in: InnoDB transaction handle */
4751 4752 4753
	const char*	table_name)	/* in: table name */
{
	dict_index_t*	index;
unknown's avatar
unknown committed
4754
	int		error;
4755

unknown's avatar
unknown committed
4756 4757
	/* We pass 0 as the space id, and determine at a lower level the space
	id where to store the table */
4758

unknown's avatar
unknown committed
4759
	index = dict_mem_index_create((char*) table_name,
unknown's avatar
unknown committed
4760
		(char*) "GEN_CLUST_INDEX", 0, DICT_CLUSTERED, 0);
4761
	error = row_create_index_for_mysql(index, trx, NULL);
4762

unknown's avatar
unknown committed
4763
	error = convert_error_code_to_mysql(error, NULL);
4764

4765
	return(error);
4766 4767 4768
}

/*********************************************************************
4769
Creates a new table to an InnoDB database. */
4770 4771 4772 4773 4774 4775 4776 4777

int
ha_innobase::create(
/*================*/
					/* out: error number */
	const char*	name,		/* in: table name */
	TABLE*		form,		/* in: information on table
					columns and indexes */
4778 4779 4780
	HA_CREATE_INFO*	create_info)	/* in: more information of the
					created table, contains also the
					create statement string */
4781 4782 4783
{
	int		error;
	dict_table_t*	innobase_table;
unknown's avatar
unknown committed
4784
	trx_t*		parent_trx;
4785
	trx_t*		trx;
unknown's avatar
unknown committed
4786
	int		primary_key_no;
4787
	uint		i;
unknown's avatar
unknown committed
4788 4789
	char		name2[FN_REFLEN];
	char		norm_name[FN_REFLEN];
unknown's avatar
unknown committed
4790
	THD		*thd= current_thd;
unknown's avatar
unknown committed
4791 4792
	ib_longlong	auto_inc_value;
	ulint		flags;
4793

unknown's avatar
unknown committed
4794
	DBUG_ENTER("ha_innobase::create");
4795

unknown's avatar
unknown committed
4796
	DBUG_ASSERT(thd != NULL);
unknown's avatar
unknown committed
4797

4798
	if (form->s->fields > 1000) {
unknown's avatar
unknown committed
4799 4800 4801
		/* The limit probably should be REC_MAX_N_FIELDS - 3 = 1020,
		but we play safe here */

unknown's avatar
unknown committed
4802 4803
		DBUG_RETURN(HA_ERR_TO_BIG_ROW);
	}
unknown's avatar
unknown committed
4804

unknown's avatar
unknown committed
4805 4806
	/* Get the transaction associated with the current thd, or create one
	if not yet created */
unknown's avatar
unknown committed
4807

unknown's avatar
unknown committed
4808
	parent_trx = check_trx_exists(thd);
unknown's avatar
unknown committed
4809 4810 4811 4812

	/* In case MySQL calls this in the middle of a SELECT query, release
	possible adaptive hash latch to avoid deadlocks of threads */

unknown's avatar
unknown committed
4813 4814
	trx_search_latch_release_if_reserved(parent_trx);

4815
	trx = trx_allocate_for_mysql();
unknown's avatar
unknown committed
4816

unknown's avatar
unknown committed
4817 4818
	trx->mysql_thd = thd;
	trx->mysql_query_str = &((*thd).query);
4819

unknown's avatar
unknown committed
4820
	if (thd->options & OPTION_NO_FOREIGN_KEY_CHECKS) {
unknown's avatar
unknown committed
4821 4822 4823
		trx->check_foreigns = FALSE;
	}

unknown's avatar
unknown committed
4824
	if (thd->options & OPTION_RELAXED_UNIQUE_CHECKS) {
unknown's avatar
unknown committed
4825 4826 4827
		trx->check_unique_secondary = FALSE;
	}

unknown's avatar
unknown committed
4828 4829 4830 4831 4832
	if (lower_case_table_names) {
		srv_lower_case_table_names = TRUE;
	} else {
		srv_lower_case_table_names = FALSE;
	}
unknown's avatar
unknown committed
4833

4834
	strcpy(name2, name);
4835 4836

	normalize_table_name(norm_name, name2);
4837

unknown's avatar
unknown committed
4838
	/* Latch the InnoDB data dictionary exclusively so that no deadlocks
unknown's avatar
unknown committed
4839
	or lock waits can happen in it during a table create operation.
unknown's avatar
unknown committed
4840
	Drop table etc. do this latching in row0mysql.c. */
unknown's avatar
unknown committed
4841

unknown's avatar
unknown committed
4842
	row_mysql_lock_data_dictionary(trx);
unknown's avatar
unknown committed
4843 4844

	/* Create the table definition in InnoDB */
4845

unknown's avatar
unknown committed
4846 4847 4848 4849 4850 4851
	flags = 0;

	if (form->s->row_type != ROW_TYPE_REDUNDANT) {
		flags |= DICT_TF_COMPACT;
	}

unknown's avatar
unknown committed
4852 4853
	error = create_table_def(trx, form, norm_name,
		create_info->options & HA_LEX_CREATE_TMP_TABLE ? name2 : NULL,
unknown's avatar
unknown committed
4854
		flags);
unknown's avatar
unknown committed
4855

unknown's avatar
unknown committed
4856
	if (error) {
4857
		goto cleanup;
unknown's avatar
unknown committed
4858
	}
4859

4860 4861
	/* Look for a primary key */

unknown's avatar
unknown committed
4862
	primary_key_no= (form->s->primary_key != MAX_KEY ?
unknown's avatar
unknown committed
4863
			 (int) form->s->primary_key :
unknown's avatar
unknown committed
4864
			 -1);
4865

4866 4867 4868
	/* Our function row_get_mysql_key_number_for_index assumes
	the primary key is always number 0, if it exists */

unknown's avatar
unknown committed
4869
	DBUG_ASSERT(primary_key_no == -1 || primary_key_no == 0);
4870

4871 4872
	/* Create the keys */

4873
	if (form->s->keys == 0 || primary_key_no == -1) {
4874 4875
		/* Create an index which is used as the clustered index;
		order the rows by their row id which is internally generated
4876
		by InnoDB */
4877

4878
		error = create_clustered_index_when_no_primary(trx,
4879
							norm_name);
unknown's avatar
unknown committed
4880
		if (error) {
4881
			goto cleanup;
unknown's avatar
unknown committed
4882
		}
4883 4884 4885
	}

	if (primary_key_no != -1) {
4886
		/* In InnoDB the clustered index must always be created
4887
		first */
unknown's avatar
unknown committed
4888
		if ((error = create_index(trx, form, norm_name,
unknown's avatar
unknown committed
4889
					  (uint) primary_key_no))) {
4890
			goto cleanup;
unknown's avatar
unknown committed
4891 4892
		}
	}
4893

4894
	for (i = 0; i < form->s->keys; i++) {
4895 4896 4897

		if (i != (uint) primary_key_no) {

unknown's avatar
unknown committed
4898
			if ((error = create_index(trx, form, norm_name, i))) {
4899
				goto cleanup;
unknown's avatar
unknown committed
4900 4901 4902
			}
		}
	}
4903

unknown's avatar
unknown committed
4904
	if (thd->query != NULL) {
4905
		error = row_table_add_foreign_constraints(trx,
unknown's avatar
unknown committed
4906
			thd->query, norm_name,
4907
			create_info->options & HA_LEX_CREATE_TMP_TABLE);
4908

4909
		error = convert_error_code_to_mysql(error, NULL);
4910

4911 4912
		if (error) {
			goto cleanup;
4913
		}
4914 4915
	}

unknown's avatar
unknown committed
4916
	innobase_commit_low(trx);
unknown's avatar
unknown committed
4917

unknown's avatar
unknown committed
4918
	row_mysql_unlock_data_dictionary(trx);
4919

unknown's avatar
Merge  
unknown committed
4920 4921 4922
	/* Flush the log to reduce probability that the .frm files and
	the InnoDB data dictionary get out-of-sync if the user runs
	with innodb_flush_log_at_trx_commit = 0 */
unknown's avatar
unknown committed
4923

unknown's avatar
unknown committed
4924
	log_buffer_flush_to_disk();
unknown's avatar
Merge  
unknown committed
4925

unknown's avatar
unknown committed
4926
	innobase_table = dict_table_get(norm_name);
4927

unknown's avatar
unknown committed
4928
	DBUG_ASSERT(innobase_table != 0);
4929

4930 4931
	if ((create_info->used_fields & HA_CREATE_USED_AUTO) &&
	   (create_info->auto_increment_value != 0)) {
4932

unknown's avatar
unknown committed
4933
		/* Query was ALTER TABLE...AUTO_INCREMENT = x; or
4934
		CREATE TABLE ...AUTO_INCREMENT = x; Find out a table
4935 4936
		definition from the dictionary and get the current value
		of the auto increment field. Set a new value to the
4937 4938
		auto increment field if the value is greater than the
		maximum value in the column. */
4939

4940
		auto_inc_value = create_info->auto_increment_value;
4941 4942 4943
		dict_table_autoinc_initialize(innobase_table, auto_inc_value);
	}

4944
	/* Tell the InnoDB server that there might be work for
4945 4946 4947 4948
	utility threads: */

	srv_active_wake_master_thread();

unknown's avatar
unknown committed
4949
	trx_free_for_mysql(trx);
4950 4951

	DBUG_RETURN(0);
4952 4953 4954

cleanup:
	innobase_commit_low(trx);
unknown's avatar
unknown committed
4955

4956
	row_mysql_unlock_data_dictionary(trx);
unknown's avatar
unknown committed
4957

4958 4959 4960
	trx_free_for_mysql(trx);

	DBUG_RETURN(error);
4961 4962
}

unknown's avatar
unknown committed
4963 4964 4965 4966 4967 4968 4969 4970 4971 4972
/*********************************************************************
Discards or imports an InnoDB tablespace. */

int
ha_innobase::discard_or_import_tablespace(
/*======================================*/
				/* out: 0 == success, -1 == error */
	my_bool discard)	/* in: TRUE if discard, else import */
{
	row_prebuilt_t* prebuilt	= (row_prebuilt_t*) innobase_prebuilt;
4973
	dict_table_t*	dict_table;
unknown's avatar
unknown committed
4974 4975 4976
	trx_t*		trx;
	int		err;

unknown's avatar
unknown committed
4977
	DBUG_ENTER("ha_innobase::discard_or_import_tablespace");
unknown's avatar
unknown committed
4978 4979 4980

	ut_a(prebuilt->trx && prebuilt->trx->magic_n == TRX_MAGIC_N);
	ut_a(prebuilt->trx ==
unknown's avatar
unknown committed
4981
		(trx_t*) current_thd->ha_data[innobase_hton.slot]);
unknown's avatar
unknown committed
4982

4983
	dict_table = prebuilt->table;
unknown's avatar
unknown committed
4984 4985 4986
	trx = prebuilt->trx;

	if (discard) {
4987
		err = row_discard_tablespace_for_mysql(dict_table->name, trx);
unknown's avatar
unknown committed
4988
	} else {
4989
		err = row_import_tablespace_for_mysql(dict_table->name, trx);
unknown's avatar
unknown committed
4990 4991
	}

unknown's avatar
unknown committed
4992
	err = convert_error_code_to_mysql(err, NULL);
unknown's avatar
unknown committed
4993

unknown's avatar
unknown committed
4994
	DBUG_RETURN(err);
unknown's avatar
unknown committed
4995 4996
}

4997 4998 4999 5000 5001 5002 5003 5004 5005 5006 5007 5008 5009 5010 5011 5012 5013 5014 5015 5016 5017 5018 5019 5020 5021 5022 5023 5024 5025 5026 5027 5028 5029 5030 5031 5032 5033 5034 5035 5036
/*********************************************************************
Deletes all rows of an InnoDB table. */

int
ha_innobase::delete_all_rows(void)
/*==============================*/
				/* out: error number */
{
	row_prebuilt_t*	prebuilt	= (row_prebuilt_t*)innobase_prebuilt;
	int		error;
	trx_t*		trx;
	THD*		thd		= current_thd;

	DBUG_ENTER("ha_innobase::delete_all_rows");

	if (thd->lex->sql_command != SQLCOM_TRUNCATE) {
	fallback:
		/* We only handle TRUNCATE TABLE t as a special case.
		DELETE FROM t will have to use ha_innobase::delete_row(). */
		DBUG_RETURN(my_errno=HA_ERR_WRONG_COMMAND);
	}

	/* Get the transaction associated with the current thd, or create one
	if not yet created */

	trx = check_trx_exists(thd);

	/* Truncate the table in InnoDB */

	error = row_truncate_table_for_mysql(prebuilt->table, trx);
	if (error == DB_ERROR) {
		/* Cannot truncate; resort to ha_innobase::delete_row() */
		goto fallback;
	}

	error = convert_error_code_to_mysql(error, NULL);

	DBUG_RETURN(error);
}

5037
/*********************************************************************
5038
Drops a table from an InnoDB database. Before calling this function,
unknown's avatar
unknown committed
5039 5040
MySQL calls innobase_commit to commit the transaction of the current user.
Then the current user cannot have locks set on the table. Drop table
5041 5042
operation inside InnoDB will remove all locks any user has on the table
inside InnoDB. */
5043 5044 5045 5046

int
ha_innobase::delete_table(
/*======================*/
unknown's avatar
unknown committed
5047 5048
				/* out: error number */
	const char*	name)	/* in: table name */
5049 5050 5051
{
	ulint	name_len;
	int	error;
unknown's avatar
unknown committed
5052
	trx_t*	parent_trx;
5053
	trx_t*	trx;
unknown's avatar
unknown committed
5054
	THD	*thd= current_thd;
5055
	char	norm_name[1000];
unknown's avatar
unknown committed
5056

unknown's avatar
unknown committed
5057
	DBUG_ENTER("ha_innobase::delete_table");
5058

unknown's avatar
unknown committed
5059 5060
	/* Get the transaction associated with the current thd, or create one
	if not yet created */
unknown's avatar
unknown committed
5061

unknown's avatar
unknown committed
5062
	parent_trx = check_trx_exists(thd);
unknown's avatar
unknown committed
5063 5064 5065 5066

	/* In case MySQL calls this in the middle of a SELECT query, release
	possible adaptive hash latch to avoid deadlocks of threads */

unknown's avatar
unknown committed
5067
	trx_search_latch_release_if_reserved(parent_trx);
unknown's avatar
unknown committed
5068

unknown's avatar
unknown committed
5069 5070 5071 5072 5073 5074
	if (lower_case_table_names) {
		srv_lower_case_table_names = TRUE;
	} else {
		srv_lower_case_table_names = FALSE;
	}

5075 5076
	trx = trx_allocate_for_mysql();

unknown's avatar
unknown committed
5077 5078
	trx->mysql_thd = current_thd;
	trx->mysql_query_str = &((*current_thd).query);
unknown's avatar
unknown committed
5079

unknown's avatar
unknown committed
5080 5081 5082 5083 5084 5085 5086 5087
	if (thd->options & OPTION_NO_FOREIGN_KEY_CHECKS) {
		trx->check_foreigns = FALSE;
	}

	if (thd->options & OPTION_RELAXED_UNIQUE_CHECKS) {
		trx->check_unique_secondary = FALSE;
	}

5088 5089 5090
	name_len = strlen(name);

	assert(name_len < 1000);
5091

5092 5093
	/* Strangely, MySQL passes the table name without the '.frm'
	extension, in contrast to ::create */
5094

5095 5096
	normalize_table_name(norm_name, name);

unknown's avatar
unknown committed
5097
	/* Drop the table in InnoDB */
5098

5099
	error = row_drop_table_for_mysql(norm_name, trx,
unknown's avatar
unknown committed
5100
		thd->lex->sql_command == SQLCOM_DROP_DB);
5101

unknown's avatar
Merge  
unknown committed
5102 5103 5104
	/* Flush the log to reduce probability that the .frm files and
	the InnoDB data dictionary get out-of-sync if the user runs
	with innodb_flush_log_at_trx_commit = 0 */
unknown's avatar
unknown committed
5105

unknown's avatar
unknown committed
5106
	log_buffer_flush_to_disk();
unknown's avatar
Merge  
unknown committed
5107

5108
	/* Tell the InnoDB server that there might be work for
5109 5110 5111 5112
	utility threads: */

	srv_active_wake_master_thread();

unknown's avatar
unknown committed
5113
	innobase_commit_low(trx);
unknown's avatar
updated  
unknown committed
5114

unknown's avatar
unknown committed
5115
	trx_free_for_mysql(trx);
5116

unknown's avatar
unknown committed
5117
	error = convert_error_code_to_mysql(error, NULL);
5118 5119 5120 5121

	DBUG_RETURN(error);
}

5122 5123 5124
/*********************************************************************
Removes all tables in the named database inside InnoDB. */

5125
void
5126 5127 5128 5129 5130 5131 5132 5133 5134
innobase_drop_database(
/*===================*/
			/* out: error number */
	char*	path)	/* in: database path; inside InnoDB the name
			of the last directory in the path is used as
			the database name: for example, in 'mysql/data/test'
			the database name is 'test' */
{
	ulint	len		= 0;
unknown's avatar
unknown committed
5135
	trx_t*	parent_trx;
5136 5137 5138
	trx_t*	trx;
	char*	ptr;
	int	error;
5139
	char*	namebuf;
unknown's avatar
unknown committed
5140

unknown's avatar
unknown committed
5141 5142
	/* Get the transaction associated with the current thd, or create one
	if not yet created */
unknown's avatar
unknown committed
5143

unknown's avatar
unknown committed
5144
	parent_trx = check_trx_exists(current_thd);
unknown's avatar
unknown committed
5145 5146 5147 5148

	/* In case MySQL calls this in the middle of a SELECT query, release
	possible adaptive hash latch to avoid deadlocks of threads */

unknown's avatar
unknown committed
5149
	trx_search_latch_release_if_reserved(parent_trx);
unknown's avatar
unknown committed
5150

5151
	ptr = strend(path) - 2;
unknown's avatar
unknown committed
5152

5153 5154 5155 5156 5157 5158
	while (ptr >= path && *ptr != '\\' && *ptr != '/') {
		ptr--;
		len++;
	}

	ptr++;
unknown's avatar
unknown committed
5159
	namebuf = my_malloc((uint) len + 2, MYF(0));
5160 5161 5162 5163

	memcpy(namebuf, ptr, len);
	namebuf[len] = '/';
	namebuf[len + 1] = '\0';
unknown's avatar
unknown committed
5164
#ifdef	__WIN__
5165
	innobase_casedn_str(namebuf);
unknown's avatar
unknown committed
5166
#endif
5167
	trx = trx_allocate_for_mysql();
unknown's avatar
unknown committed
5168 5169
	trx->mysql_thd = current_thd;
	trx->mysql_query_str = &((*current_thd).query);
5170

unknown's avatar
unknown committed
5171 5172 5173 5174
	if (current_thd->options & OPTION_NO_FOREIGN_KEY_CHECKS) {
		trx->check_foreigns = FALSE;
	}

unknown's avatar
unknown committed
5175
	error = row_drop_database_for_mysql(namebuf, trx);
5176
	my_free(namebuf, MYF(0));
5177

unknown's avatar
Merge  
unknown committed
5178 5179 5180
	/* Flush the log to reduce probability that the .frm files and
	the InnoDB data dictionary get out-of-sync if the user runs
	with innodb_flush_log_at_trx_commit = 0 */
unknown's avatar
unknown committed
5181

unknown's avatar
unknown committed
5182
	log_buffer_flush_to_disk();
unknown's avatar
Merge  
unknown committed
5183

5184 5185 5186 5187 5188
	/* Tell the InnoDB server that there might be work for
	utility threads: */

	srv_active_wake_master_thread();

unknown's avatar
unknown committed
5189 5190 5191
	innobase_commit_low(trx);
	trx_free_for_mysql(trx);
#ifdef NO_LONGER_INTERESTED_IN_DROP_DB_ERROR
unknown's avatar
unknown committed
5192
	error = convert_error_code_to_mysql(error, NULL);
5193 5194

	return(error);
5195 5196 5197
#else
	return;
#endif
5198 5199
}

5200
/*************************************************************************
5201
Renames an InnoDB table. */
5202 5203 5204 5205 5206 5207 5208 5209 5210 5211 5212

int
ha_innobase::rename_table(
/*======================*/
				/* out: 0 or error code */
	const char*	from,	/* in: old name of the table */
	const char*	to)	/* in: new name of the table */
{
	ulint	name_len1;
	ulint	name_len2;
	int	error;
unknown's avatar
unknown committed
5213
	trx_t*	parent_trx;
5214
	trx_t*	trx;
5215 5216
	char	norm_from[1000];
	char	norm_to[1000];
5217

unknown's avatar
unknown committed
5218
	DBUG_ENTER("ha_innobase::rename_table");
5219

unknown's avatar
unknown committed
5220 5221
	/* Get the transaction associated with the current thd, or create one
	if not yet created */
unknown's avatar
unknown committed
5222

unknown's avatar
unknown committed
5223
	parent_trx = check_trx_exists(current_thd);
unknown's avatar
unknown committed
5224 5225 5226 5227

	/* In case MySQL calls this in the middle of a SELECT query, release
	possible adaptive hash latch to avoid deadlocks of threads */

unknown's avatar
unknown committed
5228
	trx_search_latch_release_if_reserved(parent_trx);
unknown's avatar
unknown committed
5229

unknown's avatar
unknown committed
5230 5231 5232 5233 5234 5235
	if (lower_case_table_names) {
		srv_lower_case_table_names = TRUE;
	} else {
		srv_lower_case_table_names = FALSE;
	}

5236
	trx = trx_allocate_for_mysql();
unknown's avatar
unknown committed
5237 5238
	trx->mysql_thd = current_thd;
	trx->mysql_query_str = &((*current_thd).query);
5239

5240 5241 5242 5243
	if (current_thd->options & OPTION_NO_FOREIGN_KEY_CHECKS) {
		trx->check_foreigns = FALSE;
	}

5244 5245 5246 5247 5248
	name_len1 = strlen(from);
	name_len2 = strlen(to);

	assert(name_len1 < 1000);
	assert(name_len2 < 1000);
5249

5250 5251 5252
	normalize_table_name(norm_from, from);
	normalize_table_name(norm_to, to);

unknown's avatar
unknown committed
5253
	/* Rename the table in InnoDB */
5254

unknown's avatar
unknown committed
5255
	error = row_rename_table_for_mysql(norm_from, norm_to, trx);
5256

unknown's avatar
Merge  
unknown committed
5257 5258 5259
	/* Flush the log to reduce probability that the .frm files and
	the InnoDB data dictionary get out-of-sync if the user runs
	with innodb_flush_log_at_trx_commit = 0 */
unknown's avatar
unknown committed
5260

unknown's avatar
unknown committed
5261
	log_buffer_flush_to_disk();
unknown's avatar
Merge  
unknown committed
5262

5263
	/* Tell the InnoDB server that there might be work for
5264 5265 5266 5267
	utility threads: */

	srv_active_wake_master_thread();

unknown's avatar
unknown committed
5268 5269
	innobase_commit_low(trx);
	trx_free_for_mysql(trx);
5270

unknown's avatar
unknown committed
5271
	error = convert_error_code_to_mysql(error, NULL);
5272 5273 5274 5275 5276 5277 5278 5279 5280 5281

	DBUG_RETURN(error);
}

/*************************************************************************
Estimates the number of index records in a range. */

ha_rows
ha_innobase::records_in_range(
/*==========================*/
unknown's avatar
unknown committed
5282 5283
						/* out: estimated number of
						rows */
unknown's avatar
unknown committed
5284 5285 5286
	uint			keynr,		/* in: index number */
	key_range		*min_key,	/* in: start key value of the
						   range, may also be 0 */
unknown's avatar
unknown committed
5287
	key_range		*max_key)	/* in: range end key val, may
unknown's avatar
unknown committed
5288
						   also be 0 */
5289 5290 5291 5292
{
	row_prebuilt_t* prebuilt	= (row_prebuilt_t*) innobase_prebuilt;
	KEY*		key;
	dict_index_t*	index;
unknown's avatar
unknown committed
5293
	mysql_byte*	key_val_buff2	= (mysql_byte*) my_malloc(
5294
						  table->s->reclength
unknown's avatar
unknown committed
5295
					+ table->s->max_key_length + 100,
unknown's avatar
unknown committed
5296
								MYF(MY_FAE));
5297
	ulint		buff2_len = table->s->reclength
unknown's avatar
unknown committed
5298
					+ table->s->max_key_length + 100;
5299
	dtuple_t*	range_start;
5300
	dtuple_t*	range_end;
unknown's avatar
unknown committed
5301
	ib_longlong	n_rows;
5302 5303
	ulint		mode1;
	ulint		mode2;
unknown's avatar
unknown committed
5304 5305
	void*		heap1;
	void*		heap2;
5306

unknown's avatar
unknown committed
5307
	DBUG_ENTER("records_in_range");
5308

unknown's avatar
unknown committed
5309 5310
	prebuilt->trx->op_info = (char*)"estimating records in index range";

unknown's avatar
unknown committed
5311 5312 5313 5314
	/* In case MySQL calls this in the middle of a SELECT query, release
	possible adaptive hash latch to avoid deadlocks of threads */

	trx_search_latch_release_if_reserved(prebuilt->trx);
unknown's avatar
unknown committed
5315

5316 5317 5318
	active_index = keynr;

	key = table->key_info + active_index;
5319

5320
	index = dict_table_get_index_noninline(prebuilt->table, key->name);
5321

5322
	range_start = dtuple_create_for_mysql(&heap1, key->key_parts);
unknown's avatar
unknown committed
5323
	dict_index_copy_types(range_start, index, key->key_parts);
5324

5325
	range_end = dtuple_create_for_mysql(&heap2, key->key_parts);
unknown's avatar
unknown committed
5326
	dict_index_copy_types(range_end, index, key->key_parts);
5327

5328
	row_sel_convert_mysql_key_to_innobase(
unknown's avatar
unknown committed
5329 5330 5331
				range_start, (byte*) key_val_buff,
				(ulint)upd_and_key_val_buff_len,
				index,
unknown's avatar
unknown committed
5332
				(byte*) (min_key ? min_key->key :
unknown's avatar
unknown committed
5333
					 (const mysql_byte*) 0),
5334 5335
				(ulint) (min_key ? min_key->length : 0),
				prebuilt->trx);
5336

5337
	row_sel_convert_mysql_key_to_innobase(
unknown's avatar
unknown committed
5338 5339
				range_end, (byte*) key_val_buff2,
				buff2_len, index,
unknown's avatar
unknown committed
5340
				(byte*) (max_key ? max_key->key :
unknown's avatar
unknown committed
5341
					 (const mysql_byte*) 0),
5342 5343
				(ulint) (max_key ? max_key->length : 0),
				prebuilt->trx);
unknown's avatar
unknown committed
5344 5345

	mode1 = convert_search_mode_to_innobase(min_key ? min_key->flag :
unknown's avatar
unknown committed
5346
						HA_READ_KEY_EXACT);
unknown's avatar
unknown committed
5347
	mode2 = convert_search_mode_to_innobase(max_key ? max_key->flag :
unknown's avatar
unknown committed
5348
						HA_READ_KEY_EXACT);
5349

5350
	n_rows = btr_estimate_n_rows_in_range(index, range_start,
5351
						mode1, range_end, mode2);
5352 5353
	dtuple_free_for_mysql(heap1);
	dtuple_free_for_mysql(heap2);
5354

unknown's avatar
unknown committed
5355
	my_free((gptr) key_val_buff2, MYF(0));
5356

unknown's avatar
unknown committed
5357 5358
	prebuilt->trx->op_info = (char*)"";

unknown's avatar
unknown committed
5359 5360 5361 5362 5363 5364 5365
	/* The MySQL optimizer seems to believe an estimate of 0 rows is
	always accurate and may return the result 'Empty set' based on that.
	The accuracy is not guaranteed, and even if it were, for a locking
	read we should anyway perform the search to set the next-key lock.
	Add 1 to the value to make sure MySQL does not make the assumption! */

	if (n_rows == 0) {
unknown's avatar
unknown committed
5366
		n_rows = 1;
unknown's avatar
unknown committed
5367 5368
	}

5369 5370 5371
	DBUG_RETURN((ha_rows) n_rows);
}

5372 5373
/*************************************************************************
Gives an UPPER BOUND to the number of rows in a table. This is used in
unknown's avatar
unknown committed
5374
filesort.cc. */
5375 5376

ha_rows
unknown's avatar
unknown committed
5377
ha_innobase::estimate_rows_upper_bound(void)
5378
/*======================================*/
5379
			/* out: upper bound of rows */
5380 5381
{
	row_prebuilt_t* prebuilt	= (row_prebuilt_t*) innobase_prebuilt;
5382 5383
	dict_index_t*	index;
	ulonglong	estimate;
5384
	ulonglong	local_data_file_length;
unknown's avatar
unknown committed
5385

unknown's avatar
unknown committed
5386
	DBUG_ENTER("estimate_rows_upper_bound");
5387

unknown's avatar
unknown committed
5388 5389 5390 5391 5392 5393
	/* We do not know if MySQL can call this function before calling
	external_lock(). To be safe, update the thd of the current table
	handle. */

	update_thd(current_thd);

unknown's avatar
unknown committed
5394
	prebuilt->trx->op_info = (char*)
unknown's avatar
unknown committed
5395
				 "calculating upper bound for table rows";
unknown's avatar
unknown committed
5396

unknown's avatar
unknown committed
5397 5398 5399 5400
	/* In case MySQL calls this in the middle of a SELECT query, release
	possible adaptive hash latch to avoid deadlocks of threads */

	trx_search_latch_release_if_reserved(prebuilt->trx);
5401

5402
	index = dict_table_get_first_index_noninline(prebuilt->table);
unknown's avatar
unknown committed
5403

5404
	local_data_file_length = ((ulonglong) index->stat_n_leaf_pages)
unknown's avatar
unknown committed
5405
							* UNIV_PAGE_SIZE;
5406

unknown's avatar
unknown committed
5407 5408
	/* Calculate a minimum length for a clustered index record and from
	that an upper bound for the number of rows. Since we only calculate
unknown's avatar
unknown committed
5409 5410
	new statistics in row0mysql.c when a table has grown by a threshold
	factor, we must add a safety factor 2 in front of the formula below. */
unknown's avatar
unknown committed
5411

unknown's avatar
unknown committed
5412 5413
	estimate = 2 * local_data_file_length /
					 dict_index_calc_min_rec_len(index);
unknown's avatar
unknown committed
5414

unknown's avatar
unknown committed
5415 5416
	prebuilt->trx->op_info = (char*)"";

unknown's avatar
unknown committed
5417
	DBUG_RETURN((ha_rows) estimate);
5418 5419
}

5420 5421 5422 5423 5424 5425 5426 5427 5428 5429 5430 5431
/*************************************************************************
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_innobase::scan_time()
/*====================*/
			/* out: estimated time measured in disk seeks */
{
	row_prebuilt_t* prebuilt	= (row_prebuilt_t*) innobase_prebuilt;

unknown's avatar
unknown committed
5432 5433 5434 5435
	/* Since MySQL seems to favor table scans too much over index
	searches, we pretend that a sequential read takes the same time
	as a random disk read, that is, we do not divide the following
	by 10, which would be physically realistic. */
unknown's avatar
unknown committed
5436

unknown's avatar
unknown committed
5437
	return((double) (prebuilt->table->stat_clustered_index_size));
5438 5439
}

unknown's avatar
unknown committed
5440 5441 5442
/**********************************************************************
Calculate the time it takes to read a set of ranges through an index
This enables us to optimise reads for clustered indexes. */
unknown's avatar
unknown committed
5443

unknown's avatar
unknown committed
5444 5445 5446 5447
double
ha_innobase::read_time(
/*===================*/
			/* out: estimated time measured in disk seeks */
unknown's avatar
unknown committed
5448
	uint	index,	/* in: key number */
unknown's avatar
unknown committed
5449 5450
	uint	ranges,	/* in: how many ranges */
	ha_rows rows)	/* in: estimated number of rows in the ranges */
unknown's avatar
unknown committed
5451
{
unknown's avatar
unknown committed
5452
	ha_rows total_rows;
unknown's avatar
unknown committed
5453 5454
	double	time_for_scan;

unknown's avatar
unknown committed
5455
	if (index != table->s->primary_key) {
unknown's avatar
unknown committed
5456 5457
		/* Not clustered */
		return(handler::read_time(index, ranges, rows));
unknown's avatar
unknown committed
5458
	}
unknown's avatar
unknown committed
5459

unknown's avatar
unknown committed
5460
	if (rows <= 2) {
unknown's avatar
unknown committed
5461

unknown's avatar
unknown committed
5462 5463
		return((double) rows);
	}
unknown's avatar
unknown committed
5464 5465 5466 5467

	/* Assume that the read time is proportional to the scan time for all
	rows + at most one seek per range. */

unknown's avatar
unknown committed
5468
	time_for_scan = scan_time();
unknown's avatar
unknown committed
5469

unknown's avatar
unknown committed
5470
	if ((total_rows = estimate_rows_upper_bound()) < rows) {
unknown's avatar
unknown committed
5471

unknown's avatar
unknown committed
5472
		return(time_for_scan);
unknown's avatar
unknown committed
5473
	}
unknown's avatar
unknown committed
5474

unknown's avatar
unknown committed
5475
	return(ranges + (double) rows / (double) total_rows * time_for_scan);
unknown's avatar
unknown committed
5476 5477
}

5478 5479 5480 5481 5482 5483 5484 5485 5486 5487 5488 5489
/*************************************************************************
Returns statistics information of the table to the MySQL interpreter,
in various fields of the handle object. */

void
ha_innobase::info(
/*==============*/
	uint flag)	/* in: what information MySQL requests */
{
	row_prebuilt_t* prebuilt	= (row_prebuilt_t*) innobase_prebuilt;
	dict_table_t*	ib_table;
	dict_index_t*	index;
5490
	ha_rows		rec_per_key;
unknown's avatar
unknown committed
5491
	ib_longlong	n_rows;
5492 5493
	ulong		j;
	ulong		i;
5494
	char		path[FN_REFLEN];
unknown's avatar
unknown committed
5495
	os_file_stat_t	stat_info;
5496

unknown's avatar
unknown committed
5497
	DBUG_ENTER("info");
5498

unknown's avatar
unknown committed
5499
	/* If we are forcing recovery at a high level, we will suppress
unknown's avatar
unknown committed
5500 5501 5502
	statistics calculation on tables, because that may crash the
	server if an index is badly corrupted. */

unknown's avatar
unknown committed
5503
	if (srv_force_recovery >= SRV_FORCE_NO_IBUF_MERGE) {
unknown's avatar
unknown committed
5504

unknown's avatar
unknown committed
5505 5506
		DBUG_VOID_RETURN;
	}
unknown's avatar
unknown committed
5507

unknown's avatar
unknown committed
5508 5509 5510 5511 5512 5513 5514 5515 5516
	/* We do not know if MySQL can call this function before calling
	external_lock(). To be safe, update the thd of the current table
	handle. */

	update_thd(current_thd);

	/* In case MySQL calls this in the middle of a SELECT query, release
	possible adaptive hash latch to avoid deadlocks of threads */

unknown's avatar
unknown committed
5517 5518
	prebuilt->trx->op_info = (char*)"returning various info to MySQL";

unknown's avatar
unknown committed
5519
	trx_search_latch_release_if_reserved(prebuilt->trx);
unknown's avatar
unknown committed
5520

unknown's avatar
unknown committed
5521
	ib_table = prebuilt->table;
5522

unknown's avatar
unknown committed
5523 5524 5525
	if (flag & HA_STATUS_TIME) {
		/* In sql_show we call with this flag: update then statistics
		so that they are up-to-date */
5526

unknown's avatar
unknown committed
5527
		prebuilt->trx->op_info = (char*)"updating table statistics";
unknown's avatar
unknown committed
5528

unknown's avatar
unknown committed
5529
		dict_update_statistics(ib_table);
unknown's avatar
unknown committed
5530 5531

		prebuilt->trx->op_info = (char*)
unknown's avatar
unknown committed
5532
					  "returning various info to MySQL";
5533 5534 5535

		if (ib_table->space != 0) {
			my_snprintf(path, sizeof(path), "%s/%s%s",
unknown's avatar
unknown committed
5536
				mysql_data_home, ib_table->name, ".ibd");
5537 5538
			unpack_filename(path,path);
		} else {
unknown's avatar
unknown committed
5539 5540 5541
			my_snprintf(path, sizeof(path), "%s/%s%s",
				mysql_data_home, ib_table->name, reg_ext);

5542 5543 5544
			unpack_filename(path,path);
		}

unknown's avatar
unknown committed
5545
		/* Note that we do not know the access time of the table,
5546 5547
		nor the CHECK TABLE time, nor the UPDATE or INSERT time. */

5548
		if (os_file_get_status(path,&stat_info)) {
5549
			stats.create_time = stat_info.ctime;
5550
		}
unknown's avatar
unknown committed
5551
	}
5552 5553

	if (flag & HA_STATUS_VARIABLE) {
unknown's avatar
unknown committed
5554 5555 5556 5557 5558 5559 5560 5561 5562 5563
		n_rows = ib_table->stat_n_rows;

		/* Because we do not protect stat_n_rows by any mutex in a
		delete, it is theoretically possible that the value can be
		smaller than zero! TODO: fix this race.

		The MySQL optimizer seems to assume in a left join that n_rows
		is an accurate estimate if it is zero. Of course, it is not,
		since we do not have any locks on the rows yet at this phase.
		Since SHOW TABLE STATUS seems to call this function with the
unknown's avatar
unknown committed
5564
		HA_STATUS_TIME flag set, while the left join optimizer does not
unknown's avatar
unknown committed
5565 5566 5567 5568 5569 5570 5571 5572 5573 5574 5575 5576
		set that flag, we add one to a zero value if the flag is not
		set. That way SHOW TABLE STATUS will show the best estimate,
		while the optimizer never sees the table empty. */

		if (n_rows < 0) {
			n_rows = 0;
		}

		if (n_rows == 0 && !(flag & HA_STATUS_TIME)) {
			n_rows++;
		}

5577 5578 5579
		stats.records = (ha_rows)n_rows;
		stats.deleted = 0;
		stats.data_file_length = ((ulonglong)
5580
				ib_table->stat_clustered_index_size)
unknown's avatar
unknown committed
5581
					* UNIV_PAGE_SIZE;
5582
		stats.index_file_length = ((ulonglong)
5583
				ib_table->stat_sum_of_other_index_sizes)
unknown's avatar
unknown committed
5584
					* UNIV_PAGE_SIZE;
5585 5586
		stats.delete_length = 0;
		stats.check_time = 0;
5587

5588 5589
		if (stats.records == 0) {
			stats.mean_rec_length = 0;
unknown's avatar
unknown committed
5590
		} else {
5591
			stats.mean_rec_length = (ulong) (stats.data_file_length / stats.records);
unknown's avatar
unknown committed
5592 5593
		}
	}
5594 5595 5596 5597 5598 5599 5600

	if (flag & HA_STATUS_CONST) {
		index = dict_table_get_first_index_noninline(ib_table);

		if (prebuilt->clust_index_was_generated) {
			index = dict_table_get_next_index_noninline(index);
		}
5601

5602
		for (i = 0; i < table->s->keys; i++) {
unknown's avatar
unknown committed
5603 5604
			if (index == NULL) {
				ut_print_timestamp(stderr);
unknown's avatar
unknown committed
5605
				sql_print_error("Table %s contains fewer "
5606 5607 5608 5609
						"indexes inside InnoDB than "
						"are defined in the MySQL "
						".frm file. Have you mixed up "
						".frm files from different "
unknown's avatar
unknown committed
5610 5611 5612
						"installations? See "
"http://dev.mysql.com/doc/refman/5.0/en/innodb-troubleshooting.html\n",

5613
						ib_table->name);
unknown's avatar
unknown committed
5614 5615 5616
				break;
			}

5617 5618
			for (j = 0; j < table->key_info[i].key_parts; j++) {

unknown's avatar
unknown committed
5619
				if (j + 1 > index->n_uniq) {
unknown's avatar
unknown committed
5620
					ut_print_timestamp(stderr);
unknown's avatar
unknown committed
5621 5622 5623 5624 5625
					sql_print_error(
"Index %s of %s has %lu columns unique inside InnoDB, but MySQL is asking "
"statistics for %lu columns. Have you mixed up .frm files from different "
"installations? "
"See http://dev.mysql.com/doc/refman/5.0/en/innodb-troubleshooting.html\n",
5626 5627 5628 5629
							index->name,
							ib_table->name,
							(unsigned long)
							index->n_uniq, j + 1);
unknown's avatar
unknown committed
5630
					break;
unknown's avatar
unknown committed
5631 5632
				}

5633 5634
				if (index->stat_n_diff_key_vals[j + 1] == 0) {

5635
					rec_per_key = stats.records;
5636
				} else {
5637
					rec_per_key = (ha_rows)(stats.records /
unknown's avatar
unknown committed
5638
					 index->stat_n_diff_key_vals[j + 1]);
5639 5640
				}

unknown's avatar
unknown committed
5641 5642 5643 5644 5645 5646 5647
				/* Since MySQL seems to favor table scans
				too much over index searches, we pretend
				index selectivity is 2 times better than
				our estimate: */

				rec_per_key = rec_per_key / 2;

5648 5649 5650
				if (rec_per_key == 0) {
					rec_per_key = 1;
				}
unknown's avatar
unknown committed
5651

unknown's avatar
unknown committed
5652
				table->key_info[i].rec_per_key[j]=
5653 5654
				  rec_per_key >= ~(ulong) 0 ? ~(ulong) 0 :
				  rec_per_key;
5655
			}
unknown's avatar
unknown committed
5656

5657
			index = dict_table_get_next_index_noninline(index);
5658 5659
		}
	}
5660

unknown's avatar
unknown committed
5661
	if (flag & HA_STATUS_ERRKEY) {
unknown's avatar
unknown committed
5662 5663
		ut_a(prebuilt->trx && prebuilt->trx->magic_n == TRX_MAGIC_N);

5664
		errkey = (unsigned int) row_get_mysql_key_number_for_index(
unknown's avatar
unknown committed
5665 5666
			(dict_index_t*) trx_get_error_info(prebuilt->trx));
	}
5667

unknown's avatar
unknown committed
5668 5669 5670 5671 5672 5673
	if (flag & HA_STATUS_AUTO && table->found_next_number_field) {
		longlong	auto_inc;
		int		ret;

		/* The following function call can the first time fail in
		a lock wait timeout error because it reserves the auto-inc
unknown's avatar
unknown committed
5674
		lock on the table. If it fails, then someone is already initing
unknown's avatar
unknown committed
5675 5676 5677
		the auto-inc counter, and the second call is guaranteed to
		succeed. */

unknown's avatar
unknown committed
5678
		ret = innobase_read_and_init_auto_inc(&auto_inc);
unknown's avatar
unknown committed
5679 5680 5681 5682 5683 5684 5685 5686 5687 5688 5689 5690

		if (ret != 0) {
			ret = innobase_read_and_init_auto_inc(&auto_inc);

			if (ret != 0) {
				ut_print_timestamp(stderr);
				sql_print_error("Cannot get table %s auto-inc"
						"counter value in ::info\n",
						ib_table->name);
				auto_inc = 0;
			}
		}
unknown's avatar
unknown committed
5691

5692
		stats.auto_increment_value = auto_inc;
unknown's avatar
unknown committed
5693 5694
	}

unknown's avatar
unknown committed
5695 5696
	prebuilt->trx->op_info = (char*)"";

unknown's avatar
unknown committed
5697
	DBUG_VOID_RETURN;
5698 5699
}

unknown's avatar
unknown committed
5700
/**************************************************************************
unknown's avatar
unknown committed
5701 5702
Updates index cardinalities of the table, based on 8 random dives into
each index tree. This does NOT calculate exact statistics on the table. */
unknown's avatar
unknown committed
5703 5704 5705

int
ha_innobase::analyze(
unknown's avatar
unknown committed
5706
/*=================*/
unknown's avatar
unknown committed
5707 5708 5709 5710 5711 5712 5713 5714 5715 5716
					/* out: returns always 0 (success) */
	THD*		thd,		/* in: connection thread handle */
	HA_CHECK_OPT*	check_opt)	/* in: currently ignored */
{
	/* Simply call ::info() with all the flags */
	info(HA_STATUS_TIME | HA_STATUS_CONST | HA_STATUS_VARIABLE);

	return(0);
}

unknown's avatar
unknown committed
5717
/**************************************************************************
5718
This is mapped to "ALTER TABLE tablename ENGINE=InnoDB", which rebuilds
5719
the table in MySQL. */
unknown's avatar
unknown committed
5720

unknown's avatar
unknown committed
5721 5722 5723 5724 5725
int
ha_innobase::optimize(
/*==================*/
	THD*		thd,		/* in: connection thread handle */
	HA_CHECK_OPT*	check_opt)	/* in: currently ignored */
unknown's avatar
unknown committed
5726
{
unknown's avatar
unknown committed
5727
	return(HA_ADMIN_TRY_ALTER);
unknown's avatar
unknown committed
5728 5729
}

unknown's avatar
unknown committed
5730 5731 5732 5733 5734 5735 5736 5737 5738 5739
/***********************************************************************
Tries to check that an InnoDB table is not corrupted. If corruption is
noticed, prints to stderr information about it. In case of corruption
may also assert a failure and crash the server. */

int
ha_innobase::check(
/*===============*/
					/* out: HA_ADMIN_CORRUPT or
					HA_ADMIN_OK */
unknown's avatar
unknown committed
5740 5741
	THD*		thd,		/* in: user thread handle */
	HA_CHECK_OPT*	check_opt)	/* in: check options, currently
unknown's avatar
unknown committed
5742 5743 5744 5745
					ignored */
{
	row_prebuilt_t* prebuilt	= (row_prebuilt_t*) innobase_prebuilt;
	ulint		ret;
unknown's avatar
unknown committed
5746

unknown's avatar
unknown committed
5747
	ut_a(prebuilt->trx && prebuilt->trx->magic_n == TRX_MAGIC_N);
unknown's avatar
unknown committed
5748
	ut_a(prebuilt->trx ==
unknown's avatar
unknown committed
5749
		(trx_t*) current_thd->ha_data[innobase_hton.slot]);
unknown's avatar
unknown committed
5750

unknown's avatar
unknown committed
5751 5752 5753 5754 5755 5756 5757 5758 5759 5760 5761 5762
	if (prebuilt->mysql_template == NULL) {
		/* Build the template; we will use a dummy template
		in index scans done in checking */

		build_template(prebuilt, NULL, table, ROW_MYSQL_WHOLE_ROW);
	}

	ret = row_check_table_for_mysql(prebuilt);

	if (ret == DB_SUCCESS) {
		return(HA_ADMIN_OK);
	}
unknown's avatar
unknown committed
5763

unknown's avatar
unknown committed
5764
	return(HA_ADMIN_CORRUPT);
unknown's avatar
unknown committed
5765 5766
}

5767
/*****************************************************************
unknown's avatar
Merge  
unknown committed
5768 5769 5770
Adds information about free space in the InnoDB tablespace to a table comment
which is printed out when a user calls SHOW TABLE STATUS. Adds also info on
foreign keys. */
5771 5772 5773 5774

char*
ha_innobase::update_table_comment(
/*==============================*/
unknown's avatar
Merge  
unknown committed
5775 5776
				/* out: table comment + InnoDB free space +
				info on foreign keys */
unknown's avatar
unknown committed
5777
	const char*	comment)/* in: table comment defined by user */
5778
{
unknown's avatar
unknown committed
5779
	uint	length			= (uint) strlen(comment);
5780 5781
	char*				str;
	row_prebuilt_t*	prebuilt	= (row_prebuilt_t*)innobase_prebuilt;
unknown's avatar
unknown committed
5782
	long	flen;
5783

unknown's avatar
unknown committed
5784 5785 5786 5787
	/* We do not know if MySQL can call this function before calling
	external_lock(). To be safe, update the thd of the current table
	handle. */

unknown's avatar
unknown committed
5788
	if (length > 64000 - 3) {
5789 5790 5791
		return((char*)comment); /* string too long */
	}

unknown's avatar
unknown committed
5792 5793
	update_thd(current_thd);

unknown's avatar
unknown committed
5794 5795
	prebuilt->trx->op_info = (char*)"returning table comment";

unknown's avatar
unknown committed
5796 5797 5798 5799
	/* In case MySQL calls this in the middle of a SELECT query, release
	possible adaptive hash latch to avoid deadlocks of threads */

	trx_search_latch_release_if_reserved(prebuilt->trx);
5800
	str = NULL;
unknown's avatar
unknown committed
5801

unknown's avatar
unknown committed
5802
	/* output the data to a temporary file */
unknown's avatar
Merge  
unknown committed
5803

unknown's avatar
unknown committed
5804 5805
	mutex_enter_noninline(&srv_dict_tmpfile_mutex);
	rewind(srv_dict_tmpfile);
5806

unknown's avatar
unknown committed
5807 5808 5809 5810 5811
	fprintf(srv_dict_tmpfile, "InnoDB free: %lu kB",
		   (ulong) fsp_get_available_space_in_free_extents(
					prebuilt->table->space));

	dict_print_info_on_foreign_keys(FALSE, srv_dict_tmpfile,
5812
				prebuilt->trx, prebuilt->table);
unknown's avatar
unknown committed
5813 5814 5815 5816 5817 5818
	flen = ftell(srv_dict_tmpfile);
	if (flen < 0) {
		flen = 0;
	} else if (length + flen + 3 > 64000) {
		flen = 64000 - 3 - length;
	}
5819

unknown's avatar
unknown committed
5820 5821
	/* allocate buffer for the full string, and
	read the contents of the temporary file */
5822

unknown's avatar
unknown committed
5823
	str = my_malloc(length + flen + 3, MYF(0));
5824

unknown's avatar
unknown committed
5825 5826 5827 5828 5829 5830
	if (str) {
		char* pos	= str + length;
		if (length) {
			memcpy(str, comment, length);
			*pos++ = ';';
			*pos++ = ' ';
5831
		}
unknown's avatar
unknown committed
5832 5833 5834
		rewind(srv_dict_tmpfile);
		flen = (uint) fread(pos, 1, flen, srv_dict_tmpfile);
		pos[flen] = 0;
unknown's avatar
unknown committed
5835
	}
unknown's avatar
unknown committed
5836

unknown's avatar
unknown committed
5837 5838 5839
	mutex_exit_noninline(&srv_dict_tmpfile_mutex);

	prebuilt->trx->op_info = (char*)"";
unknown's avatar
unknown committed
5840

unknown's avatar
unknown committed
5841
	return(str ? str : (char*) comment);
5842 5843
}

unknown's avatar
unknown committed
5844 5845 5846 5847 5848 5849 5850 5851 5852 5853 5854
/***********************************************************************
Gets the foreign key create info for a table stored in InnoDB. */

char*
ha_innobase::get_foreign_key_create_info(void)
/*==========================================*/
			/* out, own: character string in the form which
			can be inserted to the CREATE TABLE statement,
			MUST be freed with ::free_foreign_key_create_info */
{
	row_prebuilt_t* prebuilt = (row_prebuilt_t*)innobase_prebuilt;
5855
	char*	str	= 0;
unknown's avatar
unknown committed
5856
	long	flen;
unknown's avatar
unknown committed
5857

unknown's avatar
unknown committed
5858
	ut_a(prebuilt != NULL);
5859

unknown's avatar
unknown committed
5860 5861 5862 5863 5864 5865
	/* We do not know if MySQL can call this function before calling
	external_lock(). To be safe, update the thd of the current table
	handle. */

	update_thd(current_thd);

unknown's avatar
unknown committed
5866
	prebuilt->trx->op_info = (char*)"getting info on foreign keys";
unknown's avatar
unknown committed
5867

unknown's avatar
unknown committed
5868 5869 5870
	/* In case MySQL calls this in the middle of a SELECT query,
	release possible adaptive hash latch to avoid
	deadlocks of threads */
unknown's avatar
unknown committed
5871

unknown's avatar
unknown committed
5872
	trx_search_latch_release_if_reserved(prebuilt->trx);
5873

unknown's avatar
unknown committed
5874 5875
	mutex_enter_noninline(&srv_dict_tmpfile_mutex);
	rewind(srv_dict_tmpfile);
unknown's avatar
unknown committed
5876

unknown's avatar
unknown committed
5877 5878
	/* output the data to a temporary file */
	dict_print_info_on_foreign_keys(TRUE, srv_dict_tmpfile,
5879
				prebuilt->trx, prebuilt->table);
unknown's avatar
unknown committed
5880
	prebuilt->trx->op_info = (char*)"";
5881

unknown's avatar
unknown committed
5882 5883 5884 5885 5886 5887
	flen = ftell(srv_dict_tmpfile);
	if (flen < 0) {
		flen = 0;
	} else if (flen > 64000 - 1) {
		flen = 64000 - 1;
	}
5888

unknown's avatar
unknown committed
5889 5890
	/* allocate buffer for the string, and
	read the contents of the temporary file */
5891

unknown's avatar
unknown committed
5892
	str = my_malloc(flen + 1, MYF(0));
5893

unknown's avatar
unknown committed
5894 5895 5896 5897
	if (str) {
		rewind(srv_dict_tmpfile);
		flen = (uint) fread(str, 1, flen, srv_dict_tmpfile);
		str[flen] = 0;
5898
	}
unknown's avatar
unknown committed
5899

unknown's avatar
unknown committed
5900 5901 5902
	mutex_exit_noninline(&srv_dict_tmpfile_mutex);

	return(str);
unknown's avatar
unknown committed
5903
}
unknown's avatar
unknown committed
5904

5905

unknown's avatar
unknown committed
5906
int
5907 5908 5909 5910 5911 5912 5913 5914 5915 5916
ha_innobase::get_foreign_key_list(THD *thd, List<FOREIGN_KEY_INFO> *f_key_list)
{
  dict_foreign_t* foreign;

  DBUG_ENTER("get_foreign_key_list");
  row_prebuilt_t* prebuilt = (row_prebuilt_t*)innobase_prebuilt;
  ut_a(prebuilt != NULL);
  update_thd(current_thd);
  prebuilt->trx->op_info = (char*)"getting list of foreign keys";
  trx_search_latch_release_if_reserved(prebuilt->trx);
5917
  mutex_enter_noninline(&(dict_sys->mutex));
5918 5919
  foreign = UT_LIST_GET_FIRST(prebuilt->table->foreign_list);

unknown's avatar
unknown committed
5920 5921 5922 5923 5924 5925 5926 5927 5928 5929 5930 5931 5932 5933 5934 5935 5936 5937 5938 5939 5940 5941 5942 5943 5944 5945 5946 5947 5948 5949 5950 5951 5952 5953 5954 5955
  while (foreign != NULL) {
	  uint i;
	  FOREIGN_KEY_INFO f_key_info;
	  LEX_STRING *name= 0;
	  const char *tmp_buff;

	  tmp_buff= foreign->id;
	  i= 0;
	  while (tmp_buff[i] != '/')
		  i++;
	  tmp_buff+= i + 1;
	  f_key_info.forein_id= make_lex_string(thd, 0, tmp_buff,
		  (uint) strlen(tmp_buff), 1);
	  tmp_buff= foreign->referenced_table_name;
	  i= 0;
	  while (tmp_buff[i] != '/')
		  i++;
	  f_key_info.referenced_db= make_lex_string(thd, 0,
		  tmp_buff, i, 1);
	  tmp_buff+= i + 1;
	  f_key_info.referenced_table= make_lex_string(thd, 0, tmp_buff,
		  (uint) strlen(tmp_buff), 1);

	  for (i= 0;;) {
		  tmp_buff= foreign->foreign_col_names[i];
		  name= make_lex_string(thd, name, tmp_buff,
			  (uint) strlen(tmp_buff), 1);
		  f_key_info.foreign_fields.push_back(name);
		  tmp_buff= foreign->referenced_col_names[i];
		  name= make_lex_string(thd, name, tmp_buff,
			  (uint) strlen(tmp_buff), 1);
		  f_key_info.referenced_fields.push_back(name);
		  if (++i >= foreign->n_fields)
			  break;
	  }

unknown's avatar
unknown committed
5956 5957 5958 5959 5960 5961 5962 5963 5964 5965 5966 5967 5968 5969 5970 5971 5972 5973 5974 5975 5976 5977 5978 5979 5980 5981 5982 5983 5984 5985 5986 5987 5988 5989 5990 5991 5992 5993 5994 5995 5996 5997 5998 5999 6000 6001 6002 6003 6004
          ulong length;
          if (foreign->type & DICT_FOREIGN_ON_DELETE_CASCADE)
          {
            length=7;
            tmp_buff= "CASCADE";
          }	
          else if (foreign->type & DICT_FOREIGN_ON_DELETE_SET_NULL)
          {
            length=8;
            tmp_buff= "SET NULL";
          }
          else if (foreign->type & DICT_FOREIGN_ON_DELETE_NO_ACTION)
          {
            length=9;
            tmp_buff= "NO ACTION";
          }
          else
          {
            length=8;
            tmp_buff= "RESTRICT";
          }
          f_key_info.delete_method= make_lex_string(thd, f_key_info.delete_method,
                                                    tmp_buff, length, 1);
 
 
          if (foreign->type & DICT_FOREIGN_ON_UPDATE_CASCADE)
          {
            length=7;
            tmp_buff= "CASCADE";
          }
          else if (foreign->type & DICT_FOREIGN_ON_UPDATE_SET_NULL)
          {
            length=8;
            tmp_buff= "SET NULL";
          }
          else if (foreign->type & DICT_FOREIGN_ON_UPDATE_NO_ACTION)
          {
            length=9;
            tmp_buff= "NO ACTION";
          }
          else
          {
            length=8;
            tmp_buff= "RESTRICT";
          }
          f_key_info.update_method= make_lex_string(thd, f_key_info.update_method,
                                                    tmp_buff, length, 1);


unknown's avatar
unknown committed
6005 6006 6007 6008 6009 6010

	  FOREIGN_KEY_INFO *pf_key_info= ((FOREIGN_KEY_INFO *)
		  thd->memdup((gptr) &f_key_info,
			  sizeof(FOREIGN_KEY_INFO)));
	  f_key_list->push_back(pf_key_info);
	  foreign = UT_LIST_GET_NEXT(foreign_list, foreign);
6011
  }
6012
  mutex_exit_noninline(&(dict_sys->mutex));
6013
  prebuilt->trx->op_info = (char*)"";
unknown's avatar
unknown committed
6014

6015 6016 6017
  DBUG_RETURN(0);
}

6018 6019 6020 6021 6022 6023 6024 6025 6026 6027 6028 6029
/*********************************************************************
Checks if ALTER TABLE may change the storage engine of the table.
Changing storage engines is not allowed for tables for which there
are foreign key constraints (parent or child tables). */

bool
ha_innobase::can_switch_engines(void)
/*=================================*/
{
	row_prebuilt_t* prebuilt	= (row_prebuilt_t*) innobase_prebuilt;
	bool	can_switch;

unknown's avatar
unknown committed
6030
	DBUG_ENTER("ha_innobase::can_switch_engines");
6031 6032 6033 6034 6035 6036 6037 6038 6039 6040 6041 6042 6043
	prebuilt->trx->op_info =
			"determining if there are foreign key constraints";
	row_mysql_lock_data_dictionary(prebuilt->trx);

	can_switch = !UT_LIST_GET_FIRST(prebuilt->table->referenced_list)
			&& !UT_LIST_GET_FIRST(prebuilt->table->foreign_list);

	row_mysql_unlock_data_dictionary(prebuilt->trx);
	prebuilt->trx->op_info = "";

	DBUG_RETURN(can_switch);
}

unknown's avatar
unknown committed
6044 6045 6046 6047 6048 6049 6050 6051 6052 6053 6054 6055 6056 6057 6058 6059 6060 6061 6062 6063
/***********************************************************************
Checks if a table is referenced by a foreign key. The MySQL manual states that
a REPLACE is either equivalent to an INSERT, or DELETE(s) + INSERT. Only a
delete is then allowed internally to resolve a duplicate key conflict in
REPLACE, not an update. */

uint
ha_innobase::referenced_by_foreign_key(void)
/*========================================*/
			/* out: > 0 if referenced by a FOREIGN KEY */
{
	row_prebuilt_t* prebuilt = (row_prebuilt_t*)innobase_prebuilt;

	if (dict_table_referenced_by_foreign_key(prebuilt->table)) {

		return(1);
	}

	return(0);
}
unknown's avatar
unknown committed
6064 6065 6066 6067 6068 6069 6070 6071

/***********************************************************************
Frees the foreign key create info for a table stored in InnoDB, if it is
non-NULL. */

void
ha_innobase::free_foreign_key_create_info(
/*======================================*/
unknown's avatar
unknown committed
6072
	char*	str)	/* in, own: create info string to free	*/
unknown's avatar
unknown committed
6073 6074
{
	if (str) {
6075
		my_free(str, MYF(0));
unknown's avatar
unknown committed
6076
	}
6077 6078
}

unknown's avatar
unknown committed
6079 6080 6081 6082 6083 6084 6085 6086
/***********************************************************************
Tells something additional to the handler about how to do things. */

int
ha_innobase::extra(
/*===============*/
			   /* out: 0 or error number */
	enum ha_extra_function operation)
6087
			   /* in: HA_EXTRA_FLUSH or some other flag */
unknown's avatar
unknown committed
6088 6089 6090 6091 6092 6093 6094 6095
{
	row_prebuilt_t*	prebuilt = (row_prebuilt_t*) innobase_prebuilt;

	/* Warning: since it is not sure that MySQL calls external_lock
	before calling this function, the trx field in prebuilt can be
	obsolete! */

	switch (operation) {
unknown's avatar
unknown committed
6096 6097 6098 6099 6100 6101 6102 6103 6104
		case HA_EXTRA_FLUSH:
			if (prebuilt->blob_heap) {
				row_mysql_prebuilt_free_blob_heap(prebuilt);
			}
			break;
		case HA_EXTRA_RESET_STATE:
			prebuilt->keep_other_fields_on_keyread = 0;
			prebuilt->read_just_key = 0;
			break;
unknown's avatar
unknown committed
6105
		case HA_EXTRA_NO_KEYREAD:
unknown's avatar
unknown committed
6106 6107 6108 6109 6110
			prebuilt->read_just_key = 0;
			break;
		case HA_EXTRA_KEYREAD:
			prebuilt->read_just_key = 1;
			break;
6111 6112 6113
		case HA_EXTRA_KEYREAD_PRESERVE_FIELDS:
			prebuilt->keep_other_fields_on_keyread = 1;
			break;
unknown's avatar
unknown committed
6114 6115 6116 6117 6118 6119 6120
		default:/* Do nothing */
			;
	}

	return(0);
}

6121 6122 6123 6124 6125 6126 6127 6128 6129 6130 6131 6132
int ha_innobase::reset()
{
  row_prebuilt_t*	prebuilt = (row_prebuilt_t*) innobase_prebuilt;
  if (prebuilt->blob_heap) {
    row_mysql_prebuilt_free_blob_heap(prebuilt);
  }
  prebuilt->keep_other_fields_on_keyread = 0;
  prebuilt->read_just_key = 0;
  return 0;
}


unknown's avatar
unknown committed
6133
/**********************************************************************
unknown's avatar
unknown committed
6134 6135 6136 6137
MySQL calls this function at the start of each SQL statement inside LOCK
TABLES. Inside LOCK TABLES the ::external_lock method does not work to
mark SQL statement borders. Note also a special case: if a temporary table
is created inside LOCK TABLES, MySQL has not called external_lock() at all
unknown's avatar
unknown committed
6138 6139 6140 6141 6142 6143
on that table.
MySQL-5.0 also calls this before each statement in an execution of a stored
procedure. To make the execution more deterministic for binlogging, MySQL-5.0
locks all tables involved in a stored procedure with full explicit table
locks (thd->in_lock_tables is true in ::store_lock()) before executing the
procedure. */
unknown's avatar
unknown committed
6144 6145

int
unknown's avatar
unknown committed
6146 6147
ha_innobase::start_stmt(
/*====================*/
unknown's avatar
unknown committed
6148 6149 6150
				/* out: 0 or error code */
	THD*		thd,	/* in: handle to the user thread */
	thr_lock_type	lock_type)
unknown's avatar
unknown committed
6151 6152 6153 6154 6155 6156 6157 6158
{
	row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
	trx_t*		trx;

	update_thd(thd);

	trx = prebuilt->trx;

unknown's avatar
unknown committed
6159 6160 6161 6162 6163 6164 6165
	/* Here we release the search latch and the InnoDB thread FIFO ticket
	if they were reserved. They should have been released already at the
	end of the previous statement, but because inside LOCK TABLES the
	lock count method does not work to mark the end of a SELECT statement,
	that may not be the case. We MUST release the search latch before an
	INSERT, for example. */

unknown's avatar
unknown committed
6166 6167
	innobase_release_stat_resources(trx);

unknown's avatar
unknown committed
6168
	if (trx->isolation_level <= TRX_ISO_READ_COMMITTED
unknown's avatar
unknown committed
6169 6170
						&& trx->global_read_view) {
		/* At low transaction isolation levels we let
unknown's avatar
unknown committed
6171 6172
		each consistent read set its own snapshot */

unknown's avatar
unknown committed
6173
		read_view_close_for_mysql(trx);
unknown's avatar
unknown committed
6174 6175
	}

unknown's avatar
unknown committed
6176
	prebuilt->sql_stat_start = TRUE;
unknown's avatar
unknown committed
6177
	prebuilt->hint_need_to_fetch_extra_cols = 0;
unknown's avatar
unknown committed
6178
	prebuilt->read_just_key = 0;
unknown's avatar
unknown committed
6179
	prebuilt->keep_other_fields_on_keyread = FALSE;
unknown's avatar
unknown committed
6180

6181
	if (!prebuilt->mysql_has_locked) {
unknown's avatar
unknown committed
6182 6183 6184 6185 6186 6187
		/* This handle is for a temporary table created inside
		this same LOCK TABLES; since MySQL does NOT call external_lock
		in this case, we must use x-row locks inside InnoDB to be
		prepared for an update of a row */

		prebuilt->select_lock_type = LOCK_X;
unknown's avatar
unknown committed
6188 6189
	} else {
		if (trx->isolation_level != TRX_ISO_SERIALIZABLE
unknown's avatar
unknown committed
6190 6191 6192
			&& thd->lex->sql_command == SQLCOM_SELECT
			&& lock_type == TL_READ) {

unknown's avatar
unknown committed
6193 6194 6195 6196 6197 6198 6199 6200 6201
			/* For other than temporary tables, we obtain
			no lock for consistent read (plain SELECT). */

			prebuilt->select_lock_type = LOCK_NONE;
		} else {
			/* Not a consistent read: restore the
			select_lock_type value. The value of
			stored_select_lock_type was decided in:
			1) ::store_lock(),
unknown's avatar
unknown committed
6202 6203
			2) ::external_lock(),
			3) ::init_table_handle_for_HANDLER(), and
6204
			4) :.transactional_table_lock(). */
unknown's avatar
unknown committed
6205 6206 6207 6208 6209 6210

			prebuilt->select_lock_type =
				prebuilt->stored_select_lock_type;
		}

		if (prebuilt->stored_select_lock_type != LOCK_S
unknown's avatar
unknown committed
6211 6212 6213 6214 6215
			&& prebuilt->stored_select_lock_type != LOCK_X) {
			sql_print_error(
				"stored_select_lock_type is %lu inside "
				"::start_stmt()!",
				prebuilt->stored_select_lock_type);
unknown's avatar
unknown committed
6216 6217 6218 6219 6220 6221 6222 6223 6224

			/* Set the value to LOCK_X: this is just fault
			tolerance, we do not know what the correct value
			should be! */

			prebuilt->select_lock_type = LOCK_X;
		}
	}

6225 6226
	trx->detailed_error[0] = '\0';

unknown's avatar
unknown committed
6227
	/* Set the MySQL flag to mark that there is an active transaction */
unknown's avatar
unknown committed
6228
	if (trx->active_trans == 0) {
unknown's avatar
unknown committed
6229

unknown's avatar
unknown committed
6230 6231 6232
		innobase_register_trx_and_stmt(thd);
		trx->active_trans = 1;
	} else {
unknown's avatar
unknown committed
6233 6234
		innobase_register_stmt(thd);
	}
unknown's avatar
unknown committed
6235 6236

	return(0);
unknown's avatar
unknown committed
6237 6238
}

unknown's avatar
unknown committed
6239 6240 6241 6242 6243 6244 6245 6246 6247 6248 6249
/**********************************************************************
Maps a MySQL trx isolation level code to the InnoDB isolation level code */
inline
ulint
innobase_map_isolation_level(
/*=========================*/
					/* out: InnoDB isolation level */
	enum_tx_isolation	iso)	/* in: MySQL isolation level code */
{
	switch(iso) {
		case ISO_REPEATABLE_READ: return(TRX_ISO_REPEATABLE_READ);
unknown's avatar
unknown committed
6250
		case ISO_READ_COMMITTED: return(TRX_ISO_READ_COMMITTED);
unknown's avatar
unknown committed
6251 6252 6253
		case ISO_SERIALIZABLE: return(TRX_ISO_SERIALIZABLE);
		case ISO_READ_UNCOMMITTED: return(TRX_ISO_READ_UNCOMMITTED);
		default: ut_a(0); return(0);
unknown's avatar
unknown committed
6254
	}
unknown's avatar
unknown committed
6255
}
unknown's avatar
unknown committed
6256

unknown's avatar
unknown committed
6257 6258
/**********************************************************************
As MySQL will execute an external lock for every new table it uses when it
unknown's avatar
unknown committed
6259 6260 6261
starts to process an SQL statement (an exception is when MySQL calls
start_stmt for the handle) we can use this function to store the pointer to
the THD in the handle. We will also use this function to communicate
unknown's avatar
unknown committed
6262 6263 6264 6265 6266 6267 6268
to InnoDB that a new SQL statement has started and that we must store a
savepoint to our transaction handle, so that we are able to roll back
the SQL statement in case of an error. */

int
ha_innobase::external_lock(
/*=======================*/
unknown's avatar
unknown committed
6269
				/* out: 0 */
unknown's avatar
unknown committed
6270
	THD*	thd,		/* in: handle to the user thread */
unknown's avatar
unknown committed
6271
	int	lock_type)	/* in: lock type */
unknown's avatar
unknown committed
6272 6273 6274 6275
{
	row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
	trx_t*		trx;

unknown's avatar
unknown committed
6276
	DBUG_ENTER("ha_innobase::external_lock");
unknown's avatar
unknown committed
6277
	DBUG_PRINT("enter",("lock_type: %d", lock_type));
unknown's avatar
unknown committed
6278 6279 6280 6281 6282 6283

	update_thd(thd);

	trx = prebuilt->trx;

	prebuilt->sql_stat_start = TRUE;
unknown's avatar
unknown committed
6284
	prebuilt->hint_need_to_fetch_extra_cols = 0;
unknown's avatar
unknown committed
6285 6286

	prebuilt->read_just_key = 0;
6287
	prebuilt->keep_other_fields_on_keyread = FALSE;
unknown's avatar
unknown committed
6288 6289 6290 6291 6292 6293

	if (lock_type == F_WRLCK) {

		/* If this is a SELECT, then it is in UPDATE TABLE ...
		or SELECT ... FOR UPDATE */
		prebuilt->select_lock_type = LOCK_X;
unknown's avatar
unknown committed
6294
		prebuilt->stored_select_lock_type = LOCK_X;
unknown's avatar
unknown committed
6295 6296 6297
	}

	if (lock_type != F_UNLCK) {
unknown's avatar
unknown committed
6298
		/* MySQL is setting a new table lock */
unknown's avatar
unknown committed
6299

6300
		trx->detailed_error[0] = '\0';
unknown's avatar
unknown committed
6301

unknown's avatar
unknown committed
6302 6303
		/* Set the MySQL flag to mark that there is an active
		transaction */
unknown's avatar
unknown committed
6304
		if (trx->active_trans == 0) {
unknown's avatar
unknown committed
6305

unknown's avatar
unknown committed
6306 6307 6308
			innobase_register_trx_and_stmt(thd);
			trx->active_trans = 1;
		} else if (trx->n_mysql_tables_in_use == 0) {
unknown's avatar
unknown committed
6309 6310
			innobase_register_stmt(thd);
		}
unknown's avatar
unknown committed
6311

unknown's avatar
unknown committed
6312
		trx->n_mysql_tables_in_use++;
6313
		prebuilt->mysql_has_locked = TRUE;
unknown's avatar
unknown committed
6314

unknown's avatar
unknown committed
6315
		if (trx->isolation_level == TRX_ISO_SERIALIZABLE
unknown's avatar
unknown committed
6316 6317
			&& prebuilt->select_lock_type == LOCK_NONE
			&& (thd->options
unknown's avatar
unknown committed
6318
				& (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) {
unknown's avatar
unknown committed
6319

unknown's avatar
unknown committed
6320 6321
			/* To get serializable execution, we let InnoDB
			conceptually add 'LOCK IN SHARE MODE' to all SELECTs
unknown's avatar
unknown committed
6322 6323 6324 6325 6326
			which otherwise would have been consistent reads. An
			exception is consistent reads in the AUTOCOMMIT=1 mode:
			we know that they are read-only transactions, and they
			can be serialized also if performed as consistent
			reads. */
unknown's avatar
unknown committed
6327 6328

			prebuilt->select_lock_type = LOCK_S;
unknown's avatar
unknown committed
6329
			prebuilt->stored_select_lock_type = LOCK_S;
unknown's avatar
unknown committed
6330 6331
		}

unknown's avatar
unknown committed
6332 6333 6334 6335
		/* Starting from 4.1.9, no InnoDB table lock is taken in LOCK
		TABLES if AUTOCOMMIT=1. It does not make much sense to acquire
		an InnoDB table lock if it is released immediately at the end
		of LOCK TABLES, and InnoDB's table locks in that case cause
unknown's avatar
unknown committed
6336 6337 6338 6339 6340 6341
		VERY easily deadlocks.

		We do not set InnoDB table locks if user has not explicitly
		requested a table lock. Note that thd->in_lock_tables
		can  be TRUE on some cases e.g. at the start of a stored
		procedure call (SQLCOM_CALL). */
unknown's avatar
unknown committed
6342

unknown's avatar
unknown committed
6343
		if (prebuilt->select_lock_type != LOCK_NONE) {
unknown's avatar
unknown committed
6344

6345
			if (thd->in_lock_tables &&
unknown's avatar
unknown committed
6346 6347 6348
				thd->lex->sql_command == SQLCOM_LOCK_TABLES &&
				thd->variables.innodb_table_locks &&
				(thd->options & OPTION_NOT_AUTOCOMMIT)) {
unknown's avatar
unknown committed
6349

unknown's avatar
unknown committed
6350 6351
				ulint	error = row_lock_table_for_mysql(
					prebuilt, NULL, 0);
6352 6353 6354

				if (error != DB_SUCCESS) {
					error = convert_error_code_to_mysql(
unknown's avatar
unknown committed
6355 6356
						(int) error, user_thd);
					DBUG_RETURN((int) error);
6357 6358
				}
			}
unknown's avatar
unknown committed
6359

unknown's avatar
unknown committed
6360
			trx->mysql_n_tables_locked++;
unknown's avatar
unknown committed
6361 6362
		}

6363
		DBUG_RETURN(0);
unknown's avatar
unknown committed
6364
	}
unknown's avatar
unknown committed
6365

unknown's avatar
unknown committed
6366
	/* MySQL is releasing a table lock */
unknown's avatar
unknown committed
6367

unknown's avatar
unknown committed
6368 6369
	trx->n_mysql_tables_in_use--;
	prebuilt->mysql_has_locked = FALSE;
unknown's avatar
unknown committed
6370

unknown's avatar
unknown committed
6371 6372
	/* If the MySQL lock count drops to zero we know that the current SQL
	statement has ended */
unknown's avatar
unknown committed
6373

unknown's avatar
unknown committed
6374
	if (trx->n_mysql_tables_in_use == 0) {
unknown's avatar
unknown committed
6375

unknown's avatar
unknown committed
6376
		trx->mysql_n_tables_locked = 0;
unknown's avatar
unknown committed
6377
		prebuilt->used_in_HANDLER = FALSE;
unknown's avatar
unknown committed
6378

unknown's avatar
unknown committed
6379 6380 6381 6382
		/* Release a possible FIFO ticket and search latch. Since we
		may reserve the kernel mutex, we have to release the search
		system latch first to obey the latching order. */

unknown's avatar
unknown committed
6383
		innobase_release_stat_resources(trx);
unknown's avatar
unknown committed
6384

unknown's avatar
unknown committed
6385
		if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) {
unknown's avatar
unknown committed
6386 6387
			if (trx->active_trans != 0) {
				innobase_commit(thd, TRUE);
unknown's avatar
unknown committed
6388 6389
			}
		} else {
unknown's avatar
unknown committed
6390
			if (trx->isolation_level <= TRX_ISO_READ_COMMITTED
unknown's avatar
unknown committed
6391
						&& trx->global_read_view) {
unknown's avatar
unknown committed
6392

unknown's avatar
unknown committed
6393
				/* At low transaction isolation levels we let
unknown's avatar
unknown committed
6394 6395
				each consistent read set its own snapshot */

unknown's avatar
unknown committed
6396
				read_view_close_for_mysql(trx);
unknown's avatar
unknown committed
6397
			}
unknown's avatar
unknown committed
6398 6399 6400
		}
	}

6401
	DBUG_RETURN(0);
unknown's avatar
unknown committed
6402 6403
}

6404 6405 6406 6407 6408 6409 6410
/**********************************************************************
With this function MySQL request a transactional lock to a table when
user issued query LOCK TABLES..WHERE ENGINE = InnoDB. */

int
ha_innobase::transactional_table_lock(
/*==================================*/
unknown's avatar
unknown committed
6411
				/* out: error code */
6412
	THD*	thd,		/* in: handle to the user thread */
unknown's avatar
unknown committed
6413
	int	lock_type)	/* in: lock type */
6414 6415 6416 6417
{
	row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
	trx_t*		trx;

unknown's avatar
unknown committed
6418
	DBUG_ENTER("ha_innobase::transactional_table_lock");
6419 6420 6421 6422 6423 6424 6425 6426
	DBUG_PRINT("enter",("lock_type: %d", lock_type));

	/* We do not know if MySQL can call this function before calling
	external_lock(). To be safe, update the thd of the current table
	handle. */

	update_thd(thd);

unknown's avatar
unknown committed
6427 6428 6429
	if (prebuilt->table->ibd_file_missing && !current_thd->tablespace_op) {
		ut_print_timestamp(stderr);
		fprintf(stderr, "  InnoDB error:\n"
6430 6431 6432
"MySQL is trying to use a table handle but the .ibd file for\n"
"table %s does not exist.\n"
"Have you deleted the .ibd file from the database directory under\n"
unknown's avatar
unknown committed
6433
"the MySQL datadir?"
unknown's avatar
unknown committed
6434
"See http://dev.mysql.com/doc/refman/5.0/en/innodb-troubleshooting.html\n"
6435 6436 6437 6438 6439 6440 6441 6442 6443 6444 6445 6446 6447 6448 6449 6450 6451
"how you can resolve the problem.\n",
				prebuilt->table->name);
		DBUG_RETURN(HA_ERR_CRASHED);
	}

	trx = prebuilt->trx;

	prebuilt->sql_stat_start = TRUE;
	prebuilt->hint_need_to_fetch_extra_cols = 0;

	prebuilt->read_just_key = 0;
	prebuilt->keep_other_fields_on_keyread = FALSE;

	if (lock_type == F_WRLCK) {
		prebuilt->select_lock_type = LOCK_X;
		prebuilt->stored_select_lock_type = LOCK_X;
	} else if (lock_type == F_RDLCK) {
unknown's avatar
unknown committed
6452 6453
		prebuilt->select_lock_type = LOCK_S;
		prebuilt->stored_select_lock_type = LOCK_S;
6454
	} else {
unknown's avatar
unknown committed
6455 6456
		ut_print_timestamp(stderr);
		fprintf(stderr, "  InnoDB error:\n"
6457 6458 6459 6460 6461 6462 6463 6464 6465
"MySQL is trying to set transactional table lock with corrupted lock type\n"
"to table %s, lock type %d does not exist.\n",
				prebuilt->table->name, lock_type);
		DBUG_RETURN(HA_ERR_CRASHED);
	}

	/* MySQL is setting a new transactional table lock */

	/* Set the MySQL flag to mark that there is an active transaction */
unknown's avatar
unknown committed
6466
	if (trx->active_trans == 0) {
unknown's avatar
unknown committed
6467

unknown's avatar
unknown committed
6468 6469 6470
		innobase_register_trx_and_stmt(thd);
		trx->active_trans = 1;
	}
6471 6472 6473 6474

	if (thd->in_lock_tables && thd->variables.innodb_table_locks) {
		ulint	error = DB_SUCCESS;

6475
		error = row_lock_table_for_mysql(prebuilt, NULL, 0);
6476 6477

		if (error != DB_SUCCESS) {
unknown's avatar
unknown committed
6478 6479
			error = convert_error_code_to_mysql((int) error, user_thd);
			DBUG_RETURN((int) error);
6480 6481 6482 6483
		}

		if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) {

unknown's avatar
unknown committed
6484 6485
			/* Store the current undo_no of the transaction
			so that we know where to roll back if we have
6486 6487 6488 6489 6490 6491 6492 6493 6494
			to roll back the next SQL statement */

			trx_mark_sql_stat_end(trx);
		}
	}

	DBUG_RETURN(0);
}

6495 6496 6497
/****************************************************************************
Here we export InnoDB status variables to MySQL.  */

6498
int
unknown's avatar
unknown committed
6499
innodb_export_status()
unknown's avatar
unknown committed
6500
/*==================*/
6501
{
unknown's avatar
unknown committed
6502 6503 6504 6505 6506
	if (innodb_inited) {
		srv_export_innodb_status();
	}

	return 0;
6507 6508
}

unknown's avatar
unknown committed
6509
/****************************************************************************
unknown's avatar
unknown committed
6510
Implements the SHOW INNODB STATUS command. Sends the output of the InnoDB
unknown's avatar
unknown committed
6511 6512
Monitor to the client. */

unknown's avatar
unknown committed
6513
bool
unknown's avatar
unknown committed
6514 6515
innodb_show_status(
/*===============*/
6516 6517
	THD*	thd,	/* in: the MySQL query thread of the caller */
	stat_print_fn *stat_print)
unknown's avatar
unknown committed
6518
{
6519 6520 6521 6522 6523
	trx_t*			trx;
	static const char	truncated_msg[] = "... truncated...\n";
	const long		MAX_STATUS_SIZE = 64000;
	ulint			trx_list_start = ULINT_UNDEFINED;
	ulint			trx_list_end = ULINT_UNDEFINED;
unknown's avatar
unknown committed
6524

unknown's avatar
unknown committed
6525
	DBUG_ENTER("innodb_show_status");
unknown's avatar
unknown committed
6526

unknown's avatar
unknown committed
6527 6528 6529
	if (have_innodb != SHOW_OPTION_YES) {
		DBUG_RETURN(FALSE);
	}
unknown's avatar
unknown committed
6530

unknown's avatar
unknown committed
6531 6532 6533 6534
	trx = check_trx_exists(thd);

	innobase_release_stat_resources(trx);

6535 6536
	/* We let the InnoDB Monitor to output at most MAX_STATUS_SIZE
	bytes of text. */
6537

6538
	long	flen, usable_len;
6539
	char*	str;
6540

6541
	mutex_enter_noninline(&srv_monitor_file_mutex);
6542
	rewind(srv_monitor_file);
6543 6544
	srv_printf_innodb_monitor(srv_monitor_file,
				&trx_list_start, &trx_list_end);
6545
	flen = ftell(srv_monitor_file);
6546
	os_file_set_eof(srv_monitor_file);
unknown's avatar
unknown committed
6547

6548 6549
	if (flen < 0) {
		flen = 0;
6550 6551 6552 6553 6554 6555
	}

	if (flen > MAX_STATUS_SIZE) {
		usable_len = MAX_STATUS_SIZE;
	} else {
		usable_len = flen;
6556
	}
unknown's avatar
unknown committed
6557

6558 6559
	/* allocate buffer for the string, and
	read the contents of the temporary file */
unknown's avatar
unknown committed
6560

unknown's avatar
unknown committed
6561 6562 6563 6564
	if (!(str = my_malloc(usable_len + 1, MYF(0)))) {
	  mutex_exit_noninline(&srv_monitor_file_mutex);
	  DBUG_RETURN(TRUE);
	}
unknown's avatar
unknown committed
6565

unknown's avatar
unknown committed
6566
	rewind(srv_monitor_file);
6567 6568
	if (flen < MAX_STATUS_SIZE) {
		/* Display the entire output. */
unknown's avatar
unknown committed
6569
		flen = (long) fread(str, 1, flen, srv_monitor_file);
6570 6571 6572 6573 6574
	} else if (trx_list_end < (ulint) flen
			&& trx_list_start < trx_list_end
			&& trx_list_start + (flen - trx_list_end)
			< MAX_STATUS_SIZE - sizeof truncated_msg - 1) {
		/* Omit the beginning of the list of active transactions. */
unknown's avatar
unknown committed
6575
		long len = (long) fread(str, 1, trx_list_start, srv_monitor_file);
6576 6577 6578 6579
		memcpy(str + len, truncated_msg, sizeof truncated_msg - 1);
		len += sizeof truncated_msg - 1;
		usable_len = (MAX_STATUS_SIZE - 1) - len;
		fseek(srv_monitor_file, flen - usable_len, SEEK_SET);
unknown's avatar
unknown committed
6580
		len += (long) fread(str + len, 1, usable_len, srv_monitor_file);
6581 6582 6583
		flen = len;
	} else {
		/* Omit the end of the output. */
unknown's avatar
unknown committed
6584
		flen = (long) fread(str, 1, MAX_STATUS_SIZE - 1, srv_monitor_file);
6585
	}
unknown's avatar
unknown committed
6586

6587
	mutex_exit_noninline(&srv_monitor_file_mutex);
6588

6589
	bool result = FALSE;
unknown's avatar
unknown committed
6590

unknown's avatar
unknown committed
6591
	if (stat_print(thd, innobase_hton_name, strlen(innobase_hton_name),
unknown's avatar
unknown committed
6592
			STRING_WITH_LEN(""), str, flen)) {
6593
		result= TRUE;
unknown's avatar
unknown committed
6594
	}
6595
	my_free(str, MYF(0));
unknown's avatar
unknown committed
6596

6597
	DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
6598 6599
}

unknown's avatar
unknown committed
6600 6601 6602 6603 6604
/****************************************************************************
Implements the SHOW MUTEX STATUS command. . */

bool
innodb_mutex_show_status(
unknown's avatar
unknown committed
6605 6606 6607 6608 6609 6610 6611 6612 6613 6614 6615 6616 6617
/*=====================*/
	THD*		thd,		/* in: the MySQL query thread of the
					caller */
	stat_print_fn*	stat_print)
{
	char buf1[IO_SIZE], buf2[IO_SIZE];
	mutex_t*  mutex;
	ulint	  rw_lock_count= 0;
	ulint	  rw_lock_count_spin_loop= 0;
	ulint	  rw_lock_count_spin_rounds= 0;
	ulint	  rw_lock_count_os_wait= 0;
	ulint	  rw_lock_count_os_yield= 0;
	ulonglong rw_lock_wait_time= 0;
unknown's avatar
unknown committed
6618
	uint	  hton_name_len= strlen(innobase_hton_name), buf1len, buf2len;
unknown's avatar
unknown committed
6619
	DBUG_ENTER("innodb_mutex_show_status");
unknown's avatar
unknown committed
6620

unknown's avatar
unknown committed
6621
#ifdef MUTEX_PROTECT_TO_BE_ADDED_LATER
unknown's avatar
unknown committed
6622
	mutex_enter(&mutex_list_mutex);
unknown's avatar
unknown committed
6623
#endif
unknown's avatar
unknown committed
6624

unknown's avatar
unknown committed
6625 6626 6627 6628 6629 6630 6631 6632 6633 6634 6635 6636 6637 6638 6639 6640 6641 6642 6643 6644
	mutex = UT_LIST_GET_FIRST(mutex_list);

	while (mutex != NULL) {
		if (mutex->mutex_type != 1) {
			if (mutex->count_using > 0) {
				buf1len= my_snprintf(buf1, sizeof(buf1),
					"%s:%s",
					mutex->cmutex_name, mutex->cfile_name);
				buf2len= my_snprintf(buf2, sizeof(buf2),
					"count=%lu, spin_waits=%lu,"
					" spin_rounds=%lu, "
					"os_waits=%lu, os_yields=%lu,"
					" os_wait_times=%lu",
					mutex->count_using,
					mutex->count_spin_loop,
					mutex->count_spin_rounds,
					mutex->count_os_wait,
					mutex->count_os_yield,
					mutex->lspent_time/1000);

unknown's avatar
unknown committed
6645
				if (stat_print(thd, innobase_hton_name,
unknown's avatar
unknown committed
6646 6647
						hton_name_len, buf1, buf1len,
						buf2, buf2len)) {
unknown's avatar
unknown committed
6648
#ifdef MUTEX_PROTECT_TO_BE_ADDED_LATER
unknown's avatar
unknown committed
6649
					mutex_exit(&mutex_list_mutex);
unknown's avatar
unknown committed
6650
#endif
unknown's avatar
unknown committed
6651 6652 6653 6654 6655 6656 6657 6658 6659 6660 6661 6662
					DBUG_RETURN(1);
				}
			}
		}
		else {
			rw_lock_count += mutex->count_using;
			rw_lock_count_spin_loop += mutex->count_spin_loop;
			rw_lock_count_spin_rounds += mutex->count_spin_rounds;
			rw_lock_count_os_wait += mutex->count_os_wait;
			rw_lock_count_os_yield += mutex->count_os_yield;
			rw_lock_wait_time += mutex->lspent_time;
		}
unknown's avatar
unknown committed
6663

unknown's avatar
unknown committed
6664 6665 6666 6667 6668 6669 6670 6671 6672 6673 6674
		mutex = UT_LIST_GET_NEXT(list, mutex);
	}

	buf2len= my_snprintf(buf2, sizeof(buf2),
		"count=%lu, spin_waits=%lu, spin_rounds=%lu, "
		"os_waits=%lu, os_yields=%lu, os_wait_times=%lu",
		rw_lock_count, rw_lock_count_spin_loop,
		rw_lock_count_spin_rounds,
		rw_lock_count_os_wait, rw_lock_count_os_yield,
		rw_lock_wait_time/1000);

unknown's avatar
unknown committed
6675
	if (stat_print(thd, innobase_hton_name, hton_name_len,
unknown's avatar
unknown committed
6676 6677 6678
			STRING_WITH_LEN("rw_lock_mutexes"), buf2, buf2len)) {
		DBUG_RETURN(1);
	}
unknown's avatar
unknown committed
6679

unknown's avatar
unknown committed
6680
#ifdef MUTEX_PROTECT_TO_BE_ADDED_LATER
unknown's avatar
unknown committed
6681
	mutex_exit(&mutex_list_mutex);
unknown's avatar
unknown committed
6682
#endif
unknown's avatar
unknown committed
6683 6684

	DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
6685 6686
}

6687
bool innobase_show_status(THD* thd, stat_print_fn* stat_print,
unknown's avatar
unknown committed
6688 6689 6690 6691 6692 6693 6694 6695 6696 6697
	enum ha_stat_type stat_type)
{
	switch (stat_type) {
	case HA_ENGINE_STATUS:
		return innodb_show_status(thd, stat_print);
	case HA_ENGINE_MUTEX:
		return innodb_mutex_show_status(thd, stat_print);
	default:
		return FALSE;
	}
6698 6699 6700
}


6701 6702 6703 6704 6705
/****************************************************************************
 Handling the shared INNOBASE_SHARE structure that is needed to provide table
 locking.
****************************************************************************/

unknown's avatar
unknown committed
6706 6707 6708 6709 6710 6711
static mysql_byte* innobase_get_key(INNOBASE_SHARE* share, uint* length,
	my_bool not_used __attribute__((unused)))
{
	*length=share->table_name_length;

	return (mysql_byte*) share->table_name;
6712 6713
}

unknown's avatar
unknown committed
6714
static INNOBASE_SHARE* get_share(const char* table_name)
6715
{
unknown's avatar
unknown committed
6716 6717 6718 6719 6720 6721 6722 6723 6724 6725 6726 6727 6728 6729 6730 6731 6732 6733 6734 6735 6736 6737 6738 6739 6740 6741 6742 6743 6744 6745 6746 6747 6748 6749 6750 6751 6752 6753 6754 6755 6756 6757 6758 6759 6760
	INNOBASE_SHARE *share;
	pthread_mutex_lock(&innobase_share_mutex);
	uint length=(uint) strlen(table_name);

	if (!(share=(INNOBASE_SHARE*) hash_search(&innobase_open_tables,
				(mysql_byte*) table_name,
				length))) {

		share = (INNOBASE_SHARE *) my_malloc(sizeof(*share)+length+1,
			MYF(MY_FAE | MY_ZEROFILL));

		share->table_name_length=length;
		share->table_name=(char*) (share+1);
		strmov(share->table_name,table_name);

		if (my_hash_insert(&innobase_open_tables,
				(mysql_byte*) share)) {
			pthread_mutex_unlock(&innobase_share_mutex);
			my_free((gptr) share,0);

			return 0;
		}

		thr_lock_init(&share->lock);
		pthread_mutex_init(&share->mutex,MY_MUTEX_INIT_FAST);
	}

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

	return share;
}

static void free_share(INNOBASE_SHARE* share)
{
	pthread_mutex_lock(&innobase_share_mutex);

	if (!--share->use_count) {
		hash_delete(&innobase_open_tables, (mysql_byte*) share);
		thr_lock_delete(&share->lock);
		pthread_mutex_destroy(&share->mutex);
		my_free((gptr) share, MYF(0));
	}

	pthread_mutex_unlock(&innobase_share_mutex);
6761
}
6762 6763

/*********************************************************************
unknown's avatar
unknown committed
6764
Converts a MySQL table lock stored in the 'lock' field of the handle to
unknown's avatar
unknown committed
6765 6766 6767 6768 6769 6770
a proper type before storing pointer to the lock into an array of pointers.
MySQL also calls this if it wants to reset some table locks to a not-locked
state during the processing of an SQL query. An example is that during a
SELECT the read lock is released early on the 'const' tables where we only
fetch one row. MySQL does not call this when it releases all locks at the
end of an SQL statement. */
6771 6772 6773 6774 6775 6776 6777 6778 6779 6780 6781 6782

THR_LOCK_DATA**
ha_innobase::store_lock(
/*====================*/
						/* out: pointer to the next
						element in the 'to' array */
	THD*			thd,		/* in: user thread handle */
	THR_LOCK_DATA**		to,		/* in: pointer to an array
						of pointers to lock structs;
						pointer to the 'lock' field
						of current handle is stored
						next to this array */
unknown's avatar
unknown committed
6783
	enum thr_lock_type	lock_type)	/* in: lock type to store in
unknown's avatar
unknown committed
6784 6785
						'lock'; this may also be
						TL_IGNORE */
6786 6787
{
	row_prebuilt_t* prebuilt	= (row_prebuilt_t*) innobase_prebuilt;
unknown's avatar
unknown committed
6788
	trx_t*		trx		= prebuilt->trx;
6789

unknown's avatar
unknown committed
6790
	/* NOTE: MySQL	can call this function with lock 'type' TL_IGNORE!
unknown's avatar
unknown committed
6791 6792 6793
	Be careful to ignore TL_IGNORE if we are going to do something with
	only 'real' locks! */

unknown's avatar
unknown committed
6794 6795 6796 6797 6798 6799 6800 6801 6802 6803
	/* If no MySQL tables is use we need to set isolation level
	of the transaction. */

	if (lock_type != TL_IGNORE
	&& trx->n_mysql_tables_in_use == 0) {
		trx->isolation_level = innobase_map_isolation_level(
						(enum_tx_isolation)
						thd->variables.tx_isolation);
	}

unknown's avatar
unknown committed
6804 6805 6806 6807 6808 6809
	if ((lock_type == TL_READ && thd->in_lock_tables) ||
		(lock_type == TL_READ_HIGH_PRIORITY && thd->in_lock_tables) ||
		lock_type == TL_READ_WITH_SHARED_LOCKS ||
		lock_type == TL_READ_NO_INSERT ||
		(thd->lex->sql_command != SQLCOM_SELECT
			&& lock_type != TL_IGNORE)) {
unknown's avatar
unknown committed
6810

unknown's avatar
unknown committed
6811 6812 6813 6814 6815
		/* The OR cases above are in this order:
		1) MySQL is doing LOCK TABLES ... READ LOCAL, or
		2) (we do not know when TL_READ_HIGH_PRIORITY is used), or
		3) this is a SELECT ... IN SHARE MODE, or
		4) we are doing a complex SQL statement like
unknown's avatar
unknown committed
6816
		INSERT INTO ... SELECT ... and the logical logging (MySQL
unknown's avatar
unknown committed
6817
		binlog) requires the use of a locking read, or
unknown's avatar
unknown committed
6818 6819 6820
		MySQL is doing LOCK TABLES ... READ.
		5) we let InnoDB do locking reads for all SQL statements that
		are not simple SELECTs; note that select_lock_type in this
unknown's avatar
unknown committed
6821 6822 6823 6824 6825 6826
		case may get strengthened in ::external_lock() to LOCK_X.
		Note that we MUST use a locking read in all data modifying
		SQL statements, because otherwise the execution would not be
		serializable, and also the results from the update could be
		unexpected if an obsolete consistent read view would be
		used. */
unknown's avatar
unknown committed
6827

unknown's avatar
unknown committed
6828 6829 6830 6831 6832 6833 6834 6835 6836 6837 6838
		ulint	isolation_level;

		isolation_level = trx->isolation_level;

		if ((srv_locks_unsafe_for_binlog
			|| isolation_level == TRX_ISO_READ_COMMITTED)
		&& isolation_level != TRX_ISO_SERIALIZABLE
		&& (lock_type == TL_READ || lock_type == TL_READ_NO_INSERT)
		&& (thd->lex->sql_command == SQLCOM_INSERT_SELECT
			|| thd->lex->sql_command == SQLCOM_UPDATE
			|| thd->lex->sql_command == SQLCOM_CREATE_TABLE)) {
6839

unknown's avatar
unknown committed
6840 6841 6842
			/* If we either have innobase_locks_unsafe_for_binlog
			option set or this session is using READ COMMITTED
			isolation level and isolation level of the transaction
6843
			is not set to serializable and MySQL is doing
unknown's avatar
unknown committed
6844 6845 6846 6847
			INSERT INTO...SELECT or UPDATE ... = (SELECT ...) or
			CREATE  ... SELECT... without FOR UPDATE or
			IN SHARE MODE in select, then we use consistent
			read for select. */
6848 6849 6850

			prebuilt->select_lock_type = LOCK_NONE;
			prebuilt->stored_select_lock_type = LOCK_NONE;
6851
		} else if (thd->lex->sql_command == SQLCOM_CHECKSUM) {
6852
			/* Use consistent read for checksum table */
6853

6854 6855 6856 6857 6858 6859
			prebuilt->select_lock_type = LOCK_NONE;
			prebuilt->stored_select_lock_type = LOCK_NONE;
		} else {
			prebuilt->select_lock_type = LOCK_S;
			prebuilt->stored_select_lock_type = LOCK_S;
		}
unknown's avatar
unknown committed
6860

unknown's avatar
unknown committed
6861 6862
	} else if (lock_type != TL_IGNORE) {

unknown's avatar
unknown committed
6863
		/* We set possible LOCK_X value in external_lock, not yet
6864
		here even if this would be SELECT ... FOR UPDATE */
unknown's avatar
unknown committed
6865

6866
		prebuilt->select_lock_type = LOCK_NONE;
unknown's avatar
unknown committed
6867
		prebuilt->stored_select_lock_type = LOCK_NONE;
6868 6869 6870 6871
	}

	if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK) {

unknown's avatar
unknown committed
6872
		/* Starting from 5.0.7, we weaken also the table locks
unknown's avatar
unknown committed
6873 6874 6875 6876 6877 6878 6879
		set at the start of a MySQL stored procedure call, just like
		we weaken the locks set at the start of an SQL statement.
		MySQL does set thd->in_lock_tables TRUE there, but in reality
		we do not need table locks to make the execution of a
		single transaction stored procedure call deterministic
		(if it does not use a consistent read). */

unknown's avatar
unknown committed
6880 6881 6882 6883 6884 6885 6886 6887
		if (lock_type == TL_READ && thd->in_lock_tables) {
			/* We come here if MySQL is processing LOCK TABLES
			... READ LOCAL. MyISAM under that table lock type
			reads the table as it was at the time the lock was
			granted (new inserts are allowed, but not seen by the
			reader). To get a similar effect on an InnoDB table,
			we must use LOCK TABLES ... READ. We convert the lock
			type here, so that for InnoDB, READ LOCAL is
unknown's avatar
unknown committed
6888 6889 6890
			equivalent to READ. This will change the InnoDB
			behavior in mysqldump, so that dumps of InnoDB tables
			are consistent with dumps of MyISAM tables. */
unknown's avatar
unknown committed
6891 6892 6893 6894

			lock_type = TL_READ_NO_INSERT;
		}

unknown's avatar
unknown committed
6895
		/* If we are not doing a LOCK TABLE, DISCARD/IMPORT
unknown's avatar
unknown committed
6896
		TABLESPACE or TRUNCATE TABLE then allow multiple
unknown's avatar
unknown committed
6897 6898
		writers. Note that ALTER TABLE uses a TL_WRITE_ALLOW_READ
		< TL_WRITE_CONCURRENT_INSERT.
6899

unknown's avatar
unknown committed
6900 6901 6902 6903
		We especially allow multiple writers if MySQL is at the
		start of a stored procedure call (SQLCOM_CALL) or a
		stored function call (MySQL does have thd->in_lock_tables
		TRUE there). */
6904

unknown's avatar
foo1  
unknown committed
6905
    		if ((lock_type >= TL_WRITE_CONCURRENT_INSERT
6906
		    && lock_type <= TL_WRITE)
unknown's avatar
unknown committed
6907
		    && !(thd->in_lock_tables
unknown's avatar
unknown committed
6908
			    && thd->lex->sql_command == SQLCOM_LOCK_TABLES)
6909
		    && !thd->tablespace_op
unknown's avatar
merged  
unknown committed
6910
		    && thd->lex->sql_command != SQLCOM_TRUNCATE
6911
		    && thd->lex->sql_command != SQLCOM_OPTIMIZE
unknown's avatar
unknown committed
6912 6913 6914 6915 6916 6917 6918 6919 6920 6921 6922
#ifdef __WIN__
                /* 
                   for alter table on win32 for succesfull operation 
                   completion it is used TL_WRITE(=10) lock instead of
                   TL_WRITE_ALLOW_READ(=6), however here in innodb handler
                   TL_WRITE is lifted to TL_WRITE_ALLOW_WRITE, which causes
                   race condition when several clients do alter table 
                   simultaneously (bug #17264). This fix avoids the problem.
                */
                    && thd->lex->sql_command != SQLCOM_ALTER_TABLE
#endif
6923
		    && thd->lex->sql_command != SQLCOM_CREATE_TABLE) {
unknown's avatar
unknown committed
6924 6925

			lock_type = TL_WRITE_ALLOW_WRITE;
6926 6927
      		}

unknown's avatar
unknown committed
6928 6929 6930 6931
		/* 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
unknown's avatar
unknown committed
6932 6933 6934 6935 6936 6937
		concurrent inserts to t2.

		We especially allow concurrent inserts if MySQL is at the
		start of a stored procedure call (SQLCOM_CALL)
		(MySQL does have thd->in_lock_tables TRUE there). */

unknown's avatar
unknown committed
6938
		if (lock_type == TL_READ_NO_INSERT
unknown's avatar
unknown committed
6939 6940
			&& (!thd->in_lock_tables
				|| thd->lex->sql_command == SQLCOM_CALL)) {
unknown's avatar
unknown committed
6941

unknown's avatar
unknown committed
6942 6943
			lock_type = TL_READ;
		}
6944

unknown's avatar
unknown committed
6945 6946 6947 6948
		lock.type = lock_type;
	}

	*to++= &lock;
6949

6950 6951 6952
	return(to);
}

6953
/***********************************************************************
unknown's avatar
unknown committed
6954 6955
This function initializes the auto-inc counter if it has not been
initialized yet. This function does not change the value of the auto-inc
unknown's avatar
unknown committed
6956
counter if it already has been initialized. In parameter ret returns
unknown's avatar
unknown committed
6957
the value of the auto-inc counter. */
6958

unknown's avatar
unknown committed
6959 6960 6961
int
ha_innobase::innobase_read_and_init_auto_inc(
/*=========================================*/
unknown's avatar
unknown committed
6962 6963
				/* out: 0 or error code: deadlock or lock wait
				timeout */
unknown's avatar
unknown committed
6964
	longlong*	ret)	/* out: auto-inc value */
6965
{
unknown's avatar
unknown committed
6966 6967
	row_prebuilt_t* prebuilt	= (row_prebuilt_t*) innobase_prebuilt;
	longlong	auto_inc;
unknown's avatar
unknown committed
6968 6969
	ulint		old_select_lock_type;
	ibool		trx_was_not_started	= FALSE;
unknown's avatar
unknown committed
6970
	int		error;
6971

unknown's avatar
unknown committed
6972
	ut_a(prebuilt);
unknown's avatar
unknown committed
6973
	ut_a(prebuilt->trx ==
unknown's avatar
unknown committed
6974
		(trx_t*) current_thd->ha_data[innobase_hton.slot]);
unknown's avatar
unknown committed
6975
	ut_a(prebuilt->table);
unknown's avatar
unknown committed
6976

unknown's avatar
unknown committed
6977 6978 6979 6980
	if (prebuilt->trx->conc_state == TRX_NOT_STARTED) {
		trx_was_not_started = TRUE;
	}

unknown's avatar
unknown committed
6981 6982 6983 6984 6985
	/* In case MySQL calls this in the middle of a SELECT query, release
	possible adaptive hash latch to avoid deadlocks of threads */

	trx_search_latch_release_if_reserved(prebuilt->trx);

unknown's avatar
unknown committed
6986
	auto_inc = dict_table_autoinc_read(prebuilt->table);
unknown's avatar
unknown committed
6987

unknown's avatar
unknown committed
6988 6989 6990
	if (auto_inc != 0) {
		/* Already initialized */
		*ret = auto_inc;
unknown's avatar
unknown committed
6991

unknown's avatar
unknown committed
6992 6993 6994
		error = 0;

		goto func_exit_early;
unknown's avatar
unknown committed
6995
	}
6996

unknown's avatar
unknown committed
6997
	error = row_lock_table_autoinc_for_mysql(prebuilt);
unknown's avatar
unknown committed
6998

unknown's avatar
unknown committed
6999 7000
	if (error != DB_SUCCESS) {
		error = convert_error_code_to_mysql(error, user_thd);
7001

unknown's avatar
unknown committed
7002
		goto func_exit_early;
unknown's avatar
unknown committed
7003
	}
unknown's avatar
unknown committed
7004

unknown's avatar
unknown committed
7005 7006
	/* Check again if someone has initialized the counter meanwhile */
	auto_inc = dict_table_autoinc_read(prebuilt->table);
unknown's avatar
unknown committed
7007

unknown's avatar
unknown committed
7008 7009
	if (auto_inc != 0) {
		*ret = auto_inc;
unknown's avatar
unknown committed
7010

unknown's avatar
unknown committed
7011 7012 7013
		error = 0;

		goto func_exit_early;
unknown's avatar
unknown committed
7014
	}
7015

unknown's avatar
unknown committed
7016 7017
	(void) extra(HA_EXTRA_KEYREAD);
	index_init(table->s->next_number_index, 1);
unknown's avatar
unknown committed
7018

unknown's avatar
unknown committed
7019 7020 7021 7022 7023 7024
	/* Starting from 5.0.9, we use a consistent read to read the auto-inc
	column maximum value. This eliminates the spurious deadlocks caused
	by the row X-lock that we previously used. Note the following flaw
	in our algorithm: if some other user meanwhile UPDATEs the auto-inc
	column, our consistent read will not return the largest value. We
	accept this flaw, since the deadlocks were a bigger trouble. */
7025

unknown's avatar
unknown committed
7026 7027
	/* Fetch all the columns in the key */

unknown's avatar
unknown committed
7028
	prebuilt->hint_need_to_fetch_extra_cols = ROW_RETRIEVE_ALL_COLS;
7029

unknown's avatar
unknown committed
7030
	old_select_lock_type = prebuilt->select_lock_type;
unknown's avatar
unknown committed
7031
	prebuilt->select_lock_type = LOCK_NONE;
unknown's avatar
unknown committed
7032 7033 7034 7035 7036

	/* Eliminate an InnoDB error print that happens when we try to SELECT
	from a table when no table has been locked in ::external_lock(). */
	prebuilt->trx->n_mysql_tables_in_use++;

unknown's avatar
unknown committed
7037
	error = index_last(table->record[1]);
7038

unknown's avatar
unknown committed
7039
	prebuilt->trx->n_mysql_tables_in_use--;
unknown's avatar
unknown committed
7040
	prebuilt->select_lock_type = old_select_lock_type;
unknown's avatar
unknown committed
7041

unknown's avatar
unknown committed
7042
	if (error) {
unknown's avatar
unknown committed
7043 7044 7045 7046 7047 7048
		if (error == HA_ERR_END_OF_FILE) {
			/* The table was empty, initialize to 1 */
			auto_inc = 1;

			error = 0;
		} else {
unknown's avatar
unknown committed
7049
			/* This should not happen in a consistent read */
7050 7051
		  sql_print_error("Consistent read of auto-inc column "
				  "returned %lu", (ulong) error);
unknown's avatar
unknown committed
7052
			auto_inc = -1;
unknown's avatar
unknown committed
7053

unknown's avatar
unknown committed
7054 7055 7056
			goto func_exit;
		}
	} else {
unknown's avatar
unknown committed
7057 7058 7059 7060 7061 7062
		/* Initialize to max(col) + 1; we use
		'found_next_number_field' below because MySQL in SHOW TABLE
		STATUS does not seem to set 'next_number_field'. The comment
		in table.h says that 'next_number_field' is set when it is
		'active'. */

unknown's avatar
unknown committed
7063 7064 7065
		auto_inc = (longlong) table->found_next_number_field->
				val_int_offset(table->s->rec_buff_length) + 1;
	}
7066

unknown's avatar
unknown committed
7067 7068 7069
	dict_table_autoinc_initialize(prebuilt->table, auto_inc);

func_exit:
unknown's avatar
unknown committed
7070
	(void) extra(HA_EXTRA_NO_KEYREAD);
7071

unknown's avatar
unknown committed
7072 7073 7074 7075
	index_end();

	*ret = auto_inc;

unknown's avatar
unknown committed
7076 7077
func_exit_early:
	/* Since MySQL does not seem to call autocommit after SHOW TABLE
unknown's avatar
unknown committed
7078
	STATUS (even if we would register the trx here), we commit our
unknown's avatar
unknown committed
7079
	transaction here if it was started here. This is to eliminate a
unknown's avatar
unknown committed
7080 7081 7082
	dangling transaction. If the user had AUTOCOMMIT=0, then SHOW
	TABLE STATUS does leave a dangling transaction if the user does not
	himself call COMMIT. */
unknown's avatar
unknown committed
7083 7084 7085 7086 7087 7088

	if (trx_was_not_started) {

		innobase_commit_low(prebuilt->trx);
	}

unknown's avatar
unknown committed
7089
	return(error);
unknown's avatar
unknown committed
7090 7091
}

7092
/*******************************************************************************
unknown's avatar
unknown committed
7093 7094 7095
This function initializes the auto-inc counter if it has not been
initialized yet. This function does not change the value of the auto-inc
counter if it already has been initialized. Returns the value of the
7096 7097 7098
auto-inc counter in *first_value, and ULONGLONG_MAX in *nb_reserved_values (as
we have a table-level lock). offset, increment, nb_desired_values are ignored.
*first_value is set to -1 if error (deadlock or lock wait timeout)            */
unknown's avatar
unknown committed
7099

7100 7101 7102 7103 7104 7105 7106
void ha_innobase::get_auto_increment(
/*=================================*/
        ulonglong offset,              /* in */
        ulonglong increment,           /* in */
        ulonglong nb_desired_values,   /* in */
        ulonglong *first_value,        /* out */
        ulonglong *nb_reserved_values) /* out */
unknown's avatar
unknown committed
7107
{
unknown's avatar
unknown committed
7108 7109 7110
	longlong	nr;
	int		error;

unknown's avatar
unknown committed
7111 7112 7113
	error = innobase_read_and_init_auto_inc(&nr);

	if (error) {
unknown's avatar
unknown committed
7114 7115 7116
		/* This should never happen in the current (5.0.6) code, since
		we call this function only after the counter has been
		initialized. */
unknown's avatar
unknown committed
7117

unknown's avatar
unknown committed
7118
		ut_print_timestamp(stderr);
7119 7120
		sql_print_error("Error %lu in ::get_auto_increment()",
				(ulong) error);
7121 7122
                *first_value= (~(ulonglong) 0);
		return;
unknown's avatar
unknown committed
7123
	}
7124

7125 7126 7127
        *first_value= (ulonglong) nr;
        /* table-level autoinc lock reserves up to +inf */
        *nb_reserved_values= ULONGLONG_MAX;
7128 7129
}

7130 7131
/* See comment in handler.h */
int
unknown's avatar
unknown committed
7132
ha_innobase::reset_auto_increment(ulonglong value)
7133 7134 7135 7136
{
	DBUG_ENTER("ha_innobase::reset_auto_increment");

	row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
unknown's avatar
unknown committed
7137
	int		error;
7138 7139 7140 7141 7142 7143 7144

	error = row_lock_table_autoinc_for_mysql(prebuilt);

	if (error != DB_SUCCESS) {
		error = convert_error_code_to_mysql(error, user_thd);

		DBUG_RETURN(error);
unknown's avatar
unknown committed
7145
	}
7146

unknown's avatar
unknown committed
7147
	dict_table_autoinc_initialize(prebuilt->table, value);
7148 7149 7150 7151

	DBUG_RETURN(0);
}

7152 7153 7154 7155
/* See comment in handler.cc */
bool
ha_innobase::get_error_message(int error, String *buf)
{
unknown's avatar
unknown committed
7156
	trx_t*	trx = check_trx_exists(current_thd);
7157 7158 7159 7160 7161 7162 7163

	buf->copy(trx->detailed_error, strlen(trx->detailed_error),
		system_charset_info);

	return FALSE;
}

unknown's avatar
unknown committed
7164 7165 7166 7167
/***********************************************************************
Compares two 'refs'. A 'ref' is the (internal) primary key value of the row.
If there is no explicitly declared non-null unique key or a primary key, then
InnoDB internally uses the row id as the primary key. */
unknown's avatar
unknown committed
7168

7169 7170
int
ha_innobase::cmp_ref(
unknown's avatar
unknown committed
7171 7172 7173 7174 7175 7176 7177
/*=================*/
				/* out: < 0 if ref1 < ref2, 0 if equal, else
				> 0 */
	const mysql_byte* ref1,	/* in: an (internal) primary key value in the
				MySQL key value format */
	const mysql_byte* ref2)	/* in: an (internal) primary key value in the
				MySQL key value format */
7178
{
unknown's avatar
unknown committed
7179
	row_prebuilt_t*	prebuilt = (row_prebuilt_t*) innobase_prebuilt;
7180
	enum_field_types mysql_type;
unknown's avatar
unknown committed
7181 7182 7183 7184 7185
	Field*		field;
	KEY_PART_INFO*	key_part;
	KEY_PART_INFO*	key_part_end;
	uint		len1;
	uint		len2;
unknown's avatar
unknown committed
7186
	int		result;
unknown's avatar
unknown committed
7187 7188 7189 7190 7191 7192 7193 7194 7195 7196 7197 7198 7199 7200 7201

	if (prebuilt->clust_index_was_generated) {
		/* The 'ref' is an InnoDB row id */

		return(memcmp(ref1, ref2, DATA_ROW_ID_LEN));
	}

	/* Do a type-aware comparison of primary key fields. PK fields
	are always NOT NULL, so no checks for NULL are performed. */

	key_part = table->key_info[table->s->primary_key].key_part;

	key_part_end = key_part
			+ table->key_info[table->s->primary_key].key_parts;

7202 7203 7204
	for (; key_part != key_part_end; ++key_part) {
		field = key_part->field;
		mysql_type = field->type();
unknown's avatar
unknown committed
7205

7206
		if (mysql_type == FIELD_TYPE_TINY_BLOB
unknown's avatar
unknown committed
7207 7208 7209 7210
			|| mysql_type == FIELD_TYPE_MEDIUM_BLOB
			|| mysql_type == FIELD_TYPE_BLOB
			|| mysql_type == FIELD_TYPE_LONG_BLOB) {

unknown's avatar
unknown committed
7211 7212 7213 7214 7215 7216
			/* In the MySQL key value format, a column prefix of
			a BLOB is preceded by a 2-byte length field */

			len1 = innobase_read_from_2_little_endian(ref1);
			len2 = innobase_read_from_2_little_endian(ref2);

7217 7218
			ref1 += 2;
			ref2 += 2;
unknown's avatar
unknown committed
7219
			result = ((Field_blob*)field)->cmp(
unknown's avatar
unknown committed
7220 7221
				(const char*)ref1, len1,
				(const char*)ref2, len2);
7222
		} else {
7223
			result = field->key_cmp(ref1, ref2);
unknown's avatar
unknown committed
7224 7225 7226 7227 7228
		}

		if (result) {

			return(result);
7229 7230
		}

7231 7232
		ref1 += key_part->store_length;
		ref2 += key_part->store_length;
7233
	}
unknown's avatar
unknown committed
7234 7235

	return(0);
7236 7237
}

unknown's avatar
unknown committed
7238 7239
char*
ha_innobase::get_mysql_bin_log_name()
unknown's avatar
unknown committed
7240
{
unknown's avatar
unknown committed
7241
	return(trx_sys_mysql_bin_log_name);
unknown's avatar
unknown committed
7242 7243
}

unknown's avatar
unknown committed
7244 7245
ulonglong
ha_innobase::get_mysql_bin_log_pos()
unknown's avatar
unknown committed
7246
{
unknown's avatar
unknown committed
7247
	/* trx... is ib_longlong, which is a typedef for a 64-bit integer
unknown's avatar
unknown committed
7248 7249
	(__int64 or longlong) so it's ok to cast it to ulonglong. */

unknown's avatar
unknown committed
7250
	return(trx_sys_mysql_bin_log_pos);
unknown's avatar
unknown committed
7251 7252
}

7253
extern "C" {
7254
/**********************************************************************
unknown's avatar
unknown committed
7255 7256 7257 7258 7259 7260 7261
This function is used to find the storage length in bytes of the first n
characters for prefix indexes using a multibyte character set. The function
finds charset information and returns length of prefix_len characters in the
index field in bytes.

NOTE: the prototype of this function is copied to data0type.c! If you change
this function, you MUST change also data0type.c! */
7262

unknown's avatar
unknown committed
7263 7264 7265 7266 7267
ulint
innobase_get_at_most_n_mbchars(
/*===========================*/
				/* out: number of bytes occupied by the first
				n characters */
7268
	ulint charset_id,	/* in: character set id */
unknown's avatar
unknown committed
7269 7270 7271
	ulint prefix_len,	/* in: prefix length in bytes of the index
				(this has to be divided by mbmaxlen to get the
				number of CHARACTERS n in the prefix) */
unknown's avatar
unknown committed
7272
	ulint data_len,		/* in: length of the string in bytes */
unknown's avatar
unknown committed
7273
	const char* str)	/* in: character string */
7274
{
7275
	ulint char_length;	/* character length in bytes */
unknown's avatar
unknown committed
7276
	ulint n_chars;		/* number of characters in prefix */
7277
	CHARSET_INFO* charset;	/* charset used in the field */
7278

unknown's avatar
unknown committed
7279
	charset = get_charset((uint) charset_id, MYF(MY_WME));
7280

7281 7282
	ut_ad(charset);
	ut_ad(charset->mbmaxlen);
7283

unknown's avatar
unknown committed
7284
	/* Calculate how many characters at most the prefix index contains */
7285

unknown's avatar
unknown committed
7286
	n_chars = prefix_len / charset->mbmaxlen;
7287

unknown's avatar
unknown committed
7288 7289 7290
	/* If the charset is multi-byte, then we must find the length of the
	first at most n chars in the string. If the string contains less
	characters than n, then we return the length to the end of the last
unknown's avatar
unknown committed
7291
	character. */
7292

unknown's avatar
unknown committed
7293 7294
	if (charset->mbmaxlen > 1) {
		/* my_charpos() returns the byte length of the first n_chars
unknown's avatar
unknown committed
7295 7296 7297 7298 7299 7300 7301 7302 7303 7304 7305 7306 7307 7308 7309 7310
		characters, or a value bigger than the length of str, if
		there were not enough full characters in str.

		Why does the code below work:
		Suppose that we are looking for n UTF-8 characters.

		1) If the string is long enough, then the prefix contains at
		least n complete UTF-8 characters + maybe some extra
		characters + an incomplete UTF-8 character. No problem in
		this case. The function returns the pointer to the
		end of the nth character.

		2) If the string is not long enough, then the string contains
		the complete value of a column, that is, only complete UTF-8
		characters, and we can store in the column prefix index the
		whole string. */
unknown's avatar
unknown committed
7311

unknown's avatar
unknown committed
7312
		char_length = my_charpos(charset, str,
unknown's avatar
unknown committed
7313
						str + data_len, (int) n_chars);
unknown's avatar
unknown committed
7314 7315
		if (char_length > data_len) {
			char_length = data_len;
unknown's avatar
unknown committed
7316
		}
unknown's avatar
unknown committed
7317
	} else {
unknown's avatar
unknown committed
7318 7319 7320 7321 7322
		if (data_len < prefix_len) {
			char_length = data_len;
		} else {
			char_length = prefix_len;
		}
7323
	}
7324

unknown's avatar
unknown committed
7325
	return(char_length);
7326 7327 7328
}
}

7329 7330
extern "C" {
/**********************************************************************
unknown's avatar
unknown committed
7331
This function returns true if
7332 7333

1) SQL-query in the current thread
unknown's avatar
unknown committed
7334
is either REPLACE or LOAD DATA INFILE REPLACE.
7335 7336 7337 7338

2) SQL-query in the current thread
is INSERT ON DUPLICATE KEY UPDATE.

unknown's avatar
unknown committed
7339
NOTE that /mysql/innobase/row/row0ins.c must contain the
7340 7341 7342
prototype for this function ! */

ibool
7343
innobase_query_is_update(void)
unknown's avatar
unknown committed
7344
/*==========================*/
7345 7346
{
	THD*	thd;
unknown's avatar
unknown committed
7347

7348
	thd = (THD *)innobase_current_thd();
unknown's avatar
unknown committed
7349

unknown's avatar
unknown committed
7350
	if (thd->lex->sql_command == SQLCOM_REPLACE ||
unknown's avatar
unknown committed
7351 7352 7353
		thd->lex->sql_command == SQLCOM_REPLACE_SELECT ||
		(thd->lex->sql_command == SQLCOM_LOAD &&
			thd->lex->duplicates == DUP_REPLACE)) {
unknown's avatar
unknown committed
7354 7355

		return(1);
7356
	}
7357

unknown's avatar
unknown committed
7358
	if (thd->lex->sql_command == SQLCOM_INSERT &&
unknown's avatar
unknown committed
7359
		thd->lex->duplicates  == DUP_UPDATE) {
unknown's avatar
unknown committed
7360 7361

		return(1);
7362 7363
	}

unknown's avatar
unknown committed
7364
	return(0);
7365
}
unknown's avatar
unknown committed
7366 7367
}

7368 7369 7370
/***********************************************************************
This function is used to prepare X/Open XA distributed transaction   */

unknown's avatar
unknown committed
7371
int
7372 7373
innobase_xa_prepare(
/*================*/
7374 7375 7376 7377 7378 7379 7380
			/* out: 0 or error number */
	THD*	thd,	/* in: handle to the MySQL thread of the user
			whose XA transaction should be prepared */
	bool	all)	/* in: TRUE - commit transaction
			FALSE - the current SQL statement ended */
{
	int error = 0;
unknown's avatar
unknown committed
7381
	trx_t* trx = check_trx_exists(thd);
unknown's avatar
unknown committed
7382

unknown's avatar
unknown committed
7383
	if (thd->lex->sql_command != SQLCOM_XA_PREPARE) {
unknown's avatar
unknown committed
7384

unknown's avatar
unknown committed
7385 7386
		/* For ibbackup to work the order of transactions in binlog
		and InnoDB must be the same. Consider the situation
unknown's avatar
unknown committed
7387

unknown's avatar
unknown committed
7388 7389 7390 7391
		  thread1> prepare; write to binlog; ...
			  <context switch>
		  thread2> prepare; write to binlog; commit
		  thread1>			     ... commit
unknown's avatar
unknown committed
7392

unknown's avatar
unknown committed
7393 7394
		To ensure this will not happen we're taking the mutex on
		prepare, and releasing it on commit.
unknown's avatar
unknown committed
7395

unknown's avatar
unknown committed
7396 7397 7398 7399 7400 7401 7402 7403 7404 7405 7406
		Note: only do it for normal commits, done via ha_commit_trans.
		If 2pc protocol is executed by external transaction
		coordinator, it will be just a regular MySQL client
		executing XA PREPARE and XA COMMIT commands.
		In this case we cannot know how many minutes or hours
		will be between XA PREPARE and XA COMMIT, and we don't want
		to block for undefined period of time.
		*/
		pthread_mutex_lock(&prepare_commit_mutex);
		trx->active_trans = 2;
	}
7407

7408 7409 7410 7411 7412
	if (!thd->variables.innodb_support_xa) {

		return(0);
	}

unknown's avatar
unknown committed
7413
	trx->xid=thd->transaction.xid_state.xid;
7414 7415 7416 7417 7418 7419 7420 7421 7422

	/* Release a possible FIFO ticket and search latch. Since we will
	reserve the kernel mutex, we have to release the search system latch
	first to obey the latching order. */

	innobase_release_stat_resources(trx);

	if (trx->active_trans == 0 && trx->conc_state != TRX_NOT_STARTED) {

7423 7424
	  sql_print_error("trx->active_trans == 0, but trx->conc_state != "
			  "TRX_NOT_STARTED");
7425 7426
	}

unknown's avatar
unknown committed
7427
	if (all
unknown's avatar
unknown committed
7428
		|| (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))) {
unknown's avatar
unknown committed
7429

unknown's avatar
unknown committed
7430 7431
		/* We were instructed to prepare the whole transaction, or
		this is an SQL statement end and autocommit is on */
7432

unknown's avatar
unknown committed
7433
		ut_ad(trx->active_trans);
unknown's avatar
unknown committed
7434

unknown's avatar
unknown committed
7435
		error = (int) trx_prepare_for_mysql(trx);
7436
	} else {
unknown's avatar
unknown committed
7437
		/* We just mark the SQL statement ended and do not do a
7438 7439 7440 7441 7442
		transaction prepare */

		if (trx->auto_inc_lock) {
			/* If we had reserved the auto-inc lock for some
			table in this SQL statement we release it now */
unknown's avatar
unknown committed
7443

7444 7445 7446 7447 7448 7449 7450 7451 7452 7453 7454 7455 7456 7457
			row_unlock_table_autoinc_for_mysql(trx);
		}
		/* Store the current undo_no of the transaction so that we
		know where to roll back if we have to roll back the next
		SQL statement */

		trx_mark_sql_stat_end(trx);
	}

	/* Tell the InnoDB server that there might be work for utility
	threads: */

	srv_active_wake_master_thread();

unknown's avatar
unknown committed
7458
	return error;
7459 7460 7461 7462 7463
}

/***********************************************************************
This function is used to recover X/Open XA distributed transactions   */

unknown's avatar
unknown committed
7464
int
7465 7466
innobase_xa_recover(
/*================*/
unknown's avatar
unknown committed
7467
				/* out: number of prepared transactions
7468
				stored in xid_list */
unknown's avatar
unknown committed
7469
	XID*	xid_list,	/* in/out: prepared transactions */
7470 7471 7472
	uint	len)		/* in: number of slots in xid_list */
{
	if (len == 0 || xid_list == NULL) {
unknown's avatar
unknown committed
7473 7474

		return(0);
7475 7476
	}

unknown's avatar
unknown committed
7477
	return(trx_recover_for_mysql(xid_list, len));
7478 7479 7480 7481 7482 7483
}

/***********************************************************************
This function is used to commit one X/Open XA distributed transaction
which is in the prepared state */

unknown's avatar
unknown committed
7484
int
7485 7486
innobase_commit_by_xid(
/*===================*/
7487
			/* out: 0 or error number */
unknown's avatar
unknown committed
7488
	XID*	xid)	/* in: X/Open XA transaction identification */
7489 7490 7491 7492 7493 7494 7495
{
	trx_t*	trx;

	trx = trx_get_trx_by_xid(xid);

	if (trx) {
		innobase_commit_low(trx);
unknown's avatar
unknown committed
7496

7497 7498 7499 7500 7501 7502 7503 7504 7505 7506
		return(XA_OK);
	} else {
		return(XAER_NOTA);
	}
}

/***********************************************************************
This function is used to rollback one X/Open XA distributed transaction
which is in the prepared state */

unknown's avatar
unknown committed
7507
int
7508 7509
innobase_rollback_by_xid(
/*=====================*/
7510
			/* out: 0 or error number */
unknown's avatar
unknown committed
7511
	XID	*xid)	/* in: X/Open XA transaction identification */
7512 7513 7514 7515 7516 7517 7518 7519 7520 7521 7522 7523
{
	trx_t*	trx;

	trx = trx_get_trx_by_xid(xid);

	if (trx) {
		return(innobase_rollback_trx(trx));
	} else {
		return(XAER_NOTA);
	}
}

7524
/***********************************************************************
unknown's avatar
unknown committed
7525 7526
Create a consistent view for a cursor based on current transaction
which is created if the corresponding MySQL thread still lacks one.
unknown's avatar
unknown committed
7527
This consistent view is then used inside of MySQL when accessing records
unknown's avatar
unknown committed
7528
using a cursor. */
7529 7530 7531 7532 7533 7534 7535 7536 7537 7538 7539

void*
innobase_create_cursor_view(void)
/*=============================*/
			/* out: Pointer to cursor view or NULL */
{
	return(read_cursor_view_create_for_mysql(
					check_trx_exists(current_thd)));
}

/***********************************************************************
unknown's avatar
unknown committed
7540
Close the given consistent cursor view of a transaction and restore
unknown's avatar
unknown committed
7541
global read view to a transaction read view. Transaction is created if the
unknown's avatar
unknown committed
7542
corresponding MySQL thread still lacks one. */
7543 7544 7545 7546 7547 7548 7549 7550 7551 7552 7553

void
innobase_close_cursor_view(
/*=======================*/
	void*	curview)/* in: Consistent read view to be closed */
{
	read_cursor_view_close_for_mysql(check_trx_exists(current_thd),
						(cursor_view_t*) curview);
}

/***********************************************************************
unknown's avatar
unknown committed
7554 7555
Set the given consistent cursor view to a transaction which is created
if the corresponding MySQL thread still lacks one. If the given
unknown's avatar
unknown committed
7556 7557
consistent cursor view is NULL global read view of a transaction is
restored to a transaction read view. */
7558 7559 7560 7561

void
innobase_set_cursor_view(
/*=====================*/
unknown's avatar
unknown committed
7562
	void*	curview)/* in: Consistent cursor view to be set */
7563
{
unknown's avatar
unknown committed
7564
	read_cursor_set_for_mysql(check_trx_exists(current_thd),
7565 7566 7567
						(cursor_view_t*) curview);
}

unknown's avatar
unknown committed
7568

unknown's avatar
unknown committed
7569 7570 7571
bool ha_innobase::check_if_incompatible_data(
	HA_CREATE_INFO*	info,
	uint		table_changes)
unknown's avatar
unknown committed
7572
{
unknown's avatar
unknown committed
7573
	if (table_changes != IS_EQUAL_YES) {
unknown's avatar
unknown committed
7574

unknown's avatar
unknown committed
7575 7576 7577 7578 7579 7580
		return COMPATIBLE_DATA_NO;
	}

	/* Check that auto_increment value was not changed */
	if ((info->used_fields & HA_CREATE_USED_AUTO) &&
		info->auto_increment_value != 0) {
unknown's avatar
unknown committed
7581

unknown's avatar
unknown committed
7582 7583 7584 7585 7586 7587 7588 7589 7590 7591 7592 7593
		return COMPATIBLE_DATA_NO;
	}

	/* Check that row format didn't change */
	if ((info->used_fields & HA_CREATE_USED_AUTO) &&
		get_row_type() != info->row_type) {

		return COMPATIBLE_DATA_NO;
	}

	return COMPATIBLE_DATA_YES;
}
unknown's avatar
WL#3201  
unknown committed
7594

unknown's avatar
unknown committed
7595 7596 7597 7598 7599 7600 7601 7602 7603 7604 7605 7606 7607
static int show_innodb_vars(THD *thd, SHOW_VAR *var, char *buff)
{
  innodb_export_status();
  var->type= SHOW_ARRAY;
  var->value= (char *) &innodb_status_variables;
  return 0;
}

SHOW_VAR innodb_status_variables_export[]= {
  {"Innodb",                   (char*) &show_innodb_vars, SHOW_FUNC},
  {NullS, NullS, SHOW_LONG}
};

unknown's avatar
unknown committed
7608 7609
struct st_mysql_storage_engine innobase_storage_engine=
{ MYSQL_HANDLERTON_INTERFACE_VERSION, &innobase_hton};
unknown's avatar
WL#3201  
unknown committed
7610 7611 7612 7613

mysql_declare_plugin(innobase)
{
  MYSQL_STORAGE_ENGINE_PLUGIN,
unknown's avatar
unknown committed
7614
  &innobase_storage_engine,
unknown's avatar
unknown committed
7615
  innobase_hton_name,
unknown's avatar
WL#3201  
unknown committed
7616
  "Innobase OY",
unknown's avatar
unknown committed
7617 7618
  "Supports transactions, row-level locking, and foreign keys",
  innobase_init, /* Plugin Init */
unknown's avatar
WL#3201  
unknown committed
7619 7620
  NULL, /* Plugin Deinit */
  0x0100 /* 1.0 */,
7621 7622 7623
  innodb_status_variables_export,/* status variables             */
  NULL,                       /* system variables                */
  NULL                        /* config options                  */
unknown's avatar
WL#3201  
unknown committed
7624 7625 7626
}
mysql_declare_plugin_end;

7627
#endif