ha_innodb.cc 234 KB
Newer Older
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1
/* Copyright (C) 2000-2005 MySQL AB & Innobase Oy
2

3 4
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
5
   the Free Software Foundation; version 2 of the License.
6

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

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

16
/* This file defines the InnoDB handler: the interface between MySQL and InnoDB
17
NOTE: You can only use noninlined InnoDB functions in this file, because we
18
have disabled the InnoDB inlining in this file. */
19

20
/* TODO list for the InnoDB handler in 5.0:
21
  - Remove the flag trx->active_trans and look at trx->conc_state
serg@serg.mylan's avatar
serg@serg.mylan committed
22
  - fix savepoint functions to use savepoint storage area
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
23 24 25
  - 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?
26
*/
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
27

28
#ifdef USE_PRAGMA_IMPLEMENTATION
29 30 31
#pragma implementation				// gcc: Class implementation
#endif

32
#include <mysql_priv.h>
33
#include <mysqld_error.h>
34

35 36 37
#include <m_ctype.h>
#include <hash.h>
#include <myisampack.h>
marko@hundin.mysql.fi's avatar
marko@hundin.mysql.fi committed
38
#include <mysys_err.h>
39
#include <my_sys.h>
40
#include "ha_innodb.h"
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
#include <mysql/plugin.h>

#ifndef MYSQL_SERVER
/* This is needed because of Bug #3596.  Let us hope that pthread_mutex_t
is defined the same in both builds: the MySQL server and the InnoDB plugin. */
extern pthread_mutex_t LOCK_thread_count;
#endif /* MYSQL_SERVER */

/** to protect innobase_open_files */
static pthread_mutex_t innobase_share_mutex;
/** to force correct commit order in binlog */
static pthread_mutex_t prepare_commit_mutex;
static ulong commit_threads = 0;
static pthread_mutex_t commit_threads_m;
static pthread_cond_t commit_cond;
static pthread_mutex_t commit_cond_m;
static bool innodb_inited = 0;
58

59 60 61 62
/*
  This needs to exist until the query cache callback is removed
  or learns to pass hton.
*/
63
static handlerton *innodb_hton_ptr;
64

65 66
#define INSIDE_HA_INNOBASE_CC

67
/* Include necessary InnoDB headers */
68
extern "C" {
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
#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"
91
#include "../storage/innobase/include/thr0loc.h"
92
#include "../storage/innobase/include/ha_prototypes.h"
93 94
}

95 96 97 98
static const long AUTOINC_OLD_STYLE_LOCKING = 0;
static const long AUTOINC_NEW_STYLE_LOCKING = 1;
static const long AUTOINC_NO_LOCKING = 2;

99
static long innobase_mirrored_log_groups, innobase_log_files_in_group,
100 101 102
	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,
103
	innobase_open_files, innobase_autoinc_lock_mode;
104

105
static long long innobase_buffer_pool_size, innobase_log_file_size;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
106

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
107 108
/* The default values for the following char* start-up parameters
are determined in innobase_init below: */
109

110 111 112
static char*	innobase_data_home_dir			= NULL;
static char*	innobase_data_file_path			= NULL;
static char*	innobase_log_group_home_dir		= NULL;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
113 114
/* The following has a misleading name: starting from 4.0.5, this also
affects Windows: */
115
static char*	innobase_unix_file_flush_method		= NULL;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
116 117 118 119

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

120
static ulong	innobase_fast_shutdown			= 1;
antony@ppcg5.local's avatar
antony@ppcg5.local committed
121
#ifdef UNIV_LOG_ARCHIVE
122 123 124 125 126 127 128 129 130 131
static my_bool	innobase_log_archive			= FALSE;
static char*	innobase_log_arch_dir			= NULL;
#endif /* UNIV_LOG_ARCHIVE */
static my_bool	innobase_use_doublewrite		= TRUE;
static my_bool	innobase_use_checksums			= TRUE;
static my_bool	innobase_file_per_table			= FALSE;
static my_bool	innobase_locks_unsafe_for_binlog	= FALSE;
static my_bool	innobase_rollback_on_timeout		= FALSE;
static my_bool	innobase_create_status_file		= FALSE;
static my_bool innobase_stats_on_metadata		= TRUE;
132
static my_bool	innobase_adaptive_hash_index	= TRUE;
133

134
static char*	internal_innobase_data_file_path	= NULL;
135

136
/* The following counter is used to convey information to InnoDB
137 138 139 140 141
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
142
static ulong	innobase_active_counter	= 0;
143

144
static HASH	innobase_open_tables;
145

146
#ifdef __NETWARE__	/* some special cleanup for NetWare */
147 148 149
bool nw_panic = FALSE;
#endif

150
static uchar* innobase_get_key(INNOBASE_SHARE *share, size_t *length,
151
	my_bool not_used __attribute__((unused)));
152 153
static INNOBASE_SHARE *get_share(const char *table_name);
static void free_share(INNOBASE_SHARE *share);
154 155 156 157 158 159 160 161
static int innobase_close_connection(handlerton *hton, THD* thd);
static int innobase_commit(handlerton *hton, THD* thd, bool all);
static int innobase_rollback(handlerton *hton, THD* thd, bool all);
static int innobase_rollback_to_savepoint(handlerton *hton, THD* thd, 
           void *savepoint);
static int innobase_savepoint(handlerton *hton, THD* thd, void *savepoint);
static int innobase_release_savepoint(handlerton *hton, THD* thd, 
           void *savepoint);
162 163
static handler *innobase_create_handler(handlerton *hton,
                                        TABLE_SHARE *table,
164
                                        MEM_ROOT *mem_root);
165

166
static const char innobase_hton_name[]= "InnoDB";
167

antony@ppcg5.local's avatar
antony@ppcg5.local committed
168 169 170 171 172 173 174 175 176 177 178

static MYSQL_THDVAR_BOOL(support_xa, PLUGIN_VAR_OPCMDARG,
  "Enable InnoDB support for the XA two-phase commit",
  /* check_func */ NULL, /* update_func */ NULL,
  /* default */ TRUE);

static MYSQL_THDVAR_BOOL(table_locks, PLUGIN_VAR_OPCMDARG,
  "Enable InnoDB locking in LOCK TABLES",
  /* check_func */ NULL, /* update_func */ NULL,
  /* default */ TRUE);

179 180 181
static handler *innobase_create_handler(handlerton *hton,
                                        TABLE_SHARE *table, 
                                        MEM_ROOT *mem_root)
182
{
183
  return new (mem_root) ha_innobase(hton, table);
184 185
}

186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
/***********************************************************************
This function is used to prepare X/Open XA distributed transaction   */
static
int
innobase_xa_prepare(
/*================*/
			/* out: 0 or error number */
	handlerton* hton,
	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 */
/***********************************************************************
This function is used to recover X/Open XA distributed transactions   */
static
int
innobase_xa_recover(
/*================*/
				/* out: number of prepared transactions
				stored in xid_list */
	handlerton* hton,
	XID*	xid_list,	/* in/out: prepared transactions */
	uint	len);		/* in: number of slots in xid_list */
/***********************************************************************
This function is used to commit one X/Open XA distributed transaction
which is in the prepared state */
static
int
innobase_commit_by_xid(
/*===================*/
			/* out: 0 or error number */
	handlerton* hton,
	XID*	xid);	/* in: X/Open XA transaction identification */
/***********************************************************************
This function is used to rollback one X/Open XA distributed transaction
which is in the prepared state */
static
int
innobase_rollback_by_xid(
/*=====================*/
			/* out: 0 or error number */
	handlerton* hton,
	XID	*xid);	/* in: X/Open XA transaction identification */
/***********************************************************************
Create a consistent view for a cursor based on current transaction
which is created if the corresponding MySQL thread still lacks one.
This consistent view is then used inside of MySQL when accessing records
using a cursor. */
static
void*
innobase_create_cursor_view(
/*========================*/
				/* out: pointer to cursor view or NULL */
	handlerton*	hton,	/* in: innobase hton */
	THD*		thd);	/* in: user thread handle */
/***********************************************************************
Set the given consistent cursor view to a transaction which is created
if the corresponding MySQL thread still lacks one. If the given
consistent cursor view is NULL global read view of a transaction is
restored to a transaction read view. */
static
void
innobase_set_cursor_view(
/*=====================*/
	handlerton* hton,
	THD*	thd,	/* in: user thread handle */
	void*	curview);/* in: Consistent cursor view to be set */
/***********************************************************************
Close the given consistent cursor view of a transaction and restore
global read view to a transaction read view. Transaction is created if the
corresponding MySQL thread still lacks one. */
static
void
innobase_close_cursor_view(
/*=======================*/
	handlerton* hton,
	THD*	thd,	/* in: user thread handle */
	void*	curview);/* in: Consistent read view to be closed */
/*********************************************************************
Removes all tables in the named database inside InnoDB. */
static
void
innobase_drop_database(
/*===================*/
			/* out: error number */
	handlerton* hton, /* in: handlerton of Innodb */
	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' */
/***********************************************************************
Closes an InnoDB database. */
static
int
innobase_end(handlerton *hton, ha_panic_function type);

/*********************************************************************
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. */
static
int
innobase_start_trx_and_assign_read_view(
/*====================================*/
			/* out: 0 */
	handlerton* hton, /* in: Innodb handlerton */ 
	THD*	thd);	/* in: MySQL thread handle of the user for whom
			the transaction should be committed */
/********************************************************************
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. */
static
bool
innobase_flush_logs(
/*================*/
				/* out: TRUE if error */
	handlerton*	hton);	/* in: InnoDB handlerton */

/****************************************************************************
Implements the SHOW INNODB STATUS command. Sends the output of the InnoDB
Monitor to the client. */
static
bool
innodb_show_status(
/*===============*/
	handlerton*	hton,	/* in: the innodb handlerton */
	THD*	thd,	/* in: the MySQL query thread of the caller */
	stat_print_fn *stat_print);
static
bool innobase_show_status(handlerton *hton, THD* thd, 
                          stat_print_fn* stat_print,
                          enum ha_stat_type stat_type);
319

320 321
/*********************************************************************
Commits a transaction in an InnoDB database. */
322
static
323 324 325 326 327
void
innobase_commit_low(
/*================*/
	trx_t*	trx);	/* in: transaction handle */

328
static SHOW_VAR innodb_status_variables[]= {
329
  {"buffer_pool_pages_data",
330
  (char*) &export_vars.innodb_buffer_pool_pages_data,	  SHOW_LONG},
331
  {"buffer_pool_pages_dirty",
332
  (char*) &export_vars.innodb_buffer_pool_pages_dirty,	  SHOW_LONG},
333 334 335
  {"buffer_pool_pages_flushed",
  (char*) &export_vars.innodb_buffer_pool_pages_flushed,  SHOW_LONG},
  {"buffer_pool_pages_free",
336
  (char*) &export_vars.innodb_buffer_pool_pages_free,	  SHOW_LONG},
337 338 339
  {"buffer_pool_pages_latched",
  (char*) &export_vars.innodb_buffer_pool_pages_latched,  SHOW_LONG},
  {"buffer_pool_pages_misc",
340
  (char*) &export_vars.innodb_buffer_pool_pages_misc,	  SHOW_LONG},
341
  {"buffer_pool_pages_total",
342
  (char*) &export_vars.innodb_buffer_pool_pages_total,	  SHOW_LONG},
343 344 345 346 347 348 349
  {"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",
350
  (char*) &export_vars.innodb_buffer_pool_reads,	  SHOW_LONG},
351
  {"buffer_pool_wait_free",
352
  (char*) &export_vars.innodb_buffer_pool_wait_free,	  SHOW_LONG},
353 354 355
  {"buffer_pool_write_requests",
  (char*) &export_vars.innodb_buffer_pool_write_requests, SHOW_LONG},
  {"data_fsyncs",
356
  (char*) &export_vars.innodb_data_fsyncs,		  SHOW_LONG},
357
  {"data_pending_fsyncs",
358
  (char*) &export_vars.innodb_data_pending_fsyncs,	  SHOW_LONG},
359
  {"data_pending_reads",
360
  (char*) &export_vars.innodb_data_pending_reads,	  SHOW_LONG},
361
  {"data_pending_writes",
362
  (char*) &export_vars.innodb_data_pending_writes,	  SHOW_LONG},
363
  {"data_read",
364
  (char*) &export_vars.innodb_data_read,		  SHOW_LONG},
365
  {"data_reads",
366
  (char*) &export_vars.innodb_data_reads,		  SHOW_LONG},
367
  {"data_writes",
368
  (char*) &export_vars.innodb_data_writes,		  SHOW_LONG},
369
  {"data_written",
370
  (char*) &export_vars.innodb_data_written,		  SHOW_LONG},
371
  {"dblwr_pages_written",
372
  (char*) &export_vars.innodb_dblwr_pages_written,	  SHOW_LONG},
373
  {"dblwr_writes",
374
  (char*) &export_vars.innodb_dblwr_writes,		  SHOW_LONG},
375
  {"log_waits",
376
  (char*) &export_vars.innodb_log_waits,		  SHOW_LONG},
377
  {"log_write_requests",
378
  (char*) &export_vars.innodb_log_write_requests,	  SHOW_LONG},
379
  {"log_writes",
380
  (char*) &export_vars.innodb_log_writes,		  SHOW_LONG},
381
  {"os_log_fsyncs",
382
  (char*) &export_vars.innodb_os_log_fsyncs,		  SHOW_LONG},
383
  {"os_log_pending_fsyncs",
384
  (char*) &export_vars.innodb_os_log_pending_fsyncs,	  SHOW_LONG},
385
  {"os_log_pending_writes",
386
  (char*) &export_vars.innodb_os_log_pending_writes,	  SHOW_LONG},
387
  {"os_log_written",
388
  (char*) &export_vars.innodb_os_log_written,		  SHOW_LONG},
389
  {"page_size",
390
  (char*) &export_vars.innodb_page_size,		  SHOW_LONG},
391
  {"pages_created",
392
  (char*) &export_vars.innodb_pages_created,		  SHOW_LONG},
393
  {"pages_read",
394
  (char*) &export_vars.innodb_pages_read,		  SHOW_LONG},
395
  {"pages_written",
396
  (char*) &export_vars.innodb_pages_written,		  SHOW_LONG},
vtkachenko@intelp4d.mysql.com's avatar
vtkachenko@intelp4d.mysql.com committed
397
  {"row_lock_current_waits",
398
  (char*) &export_vars.innodb_row_lock_current_waits,	  SHOW_LONG},
vtkachenko@intelp4d.mysql.com's avatar
vtkachenko@intelp4d.mysql.com committed
399
  {"row_lock_time",
400
  (char*) &export_vars.innodb_row_lock_time,		  SHOW_LONGLONG},
vtkachenko@intelp4d.mysql.com's avatar
vtkachenko@intelp4d.mysql.com committed
401
  {"row_lock_time_avg",
402
  (char*) &export_vars.innodb_row_lock_time_avg,	  SHOW_LONG},
paul@kite-hub.kitebird.com's avatar
paul@kite-hub.kitebird.com committed
403
  {"row_lock_time_max",
404
  (char*) &export_vars.innodb_row_lock_time_max,	  SHOW_LONG},
paul@kite-hub.kitebird.com's avatar
paul@kite-hub.kitebird.com committed
405
  {"row_lock_waits",
406
  (char*) &export_vars.innodb_row_lock_waits,		  SHOW_LONG},
407
  {"rows_deleted",
408
  (char*) &export_vars.innodb_rows_deleted,		  SHOW_LONG},
409
  {"rows_inserted",
410
  (char*) &export_vars.innodb_rows_inserted,		  SHOW_LONG},
411
  {"rows_read",
412
  (char*) &export_vars.innodb_rows_read,		  SHOW_LONG},
413
  {"rows_updated",
414
  (char*) &export_vars.innodb_rows_updated,		  SHOW_LONG},
brian@zim.(none)'s avatar
brian@zim.(none) committed
415 416
  {NullS, NullS, SHOW_LONG}
};
417

418 419
/* General functions */

420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435
/**********************************************************************
Returns true if the thread is the replication thread on the slave
server. Used in srv_conc_enter_innodb() to determine if the thread
should be allowed to enter InnoDB - the replication thread is treated
differently than other threads. Also used in
srv_conc_force_exit_innodb(). */
extern "C"
ibool
thd_is_replication_slave_thread(
/*============================*/
			/* out: true if thd is the replication thread */
	void*	thd)	/* in: thread handle (THD*) */
{
	return((ibool) thd_slave_thread((THD*) thd));
}

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
436 437 438 439 440 441 442 443 444
/**********************************************************************
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 */
{
445
	if (UNIV_LIKELY(!srv_thread_concurrency)) {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461

		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 */
{
462
	if (UNIV_LIKELY(!srv_thread_concurrency)) {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
463 464 465 466 467 468 469

		return;
	}

	srv_conc_exit_innodb(trx);
}

heikki@donna.mysql.fi's avatar
heikki@donna.mysql.fi committed
470
/**********************************************************************
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
471
Releases possible search latch and InnoDB thread FIFO ticket. These should
472 473 474 475
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
heikki@donna.mysql.fi's avatar
heikki@donna.mysql.fi committed
476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491
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);
	}
}

492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507
/**********************************************************************
Returns true if the transaction this thread is processing has edited
non-transactional tables. Used by the deadlock detector when deciding
which transaction to rollback in case of a deadlock - we try to avoid
rolling back transactions that have edited non-transactional tables. */
extern "C"
ibool
thd_has_edited_nontrans_tables(
/*===========================*/
			/* out: true if non-transactional tables have
			been edited */
	void*	thd)	/* in: thread handle (THD*) */
{
	return((ibool) thd_non_transactional_update((THD*) thd));
}

508 509 510 511 512 513
/************************************************************************
Obtain the InnoDB transaction of a MySQL thread. */
inline
trx_t*&
thd_to_trx(
/*=======*/
514 515
			/* out: reference to transaction pointer */
	THD*	thd)	/* in: MySQL thread */
516
{
517
	return(*(trx_t**) thd_ha_data(thd, innodb_hton_ptr));
518 519
}

520 521 522 523
/************************************************************************
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. */
524
static
525
int
526 527
innobase_release_temporary_latches(
/*===============================*/
528 529 530
				/* out: 0 */
	handlerton*	hton,	/* in: handlerton */
	THD*		thd)	/* in: MySQL thread */
531
{
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
532 533
	trx_t*	trx;

534 535
	DBUG_ASSERT(hton == innodb_hton_ptr);

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
536
	if (!innodb_inited) {
537

538
		return 0;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
539 540
	}

541
	trx = thd_to_trx(thd);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
542 543

	if (trx) {
544
		innobase_release_stat_resources(trx);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
545
	}
546
	return 0;
547 548
}

549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565
/************************************************************************
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();
	}
}

566
/************************************************************************
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
567 568 569
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. */
570 571 572 573 574
static
int
convert_error_code_to_mysql(
/*========================*/
			/* out: MySQL error code */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
575 576
	int	error,	/* in: InnoDB error code */
	THD*	thd)	/* in: user thread handle or NULL */
577 578 579 580 581
{
	if (error == DB_SUCCESS) {

		return(0);

582
	} else if (error == (int) DB_DUPLICATE_KEY) {
583

584
		return(HA_ERR_FOUND_DUPP_KEY);
585

586
	} else if (error == (int) DB_FOREIGN_DUPLICATE_KEY) {
587 588 589

		return(HA_ERR_FOREIGN_DUPLICATE_KEY);

590
	} else if (error == (int) DB_RECORD_NOT_FOUND) {
591

592
		return(HA_ERR_NO_ACTIVE_RECORD);
593

594
	} else if (error == (int) DB_ERROR) {
595

596
		return(-1); /* unspecified error */
597

598 599 600 601
	} 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 */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
602

603 604 605
		if (thd) {
			thd_mark_transaction_to_rollback(thd, TRUE);
		}
606

607 608
		return(HA_ERR_LOCK_DEADLOCK);
	} else if (error == (int) DB_LOCK_WAIT_TIMEOUT) {
609

610 611 612
		/* 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. */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
613

614 615 616 617
		if (thd) {
			thd_mark_transaction_to_rollback(
				thd, (bool)row_rollback_on_timeout);
		}
618

619
		return(HA_ERR_LOCK_WAIT_TIMEOUT);
620

621
	} else if (error == (int) DB_NO_REFERENCED_ROW) {
622

623
		return(HA_ERR_NO_REFERENCED_ROW);
624

625
	} else if (error == (int) DB_ROW_IS_REFERENCED) {
626

627
		return(HA_ERR_ROW_IS_REFERENCED);
628

629
	} else if (error == (int) DB_CANNOT_ADD_CONSTRAINT) {
630

631
		return(HA_ERR_CANNOT_ADD_FOREIGN);
632

633
	} else if (error == (int) DB_CANNOT_DROP_CONSTRAINT) {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
634

635
		return(HA_ERR_ROW_IS_REFERENCED); /* TODO: This is a bit
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
636 637
						misleading, a new MySQL error
						code should be introduced */
638
	} else if (error == (int) DB_COL_APPEARS_TWICE_IN_INDEX) {
639

640
		return(HA_ERR_CRASHED);
641

642
	} else if (error == (int) DB_OUT_OF_FILE_SPACE) {
643

644
		return(HA_ERR_RECORD_FILE_FULL);
645

646
	} else if (error == (int) DB_TABLE_IS_BEING_USED) {
647

648
		return(HA_ERR_WRONG_COMMAND);
649

650
	} else if (error == (int) DB_TABLE_NOT_FOUND) {
651

652
		return(HA_ERR_NO_SUCH_TABLE);
653

654
	} else if (error == (int) DB_TOO_BIG_RECORD) {
655

656
		return(HA_ERR_TO_BIG_ROW);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
657

658
	} else if (error == (int) DB_CORRUPTION) {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
659

660 661
		return(HA_ERR_CRASHED);
	} else if (error == (int) DB_NO_SAVEPOINT) {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
662

663 664 665 666 667
		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 */
668

669 670 671
		if (thd) {
			thd_mark_transaction_to_rollback(thd, TRUE);
		}
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
672 673

    		return(HA_ERR_LOCK_TABLE_FULL);
674 675 676 677 678 679 680 681 682 683 684 685 686 687
	} else if (error == DB_TOO_MANY_CONCURRENT_TRXS) {

		/* Once MySQL add the appropriate code to errmsg.txt then
		we can get rid of this #ifdef. NOTE: The code checked by
		the #ifdef is the suggested name for the error condition
		and the actual error code name could very well be different.
		This will require some monitoring, ie. the status
		of this request on our part.*/
#ifdef ER_TOO_MANY_CONCURRENT_TRXS
		return(ER_TOO_MANY_CONCURRENT_TRXS);
#else
		return(HA_ERR_RECORD_FILE_FULL);
#endif

688 689 690
	} else if (error == DB_UNSUPPORTED) {

		return(HA_ERR_UNSUPPORTED);
691
    	} else {
692
    		return(-1);			// Unknown error
693 694 695
    	}
}

696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713
/*****************************************************************
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));
}

/*****************************************************************
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
714
Releases the mutex reserved by innobase_mysql_prepare_print_arbitrary_thd().
715 716 717 718 719 720 721 722 723 724
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));
}

725
/*****************************************************************
726 727 728
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! */
729
extern "C"
730 731 732
void
innobase_mysql_print_thd(
/*=====================*/
733 734
	FILE*	f,		/* in: output stream */
	void*	input_thd,	/* in: pointer to a MySQL THD object */
735 736
	uint	max_query_len)	/* in: max query length to print, or 0 to
				   use the default max length */
737
{
antony@ppcg5.local's avatar
antony@ppcg5.local committed
738
	THD*	thd;
739
	char	buffer[1024];
740

741 742
	thd = (THD*) input_thd;
	fputs(thd_security_context(thd, buffer, sizeof(buffer), 
antony@ppcg5.local's avatar
antony@ppcg5.local committed
743
				   max_query_len), f);
744
	putc('\n', f);
745 746
}

747
/**********************************************************************
748
Get the variable length bounds of the given character set.
749 750

NOTE that the exact prototype of this function has to be in
751
/innobase/include/data0type.ic! */
752
extern "C"
753
void
754 755
innobase_get_cset_width(
/*====================*/
756 757 758
	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) */
759 760 761
{
	CHARSET_INFO*	cs;
	ut_ad(cset < 256);
762 763
	ut_ad(mbminlen);
	ut_ad(mbmaxlen);
764 765

	cs = all_charsets[cset];
766 767 768 769 770 771 772
	if (cs) {
		*mbminlen = cs->mbminlen;
		*mbmaxlen = cs->mbmaxlen;
	} else {
		ut_a(cset == 0);
		*mbminlen = *mbmaxlen = 0;
	}
773 774
}

775 776 777 778 779 780 781 782 783 784 785 786 787 788 789
/**********************************************************************
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;

790
	strconvert(thd_charset(current_thd), from,
791
		   &my_charset_filename, to, (uint) len, &errors);
792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808
}

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

809
	strconvert(thd_charset(current_thd), from,
810
		   system_charset_info, to, (uint) len, &errors);
811 812
}

813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842
/**********************************************************************
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);
}

843 844 845 846 847 848 849 850 851 852 853 854
/**************************************************************************
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 */
{
855
	return(thd_charset((THD*) mysql_thd));
856 857
}

858 859 860 861 862 863 864 865
/*************************************************************************
Creates a temporary file. */
extern "C"
int
innobase_mysql_tmpfile(void)
/*========================*/
			/* out: temporary file descriptor, or < 0 on error */
{
marko@hundin.mysql.fi's avatar
marko@hundin.mysql.fi committed
866
	int	fd2 = -1;
867
	File	fd = mysql_tmpfile("ib");
868
	if (fd >= 0) {
marko@hundin.mysql.fi's avatar
marko@hundin.mysql.fi committed
869 870 871 872 873 874 875 876 877 878 879 880
		/* 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;
881 882
			my_error(EE_OUT_OF_FILERESOURCES,
				 MYF(ME_BELL+ME_WAITTANG),
883
				 "ib*", my_errno);
884
		}
marko@hundin.mysql.fi's avatar
marko@hundin.mysql.fi committed
885 886 887
		my_close(fd, MYF(MY_WME));
	}
	return(fd2);
888 889
}

890 891 892 893 894 895 896 897 898 899 900 901 902 903 904
/*************************************************************************
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)
{
905 906 907
  return(copy_and_convert((char*)to, (uint32) to_length, to_cs,
                          (const char*)from, (uint32) from_length, from_cs,
                          errors));
908 909
}

910
/*************************************************************************
911 912
Gets the InnoDB transaction handle for a MySQL handler object, creates
an InnoDB transaction struct if the corresponding MySQL thread struct still
913
lacks one. */
914
static
915 916 917
trx_t*
check_trx_exists(
/*=============*/
918
			/* out: InnoDB transaction handle */
919 920
	THD*	thd)	/* in: user thread handle */
{
921
	trx_t*&	trx = thd_to_trx(thd);
922

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
923
	ut_ad(thd == current_thd);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
924

925
	if (trx == NULL) {
926
		DBUG_ASSERT(thd != NULL);
927
		trx = trx_allocate_for_mysql();
928

929
		trx->mysql_thd = thd;
930
		trx->mysql_query_str = thd_query(thd);
931

932 933
		/* Update the info whether we should skip XA steps that eat
		CPU time */
antony@ppcg5.local's avatar
antony@ppcg5.local committed
934
		trx->support_xa = THDVAR(thd, support_xa);
heikki@donna.mysql.fi's avatar
heikki@donna.mysql.fi committed
935
	} else {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
936
		if (trx->magic_n != TRX_MAGIC_N) {
937
			mem_analyze_corruption(trx);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
938

939
			ut_error;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
940 941 942
		}
	}

antony@ppcg5.local's avatar
antony@ppcg5.local committed
943
	if (thd_test_options(thd, OPTION_NO_FOREIGN_KEY_CHECKS)) {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
944 945 946 947 948
		trx->check_foreigns = FALSE;
	} else {
		trx->check_foreigns = TRUE;
	}

antony@ppcg5.local's avatar
antony@ppcg5.local committed
949
	if (thd_test_options(thd, OPTION_RELAXED_UNIQUE_CHECKS)) {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
950 951 952
		trx->check_unique_secondary = FALSE;
	} else {
		trx->check_unique_secondary = TRUE;
953 954 955 956 957
	}

	return(trx);
}

958 959 960 961

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

962
ha_innobase::ha_innobase(handlerton *hton, TABLE_SHARE *table_arg)
963
  :handler(hton, table_arg),
964
  int_table_flags(HA_REC_NOT_IN_SEQ |
965 966 967
		  HA_NULL_IN_KEY |
		  HA_CAN_INDEX_BLOBS |
		  HA_CAN_SQL_HANDLER |
968
		  HA_PRIMARY_KEY_REQUIRED_FOR_POSITION |
969
		  HA_PRIMARY_KEY_IN_READ_INDEX |
970
		  HA_BINLOG_ROW_CAPABLE |
971
		  HA_CAN_GEOMETRY | HA_PARTIAL_COLUMN_READ |
972
		  HA_TABLE_SCAN_ON_INDEX),
973 974 975 976
  start_of_scan(0),
  num_write_row(0)
{}

977
/*************************************************************************
978
Updates the user_thd field in a handle and also allocates a new InnoDB
979 980
transaction handle if needed, and updates the transaction fields in the
prebuilt struct. */
981
inline
982 983 984 985 986 987
int
ha_innobase::update_thd(
/*====================*/
			/* out: 0 or error code */
	THD*	thd)	/* in: thd to use the handle */
{
988
	trx_t*		trx;
989

990
	trx = check_trx_exists(thd);
991

992
	if (prebuilt->trx != trx) {
993

994
		row_update_prebuilt_trx(prebuilt, trx);
995 996 997
	}

	user_thd = thd;
998

999 1000 1001
	return(0);
}

1002
/*************************************************************************
1003 1004 1005 1006 1007
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
1008
void
1009 1010
innobase_register_stmt(
/*===================*/
1011
        handlerton*	hton,	/* in: Innobase hton */
1012
	THD*	thd)	/* in: MySQL thd (connection) object */
1013
{
1014
	/* Register the statement */
1015
	trans_register_ha(thd, FALSE, hton);
1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028
}

/*************************************************************************
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(
/*===========================*/
1029
        handlerton *hton, /* in: Innobase handlerton */
1030 1031 1032 1033 1034
	THD*	thd)	/* in: MySQL thd (connection) object */
{
	/* NOTE that actually innobase_register_stmt() registers also
	the transaction in the AUTOCOMMIT=1 mode. */

1035
	innobase_register_stmt(hton, thd);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1036

antony@ppcg5.local's avatar
antony@ppcg5.local committed
1037
	if (thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) {
1038

1039
		/* No autocommit mode, register for a transaction */
1040
		trans_register_ha(thd, TRUE, hton);
1041
	}
1042
}
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1043 1044 1045 1046 1047 1048 1049 1050 1051 1052

/*   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
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1053
id <= INV_TRX_ID to use the query cache.
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096

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
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1097 1098 1099 1100 1101 1102 1103 1104
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. */
1105
static
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1106
my_bool
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118
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 */
1119
	uint	full_name_len,	/* in: length of the full name, i.e.
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1120
				len(dbname) + len(tablename) + 1 */
1121
	ulonglong *unused)	/* unused for this engine */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1122 1123 1124 1125 1126 1127 1128
{
	ibool	is_autocommit;
	trx_t*	trx;
	char	norm_name[1000];

	ut_a(full_name_len < 999);

1129
	trx = check_trx_exists(thd);
antony@ppcg5.local's avatar
antony@ppcg5.local committed
1130 1131

	if (trx->isolation_level == TRX_ISO_SERIALIZABLE) {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1132
		/* In the SERIALIZABLE mode we add LOCK IN SHARE MODE to every
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1133
		plain SELECT if AUTOCOMMIT is not on. */
1134

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1135 1136 1137
		return((my_bool)FALSE);
	}

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1138
	if (trx->has_search_latch) {
1139 1140 1141
		sql_print_error("The calling thread is holding the adaptive "
				"search, latch though calling "
				"innobase_query_caching_of_table_permitted.");
1142 1143 1144 1145

		mutex_enter_noninline(&kernel_mutex);
		trx_print(stderr, trx, 1024);
		mutex_exit_noninline(&kernel_mutex);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1146 1147
	}

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1148 1149
	innobase_release_stat_resources(trx);

antony@ppcg5.local's avatar
antony@ppcg5.local committed
1150
	if (!thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1151 1152 1153 1154 1155 1156 1157

		is_autocommit = TRUE;
	} else {
		is_autocommit = FALSE;

	}

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175
	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. */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1176 1177 1178

		return((my_bool)TRUE);
	}
1179

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1180 1181 1182 1183 1184 1185 1186 1187
	/* 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__
1188
	innobase_casedn_str(norm_name);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1189
#endif
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1190 1191 1192
	/* The call of row_search_.. will start a new transaction if it is
	not yet started */

1193
	if (trx->active_trans == 0) {
1194

1195
		innobase_register_trx_and_stmt(innodb_hton_ptr, thd);
1196 1197
		trx->active_trans = 1;
	}
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1198

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1199 1200
	if (row_search_check_if_query_cache_permitted(trx, norm_name)) {

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1201
		/* printf("Query cache for %s permitted\n", norm_name); */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1202 1203 1204 1205

		return((my_bool)TRUE);
	}

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1206
	/* printf("Query cache for %s NOT permitted\n", norm_name); */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1207 1208 1209 1210 1211 1212 1213 1214

	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! */
1215
extern "C"
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1216 1217 1218 1219 1220
void
innobase_invalidate_query_cache(
/*============================*/
	trx_t*	trx,		/* in: transaction which modifies the table */
	char*	full_name,	/* in: concatenation of database name, null
1221 1222 1223 1224 1225
				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 */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1226
{
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1227 1228 1229 1230
	/* 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. */

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1231
	/* Argument TRUE below means we are using transactions */
1232
#ifdef HAVE_QUERY_CACHE
1233 1234 1235 1236
	mysql_query_cache_invalidate4((THD*) trx->mysql_thd,
				      (const char*) full_name,
				      (uint32) full_name_len,
				      TRUE);
1237
#endif
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1238
}
1239 1240

/*********************************************************************
1241
Display an SQL identifier. */
1242
extern "C"
1243 1244 1245 1246
void
innobase_print_identifier(
/*======================*/
	FILE*		f,	/* in: output stream */
1247
	trx_t*		trx,	/* in: transaction */
1248 1249
	ibool		table_id,/* in: TRUE=print a table name,
				FALSE=print other identifier */
1250 1251 1252
	const char*	name,	/* in: name to print */
	ulint		namelen)/* in: length of name */
{
1253 1254 1255 1256 1257 1258 1259 1260 1261 1262
	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]. */
1263
          char*	temp_name = (char*) my_malloc((uint) namelen + 1, MYF(MY_WME));
1264 1265
          uint	qnamelen = (uint) (namelen
                                   + (1 + sizeof srv_mysql50_table_name_prefix));
1266 1267

		if (temp_name) {
1268
                  qname = (char*) my_malloc(qnamelen, MYF(MY_WME));
1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279
			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));
		}
	}

1280
	if (!trx || !trx->mysql_thd) {
1281 1282 1283 1284 1285

		q = '"';
	} else {
		q = get_quote_char_for_identifier((THD*) trx->mysql_thd,
						s, (int) namelen);
1286
	}
1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303

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

1306 1307 1308 1309 1310 1311 1312 1313 1314
/**************************************************************************
Determines if the currently running transaction has been interrupted. */
extern "C"
ibool
trx_is_interrupted(
/*===============*/
			/* out: TRUE if interrupted */
	trx_t*	trx)	/* in: transaction */
{
1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328
	return(trx && trx->mysql_thd && thd_killed((THD*) trx->mysql_thd));
}

/******************************************************************
Resets some fields of a prebuilt struct. The template is used in fast
retrieval of just those column values MySQL needs in its processing. */
static
void
reset_template(
/*===========*/
	row_prebuilt_t*	prebuilt)	/* in/out: prebuilt struct */
{
	prebuilt->keep_other_fields_on_keyread = 0;
	prebuilt->read_just_key = 0;
1329 1330
}

1331 1332 1333 1334
/*********************************************************************
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,
1335
fetch next etc. This function inits the necessary things even after a
1336 1337 1338 1339 1340 1341
transaction commit. */

void
ha_innobase::init_table_handle_for_HANDLER(void)
/*============================================*/
{
1342 1343 1344 1345
	/* 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. */
1346

1347
	update_thd(ha_thd());
1348

1349 1350
	/* Initialize the prebuilt struct much like it would be inited in
	external_lock */
1351

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1352 1353
	innobase_release_stat_resources(prebuilt->trx);

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

1356
	trx_start_if_not_started_noninline(prebuilt->trx);
1357

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

1360
	trx_assign_read_view(prebuilt->trx);
1361

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

1364
	if (prebuilt->trx->active_trans == 0) {
1365

1366
		innobase_register_trx_and_stmt(ht, user_thd);
1367

1368 1369
		prebuilt->trx->active_trans = 1;
	}
1370

1371 1372
	/* We did the necessary inits in this function, no need to repeat them
	in row_search_for_mysql */
1373

1374
	prebuilt->sql_stat_start = FALSE;
1375

1376 1377
	/* We let HANDLER always to do the reads as consistent reads, even
	if the trx isolation level would have been specified as SERIALIZABLE */
1378

1379 1380
	prebuilt->select_lock_type = LOCK_NONE;
	prebuilt->stored_select_lock_type = LOCK_NONE;
1381

1382
	/* Always fetch all columns in the index record */
1383

1384
	prebuilt->hint_need_to_fetch_extra_cols = ROW_RETRIEVE_ALL_COLS;
1385

1386
	/* We want always to fetch all columns in the whole row? Or do
1387 1388
	we???? */

1389
	prebuilt->used_in_HANDLER = TRUE;
1390
	reset_template(prebuilt);
1391 1392
}

1393
/*************************************************************************
1394
Opens an InnoDB database. */
1395
static
1396
int
1397 1398 1399 1400
innobase_init(
/*==========*/
			/* out: 0 on success, error code on failure */
	void	*p)	/* in: InnoDB handlerton */
1401
{
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1402
	static char	current_dir[3];		/* Set if using current lib */
1403 1404
	int		err;
	bool		ret;
1405
	char		*default_path;
monty@hundin.mysql.fi's avatar
merge  
monty@hundin.mysql.fi committed
1406

1407
	DBUG_ENTER("innobase_init");
1408
        handlerton *innobase_hton= (handlerton *)p;
1409
        innodb_hton_ptr = innobase_hton;
1410

1411
        innobase_hton->state = SHOW_OPTION_YES;
1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434
        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;
1435

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1436 1437
	ut_a(DATA_MYSQL_TRUE_VARCHAR == (ulint)MYSQL_TYPE_VARCHAR);

1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455
#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 */

1456 1457 1458 1459 1460 1461 1462
	/* 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");

1463
			goto error;
1464 1465 1466 1467 1468 1469 1470
		}

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

1471
			goto error;
1472 1473 1474
		}
	}

1475
	os_innodb_umask = (ulint)my_umask;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1476

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1477 1478 1479 1480 1481 1482
	/* 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. */

1483
	if (mysqld_embedded) {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1484
		default_path = mysql_real_data_home;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1485
		fil_path_to_mysql_datadir = mysql_real_data_home;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1486
	} else {
1487 1488 1489 1490 1491
		/* 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;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1492 1493
	}

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1494 1495
	ut_a(default_path);

1496
	if (specialflag & SPECIAL_NO_PRIOR) {
1497
		srv_set_thread_priorities = FALSE;
1498
	} else {
1499 1500
		srv_set_thread_priorities = TRUE;
		srv_query_thread_priority = QUERY_PRIOR;
1501
	}
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1502

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1503 1504
	/* Set InnoDB initialization parameters according to the values
	read from MySQL .cnf file */
1505

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1506
	/*--------------- Data files -------------------------*/
1507

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1508
	/* The default dir for data files is the datadir of MySQL */
1509 1510

	srv_data_home = (innobase_data_home_dir ? innobase_data_home_dir :
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1511
			 default_path);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1512

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1513
	/* Set default InnoDB data file size to 10 MB and let it be
1514
	auto-extending. Thus users can use InnoDB in >= 4.0 without having
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1515 1516 1517
	to specify any startup options. */

	if (!innobase_data_file_path) {
1518
		innobase_data_file_path = (char*) "ibdata1:10M:autoextend";
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1519 1520 1521 1522 1523 1524
	}

	/* 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,
osku@127.(none)'s avatar
osku@127.(none) committed
1525
						   MYF(MY_FAE));
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1526 1527 1528

	ret = (bool) srv_parse_data_file_paths_and_sizes(
				internal_innobase_data_file_path,
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1529 1530 1531 1532 1533 1534
				&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);
1535
	if (ret == FALSE) {
1536
		sql_print_error(
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1537
			"InnoDB: syntax error in innodb_data_file_path");
1538
		my_free(internal_innobase_data_file_path,
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1539
						MYF(MY_ALLOW_ZERO_PTR));
1540
		goto error;
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1541
	}
1542

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1543 1544 1545
	/* -------------- Log files ---------------------------*/

	/* The default dir for log files is the datadir of MySQL */
1546

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1547
	if (!innobase_log_group_home_dir) {
1548
		innobase_log_group_home_dir = default_path;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1549
	}
marko@hundin.mysql.fi's avatar
marko@hundin.mysql.fi committed
1550

1551
#ifdef UNIV_LOG_ARCHIVE
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1552 1553 1554 1555 1556 1557 1558
	/* 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;
marko@hundin.mysql.fi's avatar
marko@hundin.mysql.fi committed
1559
#endif /* UNIG_LOG_ARCHIVE */
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1560

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1561 1562 1563
	ret = (bool)
		srv_parse_log_group_home_dirs(innobase_log_group_home_dir,
						&srv_log_group_home_dirs);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1564

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1565
	if (ret == FALSE || innobase_mirrored_log_groups != 1) {
1566 1567
	  sql_print_error("syntax error in innodb_log_group_home_dir, or a "
			  "wrong number of mirrored log groups");
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1568

1569
		my_free(internal_innobase_data_file_path,
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1570
						MYF(MY_ALLOW_ZERO_PTR));
1571
		goto error;
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1572
	}
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1573

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1574 1575 1576
	/* --------------------------------------------------*/

	srv_file_flush_method_str = innobase_unix_file_flush_method;
1577

monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1578
	srv_n_log_groups = (ulint) innobase_mirrored_log_groups;
1579
	srv_n_log_files = (ulint) innobase_log_files_in_group;
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1580 1581
	srv_log_file_size = (ulint) innobase_log_file_size;

marko@hundin.mysql.fi's avatar
marko@hundin.mysql.fi committed
1582
#ifdef UNIV_LOG_ARCHIVE
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1583
	srv_log_archive_on = (ulint) innobase_log_archive;
marko@hundin.mysql.fi's avatar
marko@hundin.mysql.fi committed
1584
#endif /* UNIV_LOG_ARCHIVE */
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1585
	srv_log_buffer_size = (ulint) innobase_log_buffer_size;
1586

1587 1588 1589 1590
	/* 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) {
1591
		srv_pool_size = (ulint)(innobase_buffer_pool_size / 1024);
1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602
	} 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. */
	}
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1603

monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1604 1605 1606
	srv_mem_pool_size = (ulint) innobase_additional_mem_pool_size;

	srv_n_file_io_threads = (ulint) innobase_file_io_threads;
1607

1608
	srv_lock_wait_timeout = (ulint) innobase_lock_wait_timeout;
monty@donna.mysql.fi's avatar
Merge  
monty@donna.mysql.fi committed
1609 1610
	srv_force_recovery = (ulint) innobase_force_recovery;

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1611 1612
	srv_use_doublewrite_buf = (ibool) innobase_use_doublewrite;
	srv_use_checksums = (ibool) innobase_use_checksums;
1613

antony@ppcg5.local's avatar
antony@ppcg5.local committed
1614 1615 1616 1617
#ifdef HAVE_LARGE_PAGES
        if ((os_use_large_pages = (ibool) my_use_large_pages))
		os_large_page_size = (ulint) opt_large_page_size;
#endif
1618

1619 1620
	row_rollback_on_timeout = (ibool) innobase_rollback_on_timeout;

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1621
	srv_file_per_table = (ibool) innobase_file_per_table;
1622
	srv_locks_unsafe_for_binlog = (ibool) innobase_locks_unsafe_for_binlog;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1623 1624

	srv_max_n_open_files = (ulint) innobase_open_files;
1625
	srv_innodb_status = (ibool) innobase_create_status_file;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1626

1627 1628
	srv_stats_on_metadata = (ibool) innobase_stats_on_metadata;

1629
	srv_use_adaptive_hash_indexes =
1630
		(ibool) innobase_adaptive_hash_index;
1631

1632
	srv_print_verbose_log = mysqld_embedded ? 0 : 1;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1633

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1634
	/* Store the default charset-collation number of this MySQL
1635
	installation */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1636

1637
	data_mysql_default_charset_coll = (ulint)default_charset_info->number;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1638

1639 1640
	ut_a(DATA_MYSQL_LATIN1_SWEDISH_CHARSET_COLL ==
					my_charset_latin1.number);
1641
	ut_a(DATA_MYSQL_BINARY_CHARSET_COLL == my_charset_bin.number);
1642

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1643 1644 1645 1646
	/* 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. */
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1647

1648
	ut_a(0 == strcmp((char*)my_charset_latin1.name,
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1649 1650
						(char*)"latin1_swedish_ci"));
	memcpy(srv_latin1_ordering, my_charset_latin1.sort_order, 256);
1651

1652
	/* Since we in this module access directly the fields of a trx
1653
	struct, and due to different headers and flags it might happen that
1654 1655 1656 1657 1658 1659
	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);

monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1660
	err = innobase_start_or_create_for_mysql();
1661 1662

	if (err != DB_SUCCESS) {
1663
		my_free(internal_innobase_data_file_path,
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1664
						MYF(MY_ALLOW_ZERO_PTR));
1665
		goto error;
1666
	}
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1667 1668

	(void) hash_init(&innobase_open_tables,system_charset_info, 32, 0, 0,
1669 1670 1671 1672 1673 1674
					(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);
1675
	innodb_inited= 1;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1676

1677 1678
	DBUG_RETURN(FALSE);
error:
1679
	DBUG_RETURN(TRUE);
1680 1681 1682
}

/***********************************************************************
1683
Closes an InnoDB database. */
1684
static
1685
int
1686
innobase_end(handlerton *hton, ha_panic_function type)
1687
/*==============*/
1688
				/* out: TRUE if error */
1689
{
1690
	int	err= 0;
1691 1692 1693

	DBUG_ENTER("innobase_end");

1694
#ifdef __NETWARE__	/* some special cleanup for NetWare */
1695 1696 1697 1698
	if (nw_panic) {
		set_panic_flag_for_netware();
	}
#endif
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1699
	if (innodb_inited) {
1700

1701 1702 1703 1704
		srv_fast_shutdown = (ulint) innobase_fast_shutdown;
		innodb_inited = 0;
		if (innobase_shutdown_for_mysql() != DB_SUCCESS) {
			err = 1;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1705
		}
1706 1707
		hash_free(&innobase_open_tables);
		my_free(internal_innobase_data_file_path,
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1708
						MYF(MY_ALLOW_ZERO_PTR));
1709 1710 1711 1712 1713
		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);
1714
	}
1715

1716
	DBUG_RETURN(err);
1717 1718 1719
}

/********************************************************************
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1720 1721
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. */
1722
static
1723
bool
1724
innobase_flush_logs(handlerton *hton)
1725
/*=====================*/
1726
				/* out: TRUE if error */
1727
{
1728
	bool	result = 0;
1729

1730
	DBUG_ENTER("innobase_flush_logs");
1731

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1732
	log_buffer_flush_to_disk();
1733

1734
	DBUG_RETURN(result);
1735 1736 1737
}

/*********************************************************************
1738
Commits a transaction in an InnoDB database. */
1739
static
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1740 1741 1742 1743 1744
void
innobase_commit_low(
/*================*/
	trx_t*	trx)	/* in: transaction handle */
{
1745
	if (trx->conc_state == TRX_NOT_STARTED) {
1746

1747 1748
		return;
	}
1749

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1750
	trx_commit_for_mysql(trx);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1751 1752
}

1753 1754 1755 1756 1757
/*********************************************************************
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. */
1758
static
1759 1760 1761 1762
int
innobase_start_trx_and_assign_read_view(
/*====================================*/
			/* out: 0 */
1763
        handlerton *hton, /* in: Innodb handlerton */ 
1764 1765 1766 1767 1768
	THD*	thd)	/* in: MySQL thread handle of the user for whom
			the transaction should be committed */
{
	trx_t*	trx;

1769
	DBUG_ENTER("innobase_start_trx_and_assign_read_view");
1770 1771 1772

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

1773
	trx = check_trx_exists(thd);
1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790

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

1791
	if (trx->active_trans == 0) {
1792
		innobase_register_trx_and_stmt(hton, current_thd);
1793 1794
		trx->active_trans = 1;
	}
1795 1796 1797 1798

	DBUG_RETURN(0);
}

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1799
/*********************************************************************
1800 1801
Commits a transaction in an InnoDB database or marks an SQL statement
ended. */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1802 1803
static
int
1804 1805
innobase_commit(
/*============*/
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1806
			/* out: 0 */
1807 1808
        handlerton *hton, /* in: Innodb handlerton */ 
	THD* 	thd,	/* in: MySQL thread handle of the user for whom
1809
			the transaction should be committed */
1810 1811
	bool	all)	/* in:	TRUE - commit transaction
				FALSE - the current SQL statement ended */
1812
{
1813
	trx_t*		trx;
1814

1815 1816
	DBUG_ENTER("innobase_commit");
	DBUG_PRINT("trans", ("ending transaction"));
1817

1818
	trx = check_trx_exists(thd);
1819

1820
	/* Update the info whether we should skip XA steps that eat CPU time */
antony@ppcg5.local's avatar
antony@ppcg5.local committed
1821
	trx->support_xa = THDVAR(thd, support_xa);
1822

1823 1824
	/* Since we will reserve the kernel mutex, we have to release
	the search system latch first to obey the latching order. */
1825

1826
	if (trx->has_search_latch) {
1827
		trx_search_latch_release_if_reserved(trx);
1828 1829 1830
	}

	/* The flag trx->active_trans is set to 1 in
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1831 1832 1833

	1. ::external_lock(),
	2. ::start_stmt(),
1834
	3. innobase_query_caching_of_table_permitted(),
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1835
	4. innobase_savepoint(),
1836
	5. ::init_table_handle_for_HANDLER(),
1837 1838
	6. innobase_start_trx_and_assign_read_view(),
	7. ::transactional_table_lock()
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1839 1840 1841 1842 1843

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

1845 1846 1847 1848 1849
	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");
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1850
	}
1851
	if (all
antony@ppcg5.local's avatar
antony@ppcg5.local committed
1852
		|| (!thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) {
1853 1854

		/* We were instructed to commit the whole transaction, or
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1855 1856
		this is an SQL statement end and autocommit is on */

1857 1858 1859
		/* We need current binlog position for ibbackup to work.
		Note, the position is current because of
		prepare_commit_mutex */
1860
retry:
1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876
		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);
			}
		}

1877 1878
		trx->mysql_log_file_name = mysql_bin_log_file_name();
		trx->mysql_log_offset = (ib_longlong) mysql_bin_log_file_pos();
serg@serg.mylan's avatar
serg@serg.mylan committed
1879

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1880
		innobase_commit_low(trx);
1881

1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895
		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;

1896
	} else {
1897
		/* We just mark the SQL statement ended and do not do a
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1898 1899
		transaction commit */

1900 1901 1902 1903
		/* If we had reserved the auto-inc lock for some
		table in this SQL statement we release it now */

		row_unlock_table_autoinc_for_mysql(trx);
1904

1905 1906 1907 1908 1909
		/* 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);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1910
	}
1911

1912 1913
	trx->n_autoinc_rows = 0; /* Reset the number AUTO-INC rows required */

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

1917
		srv_conc_force_exit_innodb(trx);
1918
	}
1919 1920 1921

	/* Tell the InnoDB server that there might be work for utility
	threads: */
1922 1923
	srv_active_wake_master_thread();

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1924
	DBUG_RETURN(0);
1925 1926 1927
}

/*********************************************************************
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1928
Rolls back a transaction or the latest SQL statement. */
1929 1930
static
int
1931 1932 1933
innobase_rollback(
/*==============*/
			/* out: 0 or error number */
1934
        handlerton *hton, /* in: Innodb handlerton */ 
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1935
	THD*	thd,	/* in: handle to the MySQL thread of the user
1936
			whose transaction should be rolled back */
1937 1938
	bool	all)	/* in:	TRUE - commit transaction
				FALSE - the current SQL statement ended */
1939 1940
{
	int	error = 0;
1941
	trx_t*	trx;
1942

1943 1944 1945
	DBUG_ENTER("innobase_rollback");
	DBUG_PRINT("trans", ("aborting transaction"));

1946
	trx = check_trx_exists(thd);
1947

1948
	/* Update the info whether we should skip XA steps that eat CPU time */
antony@ppcg5.local's avatar
antony@ppcg5.local committed
1949
	trx->support_xa = THDVAR(thd, support_xa);
1950

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1951 1952 1953 1954 1955 1956
	/* 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);

1957 1958 1959
	/* 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 */
1960

1961
	row_unlock_table_autoinc_for_mysql(trx);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1962

1963
	if (all
antony@ppcg5.local's avatar
antony@ppcg5.local committed
1964
		|| !thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) {
monty@donna.mysql.fi's avatar
Merge  
monty@donna.mysql.fi committed
1965

1966
		error = trx_rollback_for_mysql(trx);
1967
		trx->active_trans = 0;
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1968
	} else {
1969
		error = trx_rollback_last_sql_stat_for_mysql(trx);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1970
	}
1971

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1972 1973 1974
	DBUG_RETURN(convert_error_code_to_mysql(error, NULL));
}

1975 1976
/*********************************************************************
Rolls back a transaction */
1977
static
1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994
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);

1995 1996 1997
	/* 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 */
1998

1999
	row_unlock_table_autoinc_for_mysql(trx);
2000 2001 2002 2003 2004 2005

	error = trx_rollback_for_mysql(trx);

	DBUG_RETURN(convert_error_code_to_mysql(error, NULL));
}

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2006 2007
/*********************************************************************
Rolls back a transaction to a savepoint. */
2008 2009
static
int
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2010 2011 2012 2013
innobase_rollback_to_savepoint(
/*===========================*/
				/* out: 0 if success, HA_ERR_NO_SAVEPOINT if
				no savepoint with the given name */
2014
        handlerton *hton,       /* in: Innodb handlerton */ 
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2015 2016
	THD*	thd,		/* in: handle to the MySQL thread of the user
				whose transaction should be rolled back */
2017
	void*	savepoint)	/* in: savepoint data */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2018
{
2019 2020 2021 2022
	ib_longlong	mysql_binlog_cache_pos;
	int		error = 0;
	trx_t*		trx;
	char		name[64];
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2023 2024 2025

	DBUG_ENTER("innobase_rollback_to_savepoint");

2026
	trx = check_trx_exists(thd);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2027

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2028 2029 2030
	/* 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. */
heikki@donna.mysql.fi's avatar
heikki@donna.mysql.fi committed
2031 2032

	innobase_release_stat_resources(trx);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2033

2034
	/* TODO: use provided savepoint data area to store savepoint data */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2035

2036
	longlong2str((ulint)savepoint, name, 36);
2037

2038
	error = (int) trx_rollback_to_savepoint_for_mysql(trx, name,
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2039
						&mysql_binlog_cache_pos);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2040
	DBUG_RETURN(convert_error_code_to_mysql(error, NULL));
2041 2042
}

2043 2044
/*********************************************************************
Release transaction savepoint name. */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2045 2046
static
int
serg@serg.mylan's avatar
serg@serg.mylan committed
2047
innobase_release_savepoint(
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2048
/*=======================*/
2049 2050
				/* out: 0 if success, HA_ERR_NO_SAVEPOINT if
				no savepoint with the given name */
2051
        handlerton*	hton,	/* in: handlerton for Innodb */
2052 2053
	THD*	thd,		/* in: handle to the MySQL thread of the user
				whose transaction should be rolled back */
2054
	void*	savepoint)	/* in: savepoint data */
2055
{
2056 2057 2058
	int		error = 0;
	trx_t*		trx;
	char		name[64];
2059

serg@serg.mylan's avatar
serg@serg.mylan committed
2060
	DBUG_ENTER("innobase_release_savepoint");
2061

2062
	trx = check_trx_exists(thd);
2063

2064
	/* TODO: use provided savepoint data area to store savepoint data */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2065

2066
	longlong2str((ulint)savepoint, name, 36);
2067

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2068
	error = (int) trx_release_savepoint_for_mysql(trx, name);
2069 2070 2071 2072

	DBUG_RETURN(convert_error_code_to_mysql(error, NULL));
}

2073
/*********************************************************************
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2074
Sets a transaction savepoint. */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2075 2076
static
int
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2077 2078 2079
innobase_savepoint(
/*===============*/
				/* out: always 0, that is, always succeeds */
2080
	handlerton*	hton,   /* in: handle to the Innodb handlerton */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2081
	THD*	thd,		/* in: handle to the MySQL thread */
2082
	void*	savepoint)	/* in: savepoint data */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2083 2084 2085 2086 2087 2088
{
	int	error = 0;
	trx_t*	trx;

	DBUG_ENTER("innobase_savepoint");

2089 2090 2091 2092 2093
	/*
	  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.
	*/
2094
#ifdef MYSQL_SERVER /* plugins cannot access thd->in_sub_stmt */
antony@ppcg5.local's avatar
antony@ppcg5.local committed
2095
	DBUG_ASSERT(thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN) ||
2096
		thd->in_sub_stmt);
2097
#endif /* MYSQL_SERVER */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2098

2099
	trx = check_trx_exists(thd);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2100

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2101 2102 2103 2104 2105 2106
	/* 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);

2107 2108
	/* cannot happen outside of transaction */
	DBUG_ASSERT(trx->active_trans);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2109

2110 2111 2112
	/* TODO: use provided savepoint data area to store savepoint data */
	char name[64];
	longlong2str((ulint)savepoint,name,36);
2113

2114
	error = (int) trx_savepoint_for_mysql(trx, name, (ib_longlong)0);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2115 2116 2117 2118

	DBUG_RETURN(convert_error_code_to_mysql(error, NULL));
}

2119
/*********************************************************************
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2120
Frees a possible InnoDB trx object associated with the current THD. */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2121 2122
static
int
2123 2124
innobase_close_connection(
/*======================*/
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2125
			/* out: 0 or error number */
2126
        handlerton*	hton,	/* in:  innobase handlerton */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2127
	THD*	thd)	/* in: handle to the MySQL thread of the user
2128
			whose resources should be free'd */
2129
{
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2130 2131
	trx_t*	trx;

2132 2133
	DBUG_ENTER("innobase_close_connection");
	DBUG_ASSERT(hton == innodb_hton_ptr);
2134
	trx = thd_to_trx(thd);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2135 2136 2137

	ut_a(trx);

2138 2139 2140 2141 2142
	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");
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2143 2144
	}

2145

2146
	if (trx->conc_state != TRX_NOT_STARTED &&
2147 2148 2149 2150 2151 2152 2153
		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);
	}
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2154 2155 2156

	innobase_rollback_trx(trx);

2157
	thr_local_free(trx->mysql_thread_id);
2158
	trx_free_for_mysql(trx);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2159

2160
	DBUG_RETURN(0);
2161
}
2162 2163 2164


/*****************************************************************************
2165
** InnoDB database tables
2166 2167
*****************************************************************************/

2168 2169 2170 2171 2172 2173 2174 2175
/********************************************************************
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 */
{
	if (prebuilt && prebuilt->table) {
2176
		if (dict_table_is_comp_noninline(prebuilt->table)) {
2177 2178 2179 2180 2181 2182 2183 2184 2185
			return(ROW_TYPE_COMPACT);
		} else {
			return(ROW_TYPE_REDUNDANT);
		}
	}
	ut_ad(0);
	return(ROW_TYPE_NOT_USED);
}

2186 2187 2188 2189 2190 2191 2192


/********************************************************************
Get the table flags to use for the statement. */
handler::Table_flags
ha_innobase::table_flags() const
{
2193 2194 2195
       /* Need to use tx_isolation here since table flags is (also)
          called before prebuilt is inited. */
        ulong const tx_isolation = thd_tx_isolation(current_thd);
2196 2197 2198 2199 2200
        if (tx_isolation <= ISO_READ_COMMITTED)
                return int_table_flags;
        return int_table_flags | HA_BINLOG_STMT_CAPABLE;
}

2201
/********************************************************************
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2202
Gives the file extension of an InnoDB single-table tablespace. */
2203 2204 2205 2206
static const char* ha_innobase_exts[] = {
  ".ibd",
  NullS
};
2207 2208 2209 2210

const char**
ha_innobase::bas_ext() const
/*========================*/
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2211
				/* out: file extension string */
2212
{
2213
  return ha_innobase_exts;
2214 2215
}

2216

2217 2218 2219
/*********************************************************************
Normalizes a table name string. A normalized name consists of the
database name catenated to '/' and table name. An example:
heikki@donna.mysql.fi's avatar
heikki@donna.mysql.fi committed
2220 2221
test/mytable. On Windows normalization puts both the database name and the
table name always to lower case. */
2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235
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 */

2236
	ptr = strend(name)-1;
2237 2238 2239 2240 2241 2242 2243

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

	name_ptr = ptr + 1;

monty@bitch.mysql.fi's avatar
monty@bitch.mysql.fi committed
2244
	DBUG_ASSERT(ptr > name);
2245 2246

	ptr--;
2247

2248 2249 2250 2251 2252 2253 2254 2255 2256
	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] = '/';
heikki@donna.mysql.fi's avatar
heikki@donna.mysql.fi committed
2257 2258

#ifdef __WIN__
marko@hundin.mysql.fi's avatar
marko@hundin.mysql.fi committed
2259
	innobase_casedn_str(norm_name);
heikki@donna.mysql.fi's avatar
heikki@donna.mysql.fi committed
2260
#endif
2261
}
2262

2263
/*********************************************************************
2264
Creates and opens a handle to a table which already exists in an InnoDB
2265 2266 2267 2268 2269 2270 2271
database. */

int
ha_innobase::open(
/*==============*/
					/* out: 1 if error, 0 if success */
	const char*	name,		/* in: table name */
2272 2273
	int		mode,		/* in: not used */
	uint		test_if_locked)	/* in: not used */
2274
{
2275
	dict_table_t*	ib_table;
2276
	char		norm_name[1000];
2277
	THD*		thd;
2278 2279
	ulint		retries = 0;
	char*		is_part = NULL;
2280 2281 2282 2283 2284 2285

	DBUG_ENTER("ha_innobase::open");

	UT_NOT_USED(mode);
	UT_NOT_USED(test_if_locked);

2286
	thd = ha_thd();
2287 2288
	normalize_table_name(norm_name, name);

2289 2290
	user_thd = NULL;

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2291 2292 2293 2294
	if (!(share=get_share(name))) {

		DBUG_RETURN(1);
	}
2295

2296 2297 2298 2299
	/* 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. */
2300

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2301 2302
	upd_and_key_val_buff_len =
				table->s->reclength + table->s->max_key_length
2303
							+ MAX_REF_PARTS * 3;
2304
	if (!(uchar*) my_multi_malloc(MYF(MY_WME),
2305 2306 2307 2308
			&upd_buff, upd_and_key_val_buff_len,
			&key_val_buff, upd_and_key_val_buff_len,
			NullS)) {
		free_share(share);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2309

2310 2311
		DBUG_RETURN(1);
	}
2312

2313 2314 2315 2316 2317 2318 2319
	/* We look for pattern #P# to see if the table is partitioned
	MySQL table. The retry logic for partitioned tables is a
	workaround for http://bugs.mysql.com/bug.php?id=33349. Look
	at support issue https://support.mysql.com/view.php?id=21080
	for more details. */
	is_part = strstr(norm_name, "#P#");
retry:
2320
	/* Get pointer to a table object in InnoDB dictionary cache */
2321
	ib_table = dict_table_get(norm_name, TRUE);
2322
	
2323
	if (NULL == ib_table) {
2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335
		if (is_part && retries < 10) {
			++retries;
			os_thread_sleep(100000);
			goto retry;
		}

		if (is_part) {
			sql_print_error("Failed to open table %s after "
					"%lu attemtps.\n", norm_name,
					retries);
		}

2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347
		sql_print_error("Cannot find or open table %s from\n"
				"the internal data dictionary of InnoDB "
				"though the .frm file for the\n"
				"table exists. Maybe you have deleted and "
				"recreated InnoDB data\n"
				"files but have forgotten to delete the "
				"corresponding .frm files\n"
				"of InnoDB tables, or you have moved .frm "
				"files to another database?\n"
				"or, the table contains indexes that this "
				"version of the engine\n"
				"doesn't support.\n"
2348
				"See http://dev.mysql.com/doc/refman/5.1/en/innodb-troubleshooting.html\n"
2349 2350
				"how you can resolve the problem.\n",
				norm_name);
2351
		free_share(share);
2352
		my_free(upd_buff, MYF(0));
2353
		my_errno = ENOENT;
2354

2355 2356
		DBUG_RETURN(HA_ERR_NO_SUCH_TABLE);
	}
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2357

2358
	if (ib_table->ibd_file_missing && !thd_tablespace_op(thd)) {
2359 2360 2361 2362 2363
		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"
2364
				"See http://dev.mysql.com/doc/refman/5.1/en/innodb-troubleshooting.html\n"
2365 2366
				"how you can resolve the problem.\n",
				norm_name);
2367
		free_share(share);
2368
		my_free(upd_buff, MYF(0));
2369
		my_errno = ENOENT;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2370

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2371
		dict_table_decrement_handle_count(ib_table);
2372 2373
		DBUG_RETURN(HA_ERR_NO_SUCH_TABLE);
	}
2374

2375
	prebuilt = row_create_prebuilt(ib_table);
2376

2377
	prebuilt->mysql_row_len = table->s->reclength;
2378

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2379 2380
	/* Looks like MySQL-3.23 sometimes has primary key number != 0 */

2381
	primary_key = table->s->primary_key;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2382
	key_used_on_scan = primary_key;
2383

2384 2385
	/* Allocate a buffer for a 'row reference'. A row reference is
	a string of bytes of length ref_length which uniquely specifies
2386 2387 2388
	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! */
2389

2390 2391
	if (!row_table_got_default_clust_index(ib_table)) {
		if (primary_key >= MAX_KEY) {
2392 2393
		  sql_print_error("Table %s has a primary key in InnoDB data "
				  "dictionary, but not in MySQL!", name);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2394
		}
2395

2396 2397
		prebuilt->clust_index_was_generated = FALSE;

2398
		/* MySQL allocates the buffer for ref. key_info->key_length
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2399 2400 2401 2402
		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. */
2403 2404

		ref_length = table->key_info[primary_key].key_length;
2405
	} else {
2406
		if (primary_key != MAX_KEY) {
2407 2408 2409 2410 2411 2412 2413 2414 2415
		  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);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2416 2417
		}

2418
		prebuilt->clust_index_was_generated = TRUE;
2419

2420
		ref_length = DATA_ROW_ID_LEN;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2421

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2422 2423 2424 2425 2426 2427 2428
		/* 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. */
2429

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2430
		if (key_used_on_scan != MAX_KEY) {
2431 2432 2433 2434
			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);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2435
		}
2436
	}
2437

2438
	stats.block_size = 16 * 1024;	/* Index block size in InnoDB: used by MySQL
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2439 2440
				in query optimization */

monty@donna.mysql.fi's avatar
Merge  
monty@donna.mysql.fi committed
2441
	/* Init table lock structure */
2442
	thr_lock_data_init(&share->lock,&lock,(void*) 0);
2443

2444
	info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST);
2445

2446
	DBUG_RETURN(0);
2447 2448
}

2449 2450 2451 2452 2453 2454
uint
ha_innobase::max_supported_key_part_length() const
{
	return(DICT_MAX_INDEX_COL_LEN - 1);
}

2455
/**********************************************************************
2456
Closes a handle to an InnoDB table. */
2457 2458 2459 2460

int
ha_innobase::close(void)
/*====================*/
2461
				/* out: 0 */
2462
{
2463 2464
	THD*	thd;

2465
	DBUG_ENTER("ha_innobase::close");
2466

2467 2468 2469 2470 2471
	thd = current_thd;  // avoid calling current_thd twice, it may be slow
	if (thd != NULL) {
		innobase_release_temporary_latches(ht, thd);
	}

2472
	row_prebuilt_free(prebuilt);
2473

2474
	my_free(upd_buff, MYF(0));
2475
	free_share(share);
2476

2477
	/* Tell InnoDB server that there might be work for
2478 2479 2480 2481
	utility threads: */

	srv_active_wake_master_thread();

2482
	DBUG_RETURN(0);
2483 2484
}

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496
/* 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 */
{
2497
	return((uint) (field->ptr - table->record[0]));
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2498 2499 2500 2501 2502
}

/******************************************************************
Checks if a field in a record is SQL NULL. Uses the record format
information in table to track the null bit in record. */
2503
static inline
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
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
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;
}

2549 2550
extern "C" {
/*****************************************************************
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2551 2552 2553 2554
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! */
2555 2556 2557

int
innobase_mysql_cmp(
2558
/*===============*/
2559 2560
					/* out: 1, 0, -1, if a is greater,
					equal, less than b, respectively */
2561
	int		mysql_type,	/* in: MySQL type */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2562
	uint		charset_number,	/* in: number of the charset */
2563 2564 2565 2566 2567 2568 2569
	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 */
{
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2570
	CHARSET_INFO*		charset;
2571
	enum_field_types	mysql_tp;
2572
	int			ret;
2573

monty@bitch.mysql.fi's avatar
monty@bitch.mysql.fi committed
2574 2575
	DBUG_ASSERT(a_length != UNIV_SQL_NULL);
	DBUG_ASSERT(b_length != UNIV_SQL_NULL);
2576 2577 2578 2579 2580

	mysql_tp = (enum_field_types) mysql_type;

	switch (mysql_tp) {

2581
	case MYSQL_TYPE_BIT:
2582
	case MYSQL_TYPE_STRING:
2583
	case MYSQL_TYPE_VAR_STRING:
2584 2585 2586 2587
	case MYSQL_TYPE_TINY_BLOB:
	case MYSQL_TYPE_MEDIUM_BLOB:
	case MYSQL_TYPE_BLOB:
	case MYSQL_TYPE_LONG_BLOB:
2588
	case MYSQL_TYPE_VARCHAR:
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601
		/* 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) {
2602 2603 2604 2605
			  sql_print_error("InnoDB needs charset %lu for doing "
					  "a comparison, but MySQL cannot "
					  "find that charset.",
					  (ulong) charset_number);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2606 2607 2608 2609
				ut_a(0);
			}
		}

2610 2611 2612 2613
		/* 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! */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2614

2615 2616 2617
		ret = charset->coll->strnncollsp(charset,
				  a, a_length,
						 b, b_length, 0);
2618
		if (ret < 0) {
2619
			return(-1);
2620
		} else if (ret > 0) {
2621
			return(1);
2622
		} else {
2623 2624
			return(0);
		}
2625 2626 2627 2628 2629 2630 2631 2632 2633
	default:
		assert(0);
	}

	return(0);
}
}

/******************************************************************
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2634 2635 2636
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'. */
2637 2638
inline
ulint
2639 2640
get_innobase_type_from_mysql_type(
/*==============================*/
2641 2642 2643 2644 2645
				/* 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 */
2646
{
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2647 2648 2649
	/* 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 */
2650

2651 2652 2653 2654 2655
	DBUG_ASSERT((ulint)MYSQL_TYPE_STRING < 256);
	DBUG_ASSERT((ulint)MYSQL_TYPE_VAR_STRING < 256);
	DBUG_ASSERT((ulint)MYSQL_TYPE_DOUBLE < 256);
	DBUG_ASSERT((ulint)MYSQL_TYPE_FLOAT < 256);
	DBUG_ASSERT((ulint)MYSQL_TYPE_DECIMAL < 256);
2656 2657 2658 2659 2660 2661 2662 2663

	if (field->flags & UNSIGNED_FLAG) {

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

2664 2665
	if (field->real_type() == MYSQL_TYPE_ENUM
		|| field->real_type() == MYSQL_TYPE_SET) {
2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677

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

2678
	switch (field->type()) {
2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702
		/* 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);
		}
2703
	case MYSQL_TYPE_NEWDECIMAL:
2704
		return(DATA_FIXBINARY);
2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715
	case MYSQL_TYPE_LONG:
	case MYSQL_TYPE_LONGLONG:
	case MYSQL_TYPE_TINY:
	case MYSQL_TYPE_SHORT:
	case MYSQL_TYPE_INT24:
	case MYSQL_TYPE_DATE:
	case MYSQL_TYPE_DATETIME:
	case MYSQL_TYPE_YEAR:
	case MYSQL_TYPE_NEWDATE:
	case MYSQL_TYPE_TIME:
	case MYSQL_TYPE_TIMESTAMP:
2716
		return(DATA_INT);
2717
	case MYSQL_TYPE_FLOAT:
2718
		return(DATA_FLOAT);
2719
	case MYSQL_TYPE_DOUBLE:
2720
		return(DATA_DOUBLE);
2721
	case MYSQL_TYPE_DECIMAL:
2722
		return(DATA_DECIMAL);
2723 2724 2725 2726 2727
	case MYSQL_TYPE_GEOMETRY:
	case MYSQL_TYPE_TINY_BLOB:
	case MYSQL_TYPE_MEDIUM_BLOB:
	case MYSQL_TYPE_BLOB:
	case MYSQL_TYPE_LONG_BLOB:
2728 2729 2730
		return(DATA_BLOB);
	default:
		assert(0);
2731 2732 2733 2734
	}

	return(0);
}
2735

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758
/***********************************************************************
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(
/*===============================*/
2759 2760
				/* out: value */
	const uchar*	buf)	/* in: from where to read */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2761
{
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2762
	return (uint) ((ulint)(buf[0]) + 256 * ((ulint)(buf[1])));
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2763 2764
}

2765
/***********************************************************************
2766
Stores a key value for a row to a buffer. */
2767 2768 2769 2770 2771

uint
ha_innobase::store_key_val_for_row(
/*===============================*/
				/* out: key value length as stored in buff */
2772
	uint		keynr,	/* in: key number */
2773
	char*		buff,	/* in/out: buffer for the key value (in MySQL
2774 2775
				format) */
	uint		buff_len,/* in: buffer length */
2776
	const uchar*	record)/* in: row in MySQL format */
2777
{
2778 2779 2780
	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;
2781
	char*		buff_start	= buff;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2782 2783 2784
	enum_field_types mysql_type;
	Field*		field;
	ibool		is_null;
2785

2786
	DBUG_ENTER("store_key_val_for_row");
2787

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801
	/* 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
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2802
	value is the SQL NULL then these data bytes are set to 0.
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2803

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2804 2805 2806 2807 2808 2809
	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
2810 2811
	simple memcmp to compare two key values to determine if they are
	equal. MySQL does this to compare contents of two 'ref' values. */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2812

2813
	bzero(buff, buff_len);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2814

2815 2816
	for (; key_part != end; key_part++) {
		is_null = FALSE;
2817

2818 2819
		if (key_part->null_bit) {
			if (record[key_part->null_offset]
2820
						& key_part->null_bit) {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2821 2822
				*buff = 1;
				is_null = TRUE;
2823
			} else {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2824 2825 2826
				*buff = 0;
			}
			buff++;
2827
		}
2828

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2829 2830 2831
		field = key_part->field;
		mysql_type = field->type();

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2832 2833 2834 2835 2836
		if (mysql_type == MYSQL_TYPE_VARCHAR) {
						/* >= 5.0.3 true VARCHAR */
			ulint	lenlen;
			ulint	len;
			byte*	data;
2837
			ulint	key_len;
2838
			ulint	true_len;
2839 2840
			CHARSET_INFO*	cs;
			int	error=0;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2841

2842 2843
			key_len = key_part->length;

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2844
			if (is_null) {
2845 2846
				buff += key_len + 2;

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2847 2848
				continue;
			}
2849
			cs = field->charset();
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2850 2851 2852 2853

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

2854
			data = row_mysql_read_true_varchar(&len,
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2855 2856 2857
				(byte*) (record
				+ (ulint)get_field_offset(table, field)),
				lenlen);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2858

2859 2860 2861 2862 2863 2864 2865 2866 2867
			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,
2868 2869
                                                (uint) (key_len /
                                                        cs->mbmaxlen),
2870 2871 2872
						&error);
			}

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2873 2874
			/* In a column prefix index, we may need to truncate
			the stored value: */
2875

2876 2877
			if (true_len > key_len) {
				true_len = key_len;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2878 2879
			}

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2880 2881 2882
			/* The length in a key value is always stored in 2
			bytes */

2883
			row_mysql_store_true_var_len((byte*)buff, true_len, 2);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2884 2885
			buff += 2;

2886
			memcpy(buff, data, true_len);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2887 2888 2889 2890 2891 2892 2893

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

2894
			buff += key_len;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2895

2896 2897 2898 2899
		} else if (mysql_type == MYSQL_TYPE_TINY_BLOB
			|| mysql_type == MYSQL_TYPE_MEDIUM_BLOB
			|| mysql_type == MYSQL_TYPE_BLOB
			|| mysql_type == MYSQL_TYPE_LONG_BLOB) {
2900

2901 2902
			CHARSET_INFO*	cs;
			ulint		key_len;
2903
			ulint		true_len;
2904
			int		error=0;
2905 2906
			ulint		blob_len;
			byte*		blob_data;
2907

2908
			ut_a(key_part->key_part_flag & HA_PART_KEY_SEG);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2909

2910 2911 2912 2913 2914
			key_len = key_part->length;

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

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2915
				continue;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2916
			}
2917 2918 2919 2920

			cs = field->charset();

			blob_data = row_mysql_read_blob_ref(&blob_len,
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2921
				(byte*) (record
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2922
				+ (ulint)get_field_offset(table, field)),
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2923 2924
					(ulint) field->pack_length());

2925 2926
			true_len = blob_len;

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2927
			ut_a(get_field_offset(table, field)
2928 2929 2930 2931 2932 2933 2934 2935 2936 2937
				== 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,
2938 2939
                                                (uint) (key_len /
                                                        cs->mbmaxlen),
2940 2941
						&error);
			}
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2942 2943 2944

			/* All indexes on BLOB and TEXT are column prefix
			indexes, and we may need to truncate the data to be
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2945
			stored in the key value: */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2946

2947 2948
			if (true_len > key_len) {
				true_len = key_len;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2949 2950 2951 2952 2953
			}

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

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2954
			innobase_write_to_2_little_endian(
2955
					(byte*)buff, true_len);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2956 2957
			buff += 2;

2958
			memcpy(buff, blob_data, true_len);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2959

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2960 2961 2962
			/* Note that we always reserve the maximum possible
			length of the BLOB prefix in the key value. */

2963
			buff += key_len;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2964
		} else {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2965 2966 2967 2968 2969
			/* 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. */

2970
			CHARSET_INFO*		cs;
2971 2972
			ulint			true_len;
			ulint			key_len;
2973
			const uchar*		src_start;
2974
			int			error=0;
2975 2976 2977 2978 2979 2980
			enum_field_types	real_type;

			key_len = key_part->length;

			if (is_null) {
				 buff += key_len;
2981

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2982 2983
				 continue;
			}
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2984

2985
			src_start = record + key_part->offset;
2986 2987
			real_type = field->real_type();
			true_len = key_len;
2988

2989 2990 2991 2992 2993
			/* 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. */

2994 2995
			if (real_type != MYSQL_TYPE_ENUM
				&& real_type != MYSQL_TYPE_SET
2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010
				&& ( 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,
3011 3012
                                                        (uint) (key_len /
                                                                cs->mbmaxlen),
3013 3014
							&error);
				}
3015 3016
			}

3017 3018
			memcpy(buff, src_start, true_len);
			buff += true_len;
3019

3020 3021 3022
			/* 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
3023 3024
			support surrogate pairs, which are needed to represent
			characters in the range U+10000 to U+10FFFF. */
3025

3026 3027 3028 3029
			if (true_len < key_len) {
				ulint pad_len = key_len - true_len;
				memset(buff, ' ', pad_len);
				buff += pad_len;
3030
			}
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3031
		}
3032
	}
3033

3034
	ut_a(buff <= buff_start + buff_len);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3035 3036

	DBUG_RETURN((uint)(buff - buff_start));
3037 3038 3039
}

/******************************************************************
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3040 3041
Builds a 'template' to the prebuilt struct. The template is used in fast
retrieval of just those column values MySQL needs in its processing. */
3042
static
3043
void
3044
build_template(
3045
/*===========*/
3046
	row_prebuilt_t*	prebuilt,	/* in/out: prebuilt struct */
3047 3048 3049 3050
	THD*		thd,		/* in: current user thread, used
					only if templ_type is
					ROW_MYSQL_REC_FIELDS */
	TABLE*		table,		/* in: MySQL table */
3051
	uint		templ_type)	/* in: ROW_MYSQL_WHOLE_ROW or
3052
					ROW_MYSQL_REC_FIELDS */
3053
{
3054 3055
	dict_index_t*	index;
	dict_index_t*	clust_index;
3056
	mysql_row_templ_t* templ;
3057
	Field*		field;
3058 3059
	ulint		n_fields;
	ulint		n_requested_fields	= 0;
monty@donna.mysql.fi's avatar
Merge  
monty@donna.mysql.fi committed
3060
	ibool		fetch_all_in_key	= FALSE;
3061
	ibool		fetch_primary_key_cols	= FALSE;
3062
	ulint		i;
3063 3064
	/* byte offset of the end of last requested column */
	ulint		mysql_prefix_len	= 0;
3065

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3066 3067 3068 3069
	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. */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3070

3071
		templ_type = ROW_MYSQL_WHOLE_ROW;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3072 3073
	}

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3074
	if (templ_type == ROW_MYSQL_REC_FIELDS) {
3075 3076
		if (prebuilt->hint_need_to_fetch_extra_cols
			== ROW_RETRIEVE_ALL_COLS) {
3077

3078 3079
			/* We know we must at least fetch all columns in the
			key, or all columns in the table */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3080

3081 3082 3083 3084 3085 3086 3087
			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 */
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3088

3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102
				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;
monty@donna.mysql.fi's avatar
Merge  
monty@donna.mysql.fi committed
3103
		}
3104 3105
	}

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3106
	clust_index = dict_table_get_first_index_noninline(prebuilt->table);
3107

3108
	if (templ_type == ROW_MYSQL_REC_FIELDS) {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3109
		index = prebuilt->index;
3110 3111
	} else {
		index = clust_index;
3112
	}
3113

3114 3115 3116 3117 3118 3119 3120
	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 */
	}
3121

3122
	n_fields = (ulint)table->s->fields; /* number of columns */
3123 3124 3125 3126 3127 3128

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

3130
	prebuilt->template_type = templ_type;
3131
	prebuilt->null_bitmap_len = table->s->null_bytes;
3132

3133 3134
	prebuilt->templ_contains_blob = FALSE;

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3135 3136
	/* Note that in InnoDB, i is the column number. MySQL calls columns
	'fields'. */
3137
	for (i = 0; i < n_fields; i++) {
3138
		templ = prebuilt->mysql_template + n_requested_fields;
3139 3140
		field = table->field[i];

3141 3142 3143 3144 3145
		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);
3146

3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159
			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;
			}

3160 3161
                        if (bitmap_is_set(table->read_set, i) ||
                            bitmap_is_set(table->write_set, i)) {
3162 3163 3164 3165
				/* This field is needed in the query */

				goto include_field;
			}
3166

3167
			if (fetch_primary_key_cols
3168 3169
				&& dict_table_col_in_clustered_key(
					index->table, i)) {
3170 3171 3172 3173
				/* This field is needed in the query */

				goto include_field;
			}
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3174 3175

			/* This field is not needed in the query, skip it */
3176 3177 3178

			goto skip_field;
		}
3179
include_field:
3180
		n_requested_fields++;
3181

3182
		templ->col_no = i;
3183

3184
		if (index == clust_index) {
3185 3186
			templ->rec_field_no = dict_col_get_clust_pos_noninline(
				&index->table->cols[i], index);
3187
		} else {
3188 3189
			templ->rec_field_no = dict_index_get_nth_col_pos(
								index, i);
3190 3191
		}

3192 3193 3194 3195 3196 3197 3198 3199
		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]);
3200

3201 3202 3203 3204
			templ->mysql_null_bit_mask = (ulint) field->null_bit;
		} else {
			templ->mysql_null_bit_mask = 0;
		}
3205

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3206 3207 3208
		templ->mysql_col_offset = (ulint)
					get_field_offset(table, field);

3209
		templ->mysql_col_len = (ulint) field->pack_length();
3210 3211 3212 3213 3214
		if (mysql_prefix_len < templ->mysql_col_offset
				+ templ->mysql_col_len) {
			mysql_prefix_len = templ->mysql_col_offset
				+ templ->mysql_col_len;
		}
3215
		templ->type = index->table->cols[i].mtype;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3216 3217 3218 3219
		templ->mysql_type = (ulint)field->type();

		if (templ->mysql_type == DATA_MYSQL_TRUE_VARCHAR) {
			templ->mysql_length_bytes = (ulint)
3220
				(((Field_varstring*)field)->length_bytes);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3221
		}
3222

3223
		templ->charset = dtype_get_charset_coll_noninline(
3224 3225 3226 3227
				index->table->cols[i].prtype);
		templ->mbminlen = index->table->cols[i].mbminlen;
		templ->mbmaxlen = index->table->cols[i].mbmaxlen;
		templ->is_unsigned = index->table->cols[i].prtype
3228
							& DATA_UNSIGNED;
3229 3230
		if (templ->type == DATA_BLOB) {
			prebuilt->templ_contains_blob = TRUE;
3231
		}
3232 3233 3234
skip_field:
		;
	}
3235

3236
	prebuilt->n_template = n_requested_fields;
3237
	prebuilt->mysql_prefix_len = mysql_prefix_len;
3238

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3239
	if (index != clust_index && prebuilt->need_to_access_clustered) {
3240 3241 3242 3243
		/* 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;
3244

3245 3246 3247
			templ->rec_field_no = dict_col_get_clust_pos_noninline(
				&index->table->cols[templ->col_no],
				clust_index);
3248
		}
3249
	}
3250 3251
}

3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266
/************************************************************************
This special handling is really to overcome the limitations of MySQL's
binlogging. We need to eliminate the non-determinism that will arise in
INSERT ... SELECT type of statements, since MySQL binlog only stores the
min value of the autoinc interval. Once that is fixed we can get rid of
the special lock handling.*/

ulong
ha_innobase::innobase_autoinc_lock(void)
/*====================================*/
					/* out: DB_SUCCESS if all OK else
					error code */
{
	ulint		error = DB_SUCCESS;

3267 3268 3269
	switch (innobase_autoinc_lock_mode) {
	case AUTOINC_NO_LOCKING:
		/* Acquire only the AUTOINC mutex. */
3270
		dict_table_autoinc_lock(prebuilt->table);
3271
		break;
3272

3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291
	case AUTOINC_NEW_STYLE_LOCKING:
		/* For simple (single/multi) row INSERTs, we fallback to the
		old style only if another transaction has already acquired
		the AUTOINC lock on behalf of a LOAD FILE or INSERT ... SELECT
		etc. type of statement. */
		if (thd_sql_command(user_thd) == SQLCOM_INSERT) {
			dict_table_t*	table = prebuilt->table;

			/* Acquire the AUTOINC mutex. */
			dict_table_autoinc_lock(table);

			/* We need to check that another transaction isn't
			already holding the AUTOINC lock on the table. */
			if (table->n_waiting_or_granted_auto_inc_locks) {
				/* Release the mutex to avoid deadlocks. */
				dict_table_autoinc_unlock(table);
			} else {
				break;
			}
3292
		}
3293 3294 3295
		/* Fall through to old style locking. */

	case AUTOINC_OLD_STYLE_LOCKING:
3296 3297 3298
		error = row_lock_table_autoinc_for_mysql(prebuilt);

		if (error == DB_SUCCESS) {
3299 3300

			/* Acquire the AUTOINC mutex. */
3301 3302
			dict_table_autoinc_lock(prebuilt->table);
		}
3303 3304 3305 3306
		break;

	default:
		ut_error;
3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360
	}

	return(ulong(error));
}

/************************************************************************
Reset the autoinc value in the table.*/

ulong
ha_innobase::innobase_reset_autoinc(
/*================================*/
					/* out: DB_SUCCESS if all went well
					else error code */
	ulonglong	autoinc)	/* in: value to store */
{
	ulint		error;

	error = innobase_autoinc_lock();

	if (error == DB_SUCCESS) {

		dict_table_autoinc_initialize(prebuilt->table, autoinc);

		dict_table_autoinc_unlock(prebuilt->table);
	}

	return(ulong(error));
}

/************************************************************************
Store the autoinc value in the table. The autoinc value is only set if
it's greater than the existing autoinc value in the table.*/

ulong
ha_innobase::innobase_set_max_autoinc(
/*==================================*/
					/* out: DB_SUCCES if all went well
					else error code */
	ulonglong	auto_inc)	/* in: value to store */
{
	ulint		error;

	error = innobase_autoinc_lock();

	if (error == DB_SUCCESS) {

		dict_table_autoinc_update(prebuilt->table, auto_inc);

		dict_table_autoinc_unlock(prebuilt->table);
	}

	return(ulong(error));
}

3361
/************************************************************************
3362
Stores a row in an InnoDB database, to the table specified in this
3363 3364 3365 3366 3367
handle. */

int
ha_innobase::write_row(
/*===================*/
3368 3369
			/* out: error code */
	uchar*	record)	/* in: a row in MySQL format */
3370
{
3371
	int		error = 0;
3372
	ibool		auto_inc_used= FALSE;
3373 3374
	ulint		sql_command;
	trx_t*		trx = thd_to_trx(user_thd);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3375

3376
	DBUG_ENTER("ha_innobase::write_row");
3377

3378
	if (prebuilt->trx != trx) {
3379 3380
	  sql_print_error("The transaction object for the table handle is at "
			  "%p, but for the current thread it is at %p",
3381
			  prebuilt->trx, trx);
3382

3383 3384 3385
		fputs("InnoDB: Dump of 200 bytes around prebuilt: ", stderr);
		ut_print_buf(stderr, ((const byte*)prebuilt) - 100, 200);
		fputs("\n"
3386
			"InnoDB: Dump of 200 bytes around ha_data: ",
3387
			stderr);
3388
		ut_print_buf(stderr, ((const byte*) trx) - 100, 200);
3389 3390
		putc('\n', stderr);
		ut_error;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3391
	}
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3392

3393
	ha_statistic_increment(&SSV::ha_write_count);
3394

3395 3396
	if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
		table->timestamp_field->set_time();
3397

3398 3399 3400 3401 3402 3403 3404
	sql_command = thd_sql_command(user_thd);

	if ((sql_command == SQLCOM_ALTER_TABLE
	     || sql_command == SQLCOM_OPTIMIZE
	     || sql_command == SQLCOM_CREATE_INDEX
	     || sql_command == SQLCOM_DROP_INDEX)
	    && num_write_row >= 10000) {
3405 3406 3407 3408 3409 3410 3411 3412
		/* 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. */
3413

3414
		dict_table_t*	src_table;
marko@hundin.mysql.fi's avatar
marko@hundin.mysql.fi committed
3415
		ulint		mode;
3416

3417
		num_write_row = 0;
3418

marko@hundin.mysql.fi's avatar
marko@hundin.mysql.fi committed
3419 3420
		/* Commit the transaction.  This will release the table
		locks, so they have to be acquired again. */
3421 3422 3423 3424 3425 3426

		/* Altering an InnoDB table */
		/* Get the source table. */
		src_table = lock_get_src_table(
				prebuilt->trx, prebuilt->table, &mode);
		if (!src_table) {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3427
no_commit:
3428 3429 3430 3431
			/* Unknown situation: do not commit */
			/*
			ut_print_timestamp(stderr);
			fprintf(stderr,
3432
				"  InnoDB: ALTER TABLE is holding lock"
3433 3434 3435 3436 3437 3438 3439 3440
				" 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. */

3441
			/* Altering to InnoDB format */
3442
			innobase_commit(ht, user_thd, 1);
3443
			/* Note that this transaction is still active. */
3444
			prebuilt->trx->active_trans = 1;
3445
			/* We will need an IX lock on the destination table. */
3446
			prebuilt->sql_stat_start = TRUE;
3447 3448 3449
		} else {
			/* Ensure that there are no other table locks than
			LOCK_IX and LOCK_AUTO_INC on the destination table. */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3450

3451 3452
			if (!lock_is_table_exclusive(prebuilt->table,
							prebuilt->trx)) {
3453 3454 3455 3456 3457
				goto no_commit;
			}

			/* Commit the transaction.  This will release the table
			locks, so they have to be acquired again. */
3458
			innobase_commit(ht, user_thd, 1);
3459
			/* Note that this transaction is still active. */
3460
			prebuilt->trx->active_trans = 1;
3461
			/* Re-acquire the table lock on the source table. */
3462
			row_lock_table_for_mysql(prebuilt, src_table, mode);
3463
			/* We will need an IX lock on the destination table. */
3464
			prebuilt->sql_stat_start = TRUE;
3465
		}
3466 3467
	}

marko@hundin.mysql.fi's avatar
marko@hundin.mysql.fi committed
3468 3469
	num_write_row++;

3470
	/* This is the case where the table has an auto-increment column */
3471
	if (table->next_number_field && record == table->record[0]) {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3472

3473
		if ((error = update_auto_increment())) {
3474 3475 3476 3477

			goto func_exit;
		}

3478
		auto_inc_used = TRUE;
3479
	}
3480

3481
	if (prebuilt->mysql_template == NULL
3482 3483
	    || prebuilt->template_type != ROW_MYSQL_WHOLE_ROW) {

3484 3485
		/* Build the template used in converting quickly between
		the two database formats */
3486

3487 3488
		build_template(prebuilt, NULL, table, ROW_MYSQL_WHOLE_ROW);
	}
3489

3490
	innodb_srv_conc_enter_innodb(prebuilt->trx);
monty@donna.mysql.fi's avatar
Merge  
monty@donna.mysql.fi committed
3491

3492
	error = row_insert_for_mysql((byte*) record, prebuilt);
monty@donna.mysql.fi's avatar
Merge  
monty@donna.mysql.fi committed
3493

3494 3495
	/* Handle duplicate key errors */
	if (auto_inc_used) {
3496
		ulint		err;
3497
		ulonglong	auto_inc;
monty@donna.mysql.fi's avatar
Merge  
monty@donna.mysql.fi committed
3498

3499 3500 3501 3502 3503 3504 3505
		/* Note the number of rows processed for this statement, used
		by get_auto_increment() to determine the number of AUTO-INC
		values to reserve. This is only useful for a mult-value INSERT
		and is a statement level counter.*/
		if (trx->n_autoinc_rows > 0) {
			--trx->n_autoinc_rows;
		}
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3506

3507
		/* Get the value that MySQL attempted to store in the table.*/
3508
		auto_inc = table->next_number_field->val_int();
3509

3510 3511
		switch (error) {
		case DB_DUPLICATE_KEY:
3512

3513 3514 3515 3516
			/* A REPLACE command and LOAD DATA INFILE REPLACE
			handle a duplicate key error themselves, but we
			must update the autoinc counter if we are performing
			those statements. */
3517

3518 3519 3520 3521
			switch (sql_command) {
			case SQLCOM_LOAD:
				if ((trx->duplicates
				    & (TRX_DUP_IGNORE | TRX_DUP_REPLACE))) {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3522

3523 3524 3525
					goto set_max_autoinc;
				}
				break;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3526

3527 3528 3529 3530 3531
			case SQLCOM_REPLACE:
			case SQLCOM_INSERT_SELECT:
			case SQLCOM_REPLACE_SELECT:
				goto set_max_autoinc;
				break;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3532

3533 3534 3535
			default:
				break;
			}
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3536

3537 3538 3539 3540 3541 3542 3543 3544 3545 3546
			break;

		case DB_SUCCESS:
			/* If the actual value inserted is greater than
			the upper limit of the interval, then we try and
			update the table upper limit. Note: last_value
			will be 0 if get_auto_increment() was not called.*/

			if (auto_inc > prebuilt->last_value) {
set_max_autoinc:
3547
				ut_a(prebuilt->table->autoinc_increment > 0);
3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560

				ulonglong	have;
				ulonglong	need;

				/* Check for overflow conditions. */
				need = prebuilt->table->autoinc_increment;
				have = ~0x0ULL - auto_inc;

				if (have < need) {
					need = have;
				}

				auto_inc += need;
3561

3562 3563 3564
				err = innobase_set_max_autoinc(auto_inc);

				if (err != DB_SUCCESS) {
3565
					error = (int) err;
3566
				}
3567 3568
			}
			break;
3569 3570
		}
	}
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3571

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3572
	innodb_srv_conc_exit_innodb(prebuilt->trx);
monty@donna.mysql.fi's avatar
Merge  
monty@donna.mysql.fi committed
3573

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3574
	error = convert_error_code_to_mysql(error, user_thd);
3575

3576
func_exit:
3577
	innobase_active_small();
3578

3579
	DBUG_RETURN(error);
3580 3581
}

3582 3583 3584 3585 3586 3587 3588 3589 3590
/**************************************************************************
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 */
3591 3592
	uchar*		old_row,	/* in: old row in MySQL format */
	uchar*		new_row,	/* in: new row in MySQL format */
3593 3594
	struct st_table* table,		/* in: table in MySQL data
					dictionary */
3595
	uchar*		upd_buff,	/* in: buffer to use */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3596
	ulint		buff_len,	/* in: buffer length */
3597
	row_prebuilt_t*	prebuilt,	/* in: InnoDB prebuilt struct */
3598 3599
	THD*		thd)		/* in: user thread */
{
3600
	uchar*		original_upd_buff = upd_buff;
3601
	Field*		field;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3602
	enum_field_types field_mysql_type;
3603 3604 3605
	uint		n_fields;
	ulint		o_len;
	ulint		n_len;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3606
	ulint		col_pack_len;
3607
	byte*		new_mysql_row_col;
3608 3609 3610
	byte*		o_ptr;
	byte*		n_ptr;
	byte*		buf;
3611
	upd_field_t*	ufield;
3612
	ulint		col_type;
3613
	ulint		n_changed = 0;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3614
	dfield_t	dfield;
3615
	dict_index_t*	clust_index;
3616
	uint		i;
3617

3618
	n_fields = table->s->fields;
3619
	clust_index = dict_table_get_first_index_noninline(prebuilt->table);
3620

3621
	/* We use upd_buff to convert changed fields */
3622
	buf = (byte*) upd_buff;
3623

3624 3625 3626
	for (i = 0; i < n_fields; i++) {
		field = table->field[i];

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3627 3628
		o_ptr = (byte*) old_row + get_field_offset(table, field);
		n_ptr = (byte*) new_row + get_field_offset(table, field);
3629

3630 3631 3632
		/* Use new_mysql_row_col and col_pack_len save the values */

		new_mysql_row_col = n_ptr;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3633
		col_pack_len = field->pack_length();
3634

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3635 3636
		o_len = col_pack_len;
		n_len = col_pack_len;
3637

3638
		/* We use o_ptr and n_ptr to dig up the actual data for
3639
		comparison. */
3640

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3641
		field_mysql_type = field->type();
3642

3643
		col_type = prebuilt->table->cols[i].mtype;
3644 3645 3646 3647 3648 3649

		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);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3650

3651
			break;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3652

3653 3654 3655
		case DATA_VARCHAR:
		case DATA_BINARY:
		case DATA_VARMYSQL:
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3656 3657 3658 3659
			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 */
3660

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3661
				o_ptr = row_mysql_read_true_varchar(
3662 3663 3664 3665
					&o_len, o_ptr,
					(ulint)
					(((Field_varstring*)field)->length_bytes));

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3666
				n_ptr = row_mysql_read_true_varchar(
3667 3668 3669
					&n_len, n_ptr,
					(ulint)
					(((Field_varstring*)field)->length_bytes));
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3670 3671 3672
			}

			break;
3673 3674 3675
		default:
			;
		}
3676

3677
		if (field->null_ptr) {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3678 3679
			if (field_in_record_is_null(table, field,
							(char*) old_row)) {
3680 3681
				o_len = UNIV_SQL_NULL;
			}
3682

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3683 3684
			if (field_in_record_is_null(table, field,
							(char*) new_row)) {
3685 3686 3687 3688 3689 3690 3691 3692 3693
				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;
3694

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3695 3696 3697
			/* Let us use a dummy dfield to make the conversion
			from the MySQL column format to the InnoDB format */

3698 3699
			dict_col_copy_type_noninline(prebuilt->table->cols + i,
						     &dfield.type);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3700 3701 3702

			if (n_len != UNIV_SQL_NULL) {
				buf = row_mysql_store_col_in_innobase_format(
3703 3704 3705 3706 3707
					&dfield,
					(byte*)buf,
					TRUE,
					new_mysql_row_col,
					col_pack_len,
3708 3709
					dict_table_is_comp_noninline(
							prebuilt->table));
3710 3711
				ufield->new_val.data = dfield.data;
				ufield->new_val.len = dfield.len;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3712 3713 3714 3715
			} else {
				ufield->new_val.data = NULL;
				ufield->new_val.len = UNIV_SQL_NULL;
			}
3716 3717

			ufield->exp = NULL;
3718 3719
			ufield->field_no = dict_col_get_clust_pos_noninline(
				&prebuilt->table->cols[i], clust_index);
3720 3721 3722 3723 3724 3725 3726
			n_changed++;
		}
	}

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

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3727 3728
	ut_a(buf <= (byte*)original_upd_buff + buff_len);

3729 3730 3731 3732 3733 3734 3735
	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.
3736
TODO: currently InnoDB does not prevent the 'Halloween problem':
3737 3738
in a searched update a single row can get updated several times
if its index columns are updated! */
3739

3740 3741 3742 3743
int
ha_innobase::update_row(
/*====================*/
					/* out: error number or 0 */
3744 3745
	const uchar*	old_row,	/* in: old row in MySQL format */
	uchar*		new_row)	/* in: new row in MySQL format */
3746 3747 3748
{
	upd_t*		uvect;
	int		error = 0;
3749
	trx_t*		trx = thd_to_trx(user_thd);
3750

3751
	DBUG_ENTER("ha_innobase::update_row");
3752

3753
	ut_a(prebuilt->trx == trx);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3754

3755 3756
	if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
		table->timestamp_field->set_time();
3757

3758 3759 3760 3761 3762
	if (prebuilt->upd_node) {
		uvect = prebuilt->upd_node->update;
	} else {
		uvect = row_get_prebuilt_update_vector(prebuilt);
	}
3763 3764 3765 3766

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

3767
	calc_row_difference(uvect, (uchar*) old_row, new_row, table,
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3768 3769 3770
			upd_buff, (ulint)upd_and_key_val_buff_len,
			prebuilt, user_thd);

3771 3772 3773
	/* This is not a delete */
	prebuilt->upd_node->is_delete = FALSE;

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3774
	assert(prebuilt->template_type == ROW_MYSQL_WHOLE_ROW);
3775

3776
	innodb_srv_conc_enter_innodb(trx);
monty@donna.mysql.fi's avatar
Merge  
monty@donna.mysql.fi committed
3777

3778
	error = row_update_for_mysql((byte*) old_row, prebuilt);
3779

3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801
	/* We need to do some special AUTOINC handling for the following case:

	INSERT INTO t (c1,c2) VALUES(x,y) ON DUPLICATE KEY UPDATE ...

	We need to use the AUTOINC counter that was actually used by
	MySQL in the UPDATE statement, which can be different from the
	value used in the INSERT statement.*/

	if (error == DB_SUCCESS
	    && table->next_number_field
	    && new_row == table->record[0]
	    && thd_sql_command(user_thd) == SQLCOM_INSERT
	    && (trx->duplicates & (TRX_DUP_IGNORE | TRX_DUP_REPLACE))
		== TRX_DUP_IGNORE)  {

		longlong	auto_inc;

		auto_inc = table->next_number_field->val_int();

		if (auto_inc != 0) {
			auto_inc += prebuilt->table->autoinc_increment;

3802
			error = innobase_set_max_autoinc(auto_inc);
3803 3804 3805
		}
	}

3806
	innodb_srv_conc_exit_innodb(trx);
monty@donna.mysql.fi's avatar
Merge  
monty@donna.mysql.fi committed
3807

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3808
	error = convert_error_code_to_mysql(error, user_thd);
3809

3810 3811 3812 3813 3814 3815 3816 3817 3818 3819
	if (error == 0 /* success */
	    && uvect->n_fields == 0 /* no columns were updated */) {

		/* This is the same as success, but instructs
		MySQL that the row is not really updated and it
		should not increase the count of updated rows.
		This is fix for http://bugs.mysql.com/29157 */
		error = HA_ERR_RECORD_IS_THE_SAME;
	}

3820
	/* Tell InnoDB server that there might be work for
3821 3822
	utility threads: */

3823
	innobase_active_small();
3824 3825 3826 3827 3828 3829 3830 3831 3832 3833

	DBUG_RETURN(error);
}

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

int
ha_innobase::delete_row(
/*====================*/
3834 3835
				/* out: error number or 0 */
	const uchar*	record)	/* in: a row in MySQL format */
3836 3837
{
	int		error = 0;
3838
	trx_t*		trx = thd_to_trx(user_thd);
3839

3840
	DBUG_ENTER("ha_innobase::delete_row");
3841

3842
	ut_a(prebuilt->trx == trx);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3843

3844 3845 3846 3847
	/* Only if the table has an AUTOINC column */
	if (table->found_next_number_field && record == table->record[0]) {
		ulonglong	dummy = 0;

3848 3849 3850 3851
		/* First check whether the AUTOINC sub-system has been
		initialized using the AUTOINC mutex. If not then we
		do it the "proper" way, by acquiring the heavier locks. */
		dict_table_autoinc_lock(prebuilt->table);
3852

3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863
		if (!prebuilt->table->autoinc_inited) {
			dict_table_autoinc_unlock(prebuilt->table);

			error = innobase_get_auto_increment(&dummy);

			if (error == DB_SUCCESS) {
				dict_table_autoinc_unlock(prebuilt->table);
			} else {
				goto error_exit;
			}
		} else  {
3864 3865 3866 3867
			dict_table_autoinc_unlock(prebuilt->table);
		}
	}

3868 3869 3870
	if (!prebuilt->upd_node) {
		row_get_prebuilt_update_vector(prebuilt);
	}
3871 3872

	/* This is a delete */
3873

3874
	prebuilt->upd_node->is_delete = TRUE;
3875

3876
	innodb_srv_conc_enter_innodb(trx);
monty@donna.mysql.fi's avatar
Merge  
monty@donna.mysql.fi committed
3877

3878
	error = row_update_for_mysql((byte*) record, prebuilt);
3879

3880
	innodb_srv_conc_exit_innodb(trx);
monty@donna.mysql.fi's avatar
Merge  
monty@donna.mysql.fi committed
3881

3882
error_exit:
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3883
	error = convert_error_code_to_mysql(error, user_thd);
3884

3885
	/* Tell the InnoDB server that there might be work for
3886 3887
	utility threads: */

3888
	innobase_active_small();
3889 3890 3891 3892

	DBUG_RETURN(error);
}

3893
/**************************************************************************
3894
Removes a new lock set on a row, if it was not read optimistically. This can
3895 3896
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. */
3897 3898 3899 3900 3901 3902 3903

void
ha_innobase::unlock_row(void)
/*=========================*/
{
	DBUG_ENTER("ha_innobase::unlock_row");

3904 3905 3906 3907 3908 3909 3910
	/* Consistent read does not take any locks, thus there is
	nothing to unlock. */

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

3911 3912
	switch (prebuilt->row_read_type) {
	case ROW_READ_WITH_LOCKS:
3913 3914
		if (!srv_locks_unsafe_for_binlog
		|| prebuilt->trx->isolation_level == TRX_ISO_READ_COMMITTED) {
3915 3916 3917 3918
			break;
		}
		/* fall through */
	case ROW_READ_TRY_SEMI_CONSISTENT:
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3919
		row_unlock_for_mysql(prebuilt, FALSE);
3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941
		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)
/*=======================================*/
{
	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)
/*===========================================*/
{
3942
	ut_a(prebuilt->trx == thd_to_trx(ha_thd()));
3943

3944 3945 3946 3947 3948
	/* 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. */

3949 3950
	if (yes
	    && (srv_locks_unsafe_for_binlog
3951
		|| prebuilt->trx->isolation_level == TRX_ISO_READ_COMMITTED)) {
3952 3953 3954
		prebuilt->row_read_type = ROW_READ_TRY_SEMI_CONSISTENT;
	} else {
		prebuilt->row_read_type = ROW_READ_WITH_LOCKS;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3955
	}
3956 3957
}

3958 3959 3960 3961 3962 3963 3964
/**********************************************************************
Initializes a handle to use an index. */

int
ha_innobase::index_init(
/*====================*/
			/* out: 0 or error number */
3965 3966
	uint	keynr,	/* in: key (index) number */
	bool sorted)	/* in: 1 if result MUST be sorted according to index */
3967
{
3968 3969
	int	error	= 0;
	DBUG_ENTER("index_init");
3970

monty@donna.mysql.fi's avatar
Merge  
monty@donna.mysql.fi committed
3971
	error = change_active_index(keynr);
3972

3973
	DBUG_RETURN(error);
3974 3975 3976
}

/**********************************************************************
3977
Currently does nothing. */
3978 3979 3980 3981 3982

int
ha_innobase::index_end(void)
/*========================*/
{
3983 3984 3985 3986
	int	error	= 0;
	DBUG_ENTER("index_end");
	active_index=MAX_KEY;
	DBUG_RETURN(error);
3987 3988 3989 3990
}

/*************************************************************************
Converts a search mode flag understood by MySQL to a flag understood
3991
by InnoDB. */
3992 3993 3994 3995 3996 3997 3998
inline
ulint
convert_search_mode_to_innobase(
/*============================*/
	enum ha_rkey_function	find_flag)
{
	switch (find_flag) {
3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043
	case HA_READ_KEY_EXACT:
		/* this does not require the index to be UNIQUE */
		return(PAGE_CUR_GE);
	case HA_READ_KEY_OR_NEXT:
		return(PAGE_CUR_GE);
	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);
	case HA_READ_PREFIX_LAST:
		return(PAGE_CUR_LE);
	case HA_READ_PREFIX_LAST_OR_PREV:
		return(PAGE_CUR_LE);
		/* 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
		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
		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. */
	case HA_READ_MBR_CONTAIN:
	case HA_READ_MBR_INTERSECT:
	case HA_READ_MBR_WITHIN:
	case HA_READ_MBR_DISJOINT:
	case HA_READ_MBR_EQUAL:
		my_error(ER_TABLE_CANT_HANDLE_SPKEYS, MYF(0));
		return(PAGE_CUR_UNSUPP);
	/* do not use "default:" in order to produce a gcc warning:
	enumeration value '...' not handled in switch
	(if -Wswitch or -Wall is used) */
	}

	my_error(ER_CHECK_NOT_IMPLEMENTED, MYF(0), "this functionality");

	return(PAGE_CUR_UNSUPP);
4044
}
4045

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4046 4047 4048 4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 4086 4087 4088 4089 4090 4091 4092 4093 4094
/*
   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. */


4095 4096 4097 4098 4099 4100 4101 4102 4103
/**************************************************************************
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 */
4104
	uchar*		buf,		/* in/out: buffer for the returned
4105
					row */
4106
	const uchar*	key_ptr,	/* in: key value; if this is NULL
4107
					we position the cursor at the
4108 4109 4110
					start or end of index; this can
					also contain an InnoDB row id, in
					which case key_len is the InnoDB
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4111 4112 4113 4114
					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 */
4115
	uint			key_len,/* in: key value length */
4116 4117 4118 4119
	enum ha_rkey_function find_flag)/* in: search flags from my_base.h */
{
	ulint		mode;
	dict_index_t*	index;
4120 4121
	ulint		match_mode	= 0;
	int		error;
4122 4123
	ulint		ret;

4124
	DBUG_ENTER("index_read");
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4125

4126
	ut_a(prebuilt->trx == thd_to_trx(user_thd));
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4127

4128
	ha_statistic_increment(&SSV::ha_read_key_count);
4129

4130
	index = prebuilt->index;
4131

4132
	/* Note that if the index for which the search template is built is not
4133
	necessarily prebuilt->index, but can also be the clustered index */
4134

4135 4136 4137 4138
	if (prebuilt->sql_stat_start) {
		build_template(prebuilt, user_thd, table,
							ROW_MYSQL_REC_FIELDS);
	}
4139 4140

	if (key_ptr) {
4141
		/* Convert the search key value to InnoDB format into
4142 4143
		prebuilt->search_tuple */

4144
		row_sel_convert_mysql_key_to_innobase(prebuilt->search_tuple,
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4145 4146 4147 4148
					(byte*) key_val_buff,
					(ulint)upd_and_key_val_buff_len,
					index,
					(byte*) key_ptr,
4149
					(ulint) key_len, prebuilt->trx);
4150 4151 4152 4153
	} else {
		/* We position the cursor to the last or the first entry
		in the index */

4154
		dtuple_set_n_fields(prebuilt->search_tuple, 0);
4155
	}
4156

4157 4158 4159 4160 4161 4162 4163 4164 4165 4166 4167 4168
	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;
	}

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4169
	last_match_mode = (uint) match_mode;
4170

4171
	if (mode != PAGE_CUR_UNSUPP) {
monty@donna.mysql.fi's avatar
Merge  
monty@donna.mysql.fi committed
4172

4173
		innodb_srv_conc_enter_innodb(prebuilt->trx);
4174

4175 4176 4177 4178 4179 4180 4181 4182
		ret = row_search_for_mysql((byte*) buf, mode, prebuilt,
					   match_mode, 0);

		innodb_srv_conc_exit_innodb(prebuilt->trx);
	} else {

		ret = DB_UNSUPPORTED;
	}
monty@donna.mysql.fi's avatar
Merge  
monty@donna.mysql.fi committed
4183

4184 4185 4186 4187 4188 4189 4190 4191 4192 4193 4194 4195
	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 {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4196
		error = convert_error_code_to_mysql((int) ret, user_thd);
4197 4198
		table->status = STATUS_NOT_FOUND;
	}
4199

4200 4201 4202
	DBUG_RETURN(error);
}

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4203 4204 4205
/***********************************************************************
The following functions works like index_read, but it find the last
row with the current key value or prefix. */
4206 4207

int
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4208 4209
ha_innobase::index_read_last(
/*=========================*/
4210 4211 4212 4213 4214 4215 4216
				/* out: 0, HA_ERR_KEY_NOT_FOUND, or an
				error code */
	uchar*		buf,	/* out: fetched row */
	const uchar*	key_ptr,/* in: key value, or a prefix of a full
				key value */
	uint		key_len)/* in: length of the key val or prefix
				in bytes */
4217
{
4218
	return(index_read(buf, key_ptr, key_len, HA_READ_PREFIX_LAST));
4219 4220
}

4221
/************************************************************************
4222
Get the index for a handle. Does not change active index.*/
4223

4224 4225 4226 4227 4228 4229 4230
dict_index_t*
ha_innobase::innobase_get_index(
/*============================*/
				/* out: NULL or index instance. */
	uint		keynr)	/* in: use this index; MAX_KEY means always
				clustered index, even if it was internally
				generated by InnoDB */
4231
{
4232 4233 4234 4235
	KEY*		key = 0;
	dict_index_t*	index = 0;

	DBUG_ENTER("innobase_get_index");
antony@ppcg5.local's avatar
antony@ppcg5.local committed
4236
	ha_statistic_increment(&SSV::ha_read_key_count);
4237

4238 4239
	ut_ad(user_thd == ha_thd());
	ut_a(prebuilt->trx == thd_to_trx(user_thd));
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4240

4241
	if (keynr != MAX_KEY && table->s->keys > 0) {
4242
		key = table->key_info + keynr;
4243

4244
		index = dict_table_get_index_noninline(
4245 4246
			prebuilt->table, key->name);
	} else {
4247
		index = dict_table_get_first_index_noninline(prebuilt->table);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4248
	}
4249

4250
	if (!index) {
4251 4252 4253 4254 4255
		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);
4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275
	}

	DBUG_RETURN(index);
}

/************************************************************************
Changes the active index of a handle. */

int
ha_innobase::change_active_index(
/*=============================*/
			/* out: 0 or error code */
	uint	keynr)	/* in: use this index; MAX_KEY means always clustered
			index, even if it was internally generated by
			InnoDB */
{
	DBUG_ENTER("change_active_index");

	ut_ad(user_thd == ha_thd());
	ut_a(prebuilt->trx == thd_to_trx(user_thd));
4276

4277 4278 4279
	active_index = keynr;

	prebuilt->index = innobase_get_index(keynr);
4280

4281
	if (!prebuilt->index) {
4282
		DBUG_RETURN(1);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4283
	}
4284

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4285
	assert(prebuilt->search_tuple != 0);
monty@donna.mysql.fi's avatar
Merge  
monty@donna.mysql.fi committed
4286

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4287
	dtuple_set_n_fields(prebuilt->search_tuple, prebuilt->index->n_fields);
4288

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4289
	dict_index_copy_types(prebuilt->search_tuple, prebuilt->index,
4290
			prebuilt->index->n_fields);
4291

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4292 4293 4294 4295 4296
	/* 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. */
4297

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4298
	build_template(prebuilt, user_thd, table, ROW_MYSQL_REC_FIELDS);
4299

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4300
	DBUG_RETURN(0);
4301 4302 4303 4304 4305 4306 4307 4308 4309 4310 4311
}

/**************************************************************************
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 */
4312
	uchar*		buf,		/* in/out: buffer for the returned
4313
					row */
4314
	uint		keynr,		/* in: use this index */
4315
	const uchar*	key,		/* in: key value; if this is NULL
4316 4317 4318 4319 4320
					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 */
{
monty@donna.mysql.fi's avatar
Merge  
monty@donna.mysql.fi committed
4321 4322 4323 4324
	if (change_active_index(keynr)) {

		return(1);
	}
4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336 4337

	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 */
4338
	uchar*	buf,		/* in/out: buffer for next row in MySQL
4339
				format */
4340
	uint	direction,	/* in: ROW_SEL_NEXT or ROW_SEL_PREV */
4341 4342 4343 4344 4345
	uint	match_mode)	/* in: 0, ROW_SEL_EXACT, or
				ROW_SEL_EXACT_PREFIX */
{
	ulint		ret;
	int		error	= 0;
4346

4347
	DBUG_ENTER("general_fetch");
4348

4349
	ut_a(prebuilt->trx == thd_to_trx(user_thd));
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4350

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4351
	innodb_srv_conc_enter_innodb(prebuilt->trx);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4352

monty@donna.mysql.fi's avatar
Merge  
monty@donna.mysql.fi committed
4353 4354
	ret = row_search_for_mysql((byte*)buf, 0, prebuilt, match_mode,
								direction);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4355
	innodb_srv_conc_exit_innodb(prebuilt->trx);
4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368

	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 {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4369
		error = convert_error_code_to_mysql((int) ret, user_thd);
4370 4371
		table->status = STATUS_NOT_FOUND;
	}
4372

4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 4384
	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 */
4385
	uchar*		buf)	/* in/out: buffer for next row in MySQL
4386 4387
				format */
{
4388
	ha_statistic_increment(&SSV::ha_read_next_count);
4389

4390 4391 4392 4393 4394 4395 4396 4397 4398 4399 4400
	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 */
4401 4402
	uchar*		buf,	/* in/out: buffer for the row */
	const uchar*	key,	/* in: key value */
4403
	uint		keylen)	/* in: key value length */
4404
{
4405
	ha_statistic_increment(&SSV::ha_read_next_count);
4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 4416

	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(
/*====================*/
4417 4418
			/* out: 0, HA_ERR_END_OF_FILE, or error number */
	uchar*	buf)	/* in/out: buffer for previous row in MySQL format */
4419
{
4420
	ha_statistic_increment(&SSV::ha_read_prev_count);
4421

4422 4423 4424 4425 4426 4427 4428 4429 4430 4431
	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(
/*=====================*/
4432 4433
			/* out: 0, HA_ERR_END_OF_FILE, or error code */
	uchar*	buf)	/* in/out: buffer for the row */
4434 4435 4436
{
	int	error;

4437
	DBUG_ENTER("index_first");
4438
	ha_statistic_increment(&SSV::ha_read_first_count);
4439

4440
	error = index_read(buf, NULL, 0, HA_READ_AFTER_KEY);
4441

4442
	/* MySQL does not seem to allow this to return HA_ERR_KEY_NOT_FOUND */
4443

4444 4445 4446
	if (error == HA_ERR_KEY_NOT_FOUND) {
		error = HA_ERR_END_OF_FILE;
	}
4447

4448
	DBUG_RETURN(error);
4449 4450 4451 4452 4453 4454 4455 4456 4457
}

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

int
ha_innobase::index_last(
/*====================*/
4458 4459
			/* out: 0, HA_ERR_END_OF_FILE, or error code */
	uchar*	buf)	/* in/out: buffer for the row */
4460 4461 4462
{
	int	error;

4463
	DBUG_ENTER("index_last");
4464
	ha_statistic_increment(&SSV::ha_read_last_count);
4465

4466
	error = index_read(buf, NULL, 0, HA_READ_BEFORE_KEY);
4467

4468
	/* MySQL does not seem to allow this to return HA_ERR_KEY_NOT_FOUND */
4469

4470 4471 4472
	if (error == HA_ERR_KEY_NOT_FOUND) {
		error = HA_ERR_END_OF_FILE;
	}
4473

4474
	DBUG_RETURN(error);
4475 4476 4477 4478 4479 4480 4481 4482 4483
}

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

int
ha_innobase::rnd_init(
/*==================*/
			/* out: 0 or error number */
4484
	bool	scan)	/* in: ???????? */
4485
{
monty@donna.mysql.fi's avatar
Merge  
monty@donna.mysql.fi committed
4486
	int	err;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4487

4488 4489 4490
	/* Store the active index value so that we can restore the original
	value after a scan */

4491
	if (prebuilt->clust_index_was_generated) {
monty@donna.mysql.fi's avatar
Merge  
monty@donna.mysql.fi committed
4492
		err = change_active_index(MAX_KEY);
4493
	} else {
monty@donna.mysql.fi's avatar
Merge  
monty@donna.mysql.fi committed
4494
		err = change_active_index(primary_key);
4495
	}
4496

4497 4498 4499 4500 4501 4502 4503
	/* 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);
	}

4504
	start_of_scan = 1;
4505

4506
	return(err);
4507 4508 4509
}

/*********************************************************************
4510
Ends a table scan. */
4511 4512 4513 4514 4515 4516

int
ha_innobase::rnd_end(void)
/*======================*/
				/* out: 0 or error number */
{
4517
	return(index_end());
4518 4519 4520 4521 4522 4523 4524 4525 4526 4527
}

/*********************************************************************
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 */
4528
	uchar*	buf)	/* in/out: returns the row in this buffer,
4529 4530
			in MySQL format */
{
4531
	int	error;
4532

4533
	DBUG_ENTER("rnd_next");
4534
	ha_statistic_increment(&SSV::ha_read_rnd_next_count);
4535

4536
	if (start_of_scan) {
4537 4538 4539 4540
		error = index_first(buf);
		if (error == HA_ERR_KEY_NOT_FOUND) {
			error = HA_ERR_END_OF_FILE;
		}
4541
		start_of_scan = 0;
4542
	} else {
4543
		error = general_fetch(buf, ROW_SEL_NEXT, 0);
4544
	}
4545

4546
	DBUG_RETURN(error);
4547 4548 4549
}

/**************************************************************************
4550
Fetches a row from the table based on a row reference. */
4551

4552 4553 4554
int
ha_innobase::rnd_pos(
/*=================*/
4555 4556 4557 4558 4559 4560
			/* out: 0, HA_ERR_KEY_NOT_FOUND, or error code */
	uchar*	buf,	/* in/out: buffer for the row */
	uchar*	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 */
4561
{
4562 4563
	int		error;
	uint		keynr	= active_index;
4564
	DBUG_ENTER("rnd_pos");
4565
	DBUG_DUMP("key", pos, ref_length);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4566

4567
	ha_statistic_increment(&SSV::ha_read_rnd_count);
4568

4569
	ut_a(prebuilt->trx == thd_to_trx(ha_thd()));
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4570

4571 4572 4573 4574
	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
4575
		that MySQL knows of */
4576

monty@donna.mysql.fi's avatar
Merge  
monty@donna.mysql.fi committed
4577
		error = change_active_index(MAX_KEY);
4578
	} else {
monty@donna.mysql.fi's avatar
Merge  
monty@donna.mysql.fi committed
4579
		error = change_active_index(primary_key);
4580
	}
4581

monty@donna.mysql.fi's avatar
Merge  
monty@donna.mysql.fi committed
4582
	if (error) {
4583
		DBUG_PRINT("error", ("Got error: %d", error));
monty@donna.mysql.fi's avatar
Merge  
monty@donna.mysql.fi committed
4584 4585
		DBUG_RETURN(error);
	}
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4586

4587
	/* Note that we assume the length of the row reference is fixed
4588
	for the table, and it is == ref_length */
4589 4590

	error = index_read(buf, pos, ref_length, HA_READ_KEY_EXACT);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4591 4592

	if (error) {
4593
		DBUG_PRINT("error", ("Got error: %d", error));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4594
	}
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4595

4596
	change_active_index(keynr);
4597

4598
	DBUG_RETURN(error);
4599 4600 4601
}

/*************************************************************************
4602
Stores a reference to the current row to 'ref' field of the handle. Note
4603 4604
that in the case where we have generated the clustered index for the
table, the function parameter is illogical: we MUST ASSUME that 'record'
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4605
is the current 'position' of the handle, because if row ref is actually
4606
the row id internally generated in InnoDB, then 'record' does not contain
4607 4608
it. We just guess that the row id must be for the record where the handle
was positioned the last time. */
4609 4610 4611 4612

void
ha_innobase::position(
/*==================*/
4613
	const uchar*	record)	/* in: row in MySQL format */
4614
{
4615
	uint		len;
4616

4617
	ut_a(prebuilt->trx == thd_to_trx(ha_thd()));
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4618

4619 4620 4621 4622
	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
4623
		that MySQL knows of */
4624 4625 4626 4627 4628

		len = DATA_ROW_ID_LEN;

		memcpy(ref, prebuilt->row_id, len);
	} else {
4629 4630
		len = store_key_val_for_row(primary_key, (char*)ref,
							 ref_length, record);
4631
	}
4632

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4633 4634
	/* We assume that the 'ref' value len is always fixed for the same
	table. */
4635

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4636
	if (len != ref_length) {
4637
	  sql_print_error("Stored ref len is %lu, but table ref len is %lu",
4638
			  (ulong) len, (ulong) ref_length);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4639
	}
4640 4641
}

4642 4643 4644
/*********************************************************************
If it's a DB_TOO_BIG_RECORD error then set a suitable message to
return to the client.*/
4645
inline
4646 4647 4648
void
innodb_check_for_record_too_big_error(
/*==================================*/
4649 4650
	ulint	comp,	/* in: ROW_FORMAT: nonzero=COMPACT, 0=REDUNDANT */
	int	error)	/* in: error code to check */
4651 4652
{
	if (error == (int)DB_TOO_BIG_RECORD) {
4653 4654
		ulint	max_row_size
			= page_get_free_space_of_empty_noninline(comp) / 2;
4655 4656 4657 4658 4659

		my_error(ER_TOO_BIG_ROWSIZE, MYF(0), max_row_size);
	}
}

4660 4661 4662 4663 4664 4665
/* limit innodb monitor access to users with PROCESS privilege.
See http://bugs.mysql.com/32710 for expl. why we choose PROCESS. */
#define IS_MAGIC_TABLE_AND_USER_DENIED_ACCESS(table_name, thd) \
	(row_is_magic_monitor_table(table_name) \
	 && check_global_access(thd, PROCESS_ACL))

4666
/*********************************************************************
4667
Creates a table definition to an InnoDB database. */
4668 4669 4670 4671
static
int
create_table_def(
/*=============*/
4672
	trx_t*		trx,		/* in: InnoDB transaction handle */
4673 4674
	TABLE*		form,		/* in: information on table
					columns and indexes */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4675
	const char*	table_name,	/* in: table name */
marko@hundin.mysql.fi's avatar
marko@hundin.mysql.fi committed
4676
	const char*	path_of_temp_table,/* in: if this is a table explicitly
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4677 4678 4679 4680 4681 4682 4683
					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 */
4684
	ulint		flags)		/* in: table flags */
4685 4686 4687 4688
{
	Field*		field;
	dict_table_t*	table;
	ulint		n_cols;
4689 4690
	int		error;
	ulint		col_type;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4691
	ulint		col_len;
4692
	ulint		nulls_allowed;
4693
	ulint		unsigned_type;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4694
	ulint		binary_type;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4695
	ulint		long_true_varchar;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4696
	ulint		charset_no;
4697
	ulint		i;
4698

4699 4700
	DBUG_ENTER("create_table_def");
	DBUG_PRINT("enter", ("table_name: %s", table_name));
4701

4702 4703 4704 4705 4706 4707
	ut_a(trx->mysql_thd != NULL);
	if (IS_MAGIC_TABLE_AND_USER_DENIED_ACCESS(table_name,
						  (THD*) trx->mysql_thd)) {
		DBUG_RETURN(HA_ERR_GENERIC);
	}

4708
	n_cols = form->s->fields;
4709

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4710 4711
	/* We pass 0 as the space id, and determine at a lower level the space
	id where to store the table */
4712

4713
	table = dict_mem_table_create(table_name, 0, n_cols, flags);
4714

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4715 4716 4717 4718 4719
	if (path_of_temp_table) {
		table->dir_path_of_temp_table =
			mem_heap_strdup(table->heap, path_of_temp_table);
	}

4720 4721 4722
	for (i = 0; i < n_cols; i++) {
		field = form->field[i];

4723 4724
		col_type = get_innobase_type_from_mysql_type(&unsigned_type,
									field);
4725 4726 4727 4728 4729 4730
		if (field->null_ptr) {
			nulls_allowed = 0;
		} else {
			nulls_allowed = DATA_NOT_NULL;
		}

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4731
		if (field->binary()) {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4732 4733 4734 4735 4736
			binary_type = DATA_BINARY_TYPE;
		} else {
			binary_type = 0;
		}

4737
		charset_no = 0;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4738 4739 4740 4741 4742

		if (dtype_is_string_type(col_type)) {

			charset_no = (ulint)field->charset()->number;

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4743 4744 4745 4746 4747 4748 4749 4750 4751 4752 4753 4754 4755
			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. */
4756

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4757 4758 4759 4760 4761 4762 4763 4764
		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;
			}
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4765 4766
		}

4767
		dict_mem_table_add_col(table, table->heap,
4768 4769 4770 4771 4772 4773 4774
			(char*) field->field_name,
			col_type,
			dtype_form_prtype(
				(ulint)field->type()
				| nulls_allowed | unsigned_type
				| binary_type | long_true_varchar,
				charset_no),
4775
			col_len);
4776 4777 4778 4779
	}

	error = row_create_table_for_mysql(table, trx);

4780
	innodb_check_for_record_too_big_error(flags & DICT_TF_COMPACT, error);
4781

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4782
	error = convert_error_code_to_mysql(error, NULL);
4783 4784 4785 4786 4787

	DBUG_RETURN(error);
}

/*********************************************************************
4788
Creates an index in an InnoDB database. */
4789 4790
static
int
4791 4792
create_index(
/*=========*/
4793
	trx_t*		trx,		/* in: InnoDB transaction handle */
4794 4795 4796 4797 4798
	TABLE*		form,		/* in: information on table
					columns and indexes */
	const char*	table_name,	/* in: table name */
	uint		key_num)	/* in: index number */
{
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4799
	Field*		field;
4800
	dict_index_t*	index;
4801
	int		error;
4802 4803 4804 4805
	ulint		n_fields;
	KEY*		key;
	KEY_PART_INFO*	key_part;
	ulint		ind_type;
4806 4807
	ulint		col_type;
	ulint		prefix_len;
4808
	ulint		is_unsigned;
4809 4810
	ulint		i;
	ulint		j;
4811
	ulint*		field_lengths;
4812 4813

	DBUG_ENTER("create_index");
4814

4815 4816
	key = form->key_info + key_num;

4817
	n_fields = key->key_parts;
4818

4819
	ind_type = 0;
4820

4821
	if (key_num == form->s->primary_key) {
4822 4823
		ind_type = ind_type | DICT_CLUSTERED;
	}
4824

4825 4826 4827 4828
	if (key->flags & HA_NOSAME ) {
		ind_type = ind_type | DICT_UNIQUE;
	}

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4829 4830
	/* We pass 0 as the space id, and determine at a lower level the space
	id where to store the table */
4831 4832 4833

	index = dict_mem_index_create((char*) table_name, key->name, 0,
						ind_type, n_fields);
4834 4835 4836

	field_lengths = (ulint*) my_malloc(sizeof(ulint) * n_fields,
		MYF(MY_FAE));
4837

4838 4839 4840
	for (i = 0; i < n_fields; i++) {
		key_part = key->key_part + i;

4841
		/* (The flag HA_PART_KEY_SEG denotes in MySQL a column prefix
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4842 4843 4844 4845
		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. */
4846

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4847
		field = NULL;
4848
		for (j = 0; j < form->s->fields; j++) {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4849 4850 4851

			field = form->field[j];

4852 4853 4854
			if (0 == innobase_strcasecmp(
					field->field_name,
					key_part->field->field_name)) {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4855 4856 4857 4858 4859 4860
				/* Found the corresponding column */

				break;
			}
		}

4861
		ut_a(j < form->s->fields);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4862

4863 4864
		col_type = get_innobase_type_from_mysql_type(
					&is_unsigned, key_part->field);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4865 4866

		if (DATA_BLOB == col_type
4867 4868 4869 4870 4871
			|| (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)) {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4872

4873
			prefix_len = key_part->length;
4874 4875

			if (col_type == DATA_INT
4876 4877 4878 4879 4880 4881 4882 4883 4884 4885 4886 4887
				|| 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;
4888 4889
			}
		} else {
4890
			prefix_len = 0;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4891 4892
		}

4893 4894
		field_lengths[i] = key_part->length;

4895
		dict_mem_index_add_field(index,
4896
			(char*) key_part->field->field_name, prefix_len);
4897 4898
	}

4899 4900 4901
	/* 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. */
4902
	error = row_create_index_for_mysql(index, trx, field_lengths);
4903

4904 4905
	innodb_check_for_record_too_big_error(form->s->row_type
					      != ROW_TYPE_REDUNDANT, error);
4906

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4907
	error = convert_error_code_to_mysql(error, NULL);
4908

4909
	my_free(field_lengths, MYF(0));
4910

4911 4912 4913 4914
	DBUG_RETURN(error);
}

/*********************************************************************
4915
Creates an index to an InnoDB table when the user has defined no
4916
primary index. */
4917 4918
static
int
4919 4920
create_clustered_index_when_no_primary(
/*===================================*/
4921
	trx_t*		trx,		/* in: InnoDB transaction handle */
4922 4923
	ulint		comp,		/* in: ROW_FORMAT:
					nonzero=COMPACT, 0=REDUNDANT */
4924 4925 4926
	const char*	table_name)	/* in: table name */
{
	dict_index_t*	index;
4927
	int		error;
4928

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4929 4930
	/* We pass 0 as the space id, and determine at a lower level the space
	id where to store the table */
4931

4932 4933
	index = dict_mem_index_create(table_name, "GEN_CLUST_INDEX",
				      0, DICT_CLUSTERED, 0);
4934
	error = row_create_index_for_mysql(index, trx, NULL);
4935

4936
	innodb_check_for_record_too_big_error(comp, error);
4937

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4938
	error = convert_error_code_to_mysql(error, NULL);
4939

4940
	return(error);
4941 4942 4943
}

/*********************************************************************
4944 4945 4946 4947 4948 4949 4950 4951 4952 4953 4954 4955 4956 4957
Update create_info.  Used in SHOW CREATE TABLE et al. */

void
ha_innobase::update_create_info(
/*============================*/
	HA_CREATE_INFO* create_info)	/* in/out: create info */
{
  if (!(create_info->used_fields & HA_CREATE_USED_AUTO)) {
    ha_innobase::info(HA_STATUS_AUTO);
    create_info->auto_increment_value = stats.auto_increment_value;
  }
}

/*********************************************************************
4958
Creates a new table to an InnoDB database. */
4959 4960 4961 4962 4963 4964 4965 4966

int
ha_innobase::create(
/*================*/
					/* out: error number */
	const char*	name,		/* in: table name */
	TABLE*		form,		/* in: information on table
					columns and indexes */
4967 4968 4969
	HA_CREATE_INFO*	create_info)	/* in: more information of the
					created table, contains also the
					create statement string */
4970 4971 4972
{
	int		error;
	dict_table_t*	innobase_table;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4973
	trx_t*		parent_trx;
4974
	trx_t*		trx;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4975
	int		primary_key_no;
4976
	uint		i;
4977 4978
	char		name2[FN_REFLEN];
	char		norm_name[FN_REFLEN];
4979
	THD*		thd = ha_thd();
4980 4981
	ib_longlong	auto_inc_value;
	ulint		flags;
4982

4983
	DBUG_ENTER("ha_innobase::create");
4984

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4985
	DBUG_ASSERT(thd != NULL);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4986

4987
	if (form->s->fields > 1000) {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4988 4989 4990
		/* The limit probably should be REC_MAX_N_FIELDS - 3 = 1020,
		but we play safe here */

4991 4992
		DBUG_RETURN(HA_ERR_TO_BIG_ROW);
	}
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4993

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4994 4995
	/* Get the transaction associated with the current thd, or create one
	if not yet created */
4996

4997
	parent_trx = check_trx_exists(thd);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4998 4999 5000 5001

	/* In case MySQL calls this in the middle of a SELECT query, release
	possible adaptive hash latch to avoid deadlocks of threads */

5002 5003
	trx_search_latch_release_if_reserved(parent_trx);

5004
	trx = trx_allocate_for_mysql();
5005

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5006
	trx->mysql_thd = thd;
5007
	trx->mysql_query_str = thd_query(thd);
5008

antony@ppcg5.local's avatar
antony@ppcg5.local committed
5009
	if (thd_test_options(thd, OPTION_NO_FOREIGN_KEY_CHECKS)) {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5010 5011 5012
		trx->check_foreigns = FALSE;
	}

antony@ppcg5.local's avatar
antony@ppcg5.local committed
5013
	if (thd_test_options(thd, OPTION_RELAXED_UNIQUE_CHECKS)) {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5014 5015 5016
		trx->check_unique_secondary = FALSE;
	}

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5017 5018 5019 5020 5021
	if (lower_case_table_names) {
		srv_lower_case_table_names = TRUE;
	} else {
		srv_lower_case_table_names = FALSE;
	}
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
5022

5023
	strcpy(name2, name);
5024 5025

	normalize_table_name(norm_name, name2);
5026

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5027
	/* Latch the InnoDB data dictionary exclusively so that no deadlocks
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5028
	or lock waits can happen in it during a table create operation.
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5029
	Drop table etc. do this latching in row0mysql.c. */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5030

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5031
	row_mysql_lock_data_dictionary(trx);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5032 5033

	/* Create the table definition in InnoDB */
5034

5035 5036 5037 5038 5039 5040
	flags = 0;

	if (form->s->row_type != ROW_TYPE_REDUNDANT) {
		flags |= DICT_TF_COMPACT;
	}

marko@hundin.mysql.fi's avatar
marko@hundin.mysql.fi committed
5041 5042
	error = create_table_def(trx, form, norm_name,
		create_info->options & HA_LEX_CREATE_TMP_TABLE ? name2 : NULL,
5043
		flags);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5044

5045
	if (error) {
5046
		goto cleanup;
5047
	}
5048

5049 5050
	/* Look for a primary key */

5051
	primary_key_no= (form->s->primary_key != MAX_KEY ?
5052
			 (int) form->s->primary_key :
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
5053
			 -1);
5054

5055 5056 5057
	/* Our function row_get_mysql_key_number_for_index assumes
	the primary key is always number 0, if it exists */

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
5058
	DBUG_ASSERT(primary_key_no == -1 || primary_key_no == 0);
5059

5060 5061
	/* Create the keys */

5062
	if (form->s->keys == 0 || primary_key_no == -1) {
5063 5064
		/* Create an index which is used as the clustered index;
		order the rows by their row id which is internally generated
5065
		by InnoDB */
5066

5067 5068 5069
		error = create_clustered_index_when_no_primary(
			trx, form->s->row_type != ROW_TYPE_REDUNDANT,
			norm_name);
5070
		if (error) {
5071
			goto cleanup;
5072
		}
5073 5074 5075
	}

	if (primary_key_no != -1) {
5076
		/* In InnoDB the clustered index must always be created
5077
		first */
5078
		if ((error = create_index(trx, form, norm_name,
monty@donna.mysql.fi's avatar
monty@donna.mysql.fi committed
5079
					  (uint) primary_key_no))) {
5080
			goto cleanup;
5081 5082
		}
	}
5083

5084
	for (i = 0; i < form->s->keys; i++) {
5085 5086 5087

		if (i != (uint) primary_key_no) {

5088
			if ((error = create_index(trx, form, norm_name, i))) {
5089
				goto cleanup;
5090 5091 5092
			}
		}
	}
5093

5094
	if (*trx->mysql_query_str) {
5095
		error = row_table_add_foreign_constraints(trx,
5096
			*trx->mysql_query_str, norm_name,
5097
			create_info->options & HA_LEX_CREATE_TMP_TABLE);
5098

5099
		error = convert_error_code_to_mysql(error, NULL);
5100

5101 5102
		if (error) {
			goto cleanup;
5103
		}
5104 5105
	}

5106
	innobase_commit_low(trx);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5107

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5108
	row_mysql_unlock_data_dictionary(trx);
5109

monty@donna.mysql.fi's avatar
Merge  
monty@donna.mysql.fi committed
5110 5111 5112
	/* 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 */
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
5113

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5114
	log_buffer_flush_to_disk();
monty@donna.mysql.fi's avatar
Merge  
monty@donna.mysql.fi committed
5115

5116
	innobase_table = dict_table_get(norm_name, FALSE);
5117

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
5118
	DBUG_ASSERT(innobase_table != 0);
5119

5120 5121 5122 5123 5124 5125 5126 5127 5128
	/* Note: We can't call update_thd() as prebuilt will not be
	setup at this stage and so we use thd. */

	/* We need to copy the AUTOINC value from the old table if
	this is an ALTER TABLE. */

	if (((create_info->used_fields & HA_CREATE_USED_AUTO)
	    || thd_sql_command(thd) == SQLCOM_ALTER_TABLE)
	    && create_info->auto_increment_value != 0) {
5129

5130
		/* Query was ALTER TABLE...AUTO_INCREMENT = x; or
5131
		CREATE TABLE ...AUTO_INCREMENT = x; Find out a table
5132 5133
		definition from the dictionary and get the current value
		of the auto increment field. Set a new value to the
5134 5135
		auto increment field if the value is greater than the
		maximum value in the column. */
5136

5137
		auto_inc_value = create_info->auto_increment_value;
5138 5139

		dict_table_autoinc_lock(innobase_table);
5140
		dict_table_autoinc_initialize(innobase_table, auto_inc_value);
5141
		dict_table_autoinc_unlock(innobase_table);
5142 5143
	}

5144
	/* Tell the InnoDB server that there might be work for
5145 5146 5147 5148
	utility threads: */

	srv_active_wake_master_thread();

5149
	trx_free_for_mysql(trx);
5150 5151

	DBUG_RETURN(0);
5152 5153 5154

cleanup:
	innobase_commit_low(trx);
5155

5156
	row_mysql_unlock_data_dictionary(trx);
5157

5158 5159 5160
	trx_free_for_mysql(trx);

	DBUG_RETURN(error);
5161 5162
}

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5163 5164 5165 5166 5167 5168 5169 5170 5171
/*********************************************************************
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 */
{
5172
	dict_table_t*	dict_table;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5173 5174 5175
	trx_t*		trx;
	int		err;

5176
	DBUG_ENTER("ha_innobase::discard_or_import_tablespace");
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5177

5178 5179
	ut_a(prebuilt->trx);
	ut_a(prebuilt->trx->magic_n == TRX_MAGIC_N);
5180
	ut_a(prebuilt->trx == thd_to_trx(ha_thd()));
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5181

5182
	dict_table = prebuilt->table;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5183 5184 5185
	trx = prebuilt->trx;

	if (discard) {
5186
		err = row_discard_tablespace_for_mysql(dict_table->name, trx);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5187
	} else {
5188
		err = row_import_tablespace_for_mysql(dict_table->name, trx);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5189 5190
	}

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5191
	err = convert_error_code_to_mysql(err, NULL);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5192

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5193
	DBUG_RETURN(err);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5194 5195
}

5196 5197 5198 5199 5200 5201 5202 5203 5204 5205 5206 5207 5208
/*********************************************************************
Deletes all rows of an InnoDB table. */

int
ha_innobase::delete_all_rows(void)
/*==============================*/
				/* out: error number */
{
	int		error;

	DBUG_ENTER("ha_innobase::delete_all_rows");

	/* Get the transaction associated with the current thd, or create one
5209
	if not yet created, and update prebuilt->trx */
5210

5211
	update_thd(ha_thd());
5212

5213 5214 5215 5216 5217 5218
	if (thd_sql_command(user_thd) != 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);
	}
5219

5220
	/* Truncate the table in InnoDB */
5221

5222 5223 5224 5225
	error = row_truncate_table_for_mysql(prebuilt->table, prebuilt->trx);
	if (error == DB_ERROR) {
		/* Cannot truncate; resort to ha_innobase::delete_row() */
		goto fallback;
antony@ppcg5.local's avatar
antony@ppcg5.local committed
5226 5227
	}

5228 5229 5230
	error = convert_error_code_to_mysql(error, NULL);

	DBUG_RETURN(error);
5231 5232
}

5233
/*********************************************************************
5234
Drops a table from an InnoDB database. Before calling this function,
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
5235 5236
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
5237 5238
operation inside InnoDB will remove all locks any user has on the table
inside InnoDB. */
5239 5240 5241 5242

int
ha_innobase::delete_table(
/*======================*/
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
5243 5244
				/* out: error number */
	const char*	name)	/* in: table name */
5245 5246 5247
{
	ulint	name_len;
	int	error;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5248
	trx_t*	parent_trx;
5249
	trx_t*	trx;
5250
	THD	*thd = ha_thd();
5251
	char	norm_name[1000];
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5252

5253
	DBUG_ENTER("ha_innobase::delete_table");
5254

5255 5256 5257 5258 5259 5260 5261 5262
	/* Strangely, MySQL passes the table name without the '.frm'
	extension, in contrast to ::create */
	normalize_table_name(norm_name, name);

	if (IS_MAGIC_TABLE_AND_USER_DENIED_ACCESS(norm_name, thd)) {
		DBUG_RETURN(HA_ERR_GENERIC);
	}

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5263 5264
	/* Get the transaction associated with the current thd, or create one
	if not yet created */
5265

5266
	parent_trx = check_trx_exists(thd);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5267 5268 5269 5270

	/* In case MySQL calls this in the middle of a SELECT query, release
	possible adaptive hash latch to avoid deadlocks of threads */

5271
	trx_search_latch_release_if_reserved(parent_trx);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5272

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5273 5274 5275 5276 5277 5278
	if (lower_case_table_names) {
		srv_lower_case_table_names = TRUE;
	} else {
		srv_lower_case_table_names = FALSE;
	}

5279 5280
	trx = trx_allocate_for_mysql();

5281 5282
	trx->mysql_thd = thd;
	trx->mysql_query_str = thd_query(thd);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5283

5284 5285 5286
	if (thd_test_options(thd, OPTION_NO_FOREIGN_KEY_CHECKS)) {
		trx->check_foreigns = FALSE;
	}
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5287

5288 5289 5290
	if (thd_test_options(thd, OPTION_RELAXED_UNIQUE_CHECKS)) {
		trx->check_unique_secondary = FALSE;
	}
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5291

5292
	name_len = strlen(name);
5293

5294
	assert(name_len < 1000);
5295

5296
	/* Drop the table in InnoDB */
5297

5298 5299 5300
	error = row_drop_table_for_mysql(norm_name, trx,
					 thd_sql_command(thd)
					 == SQLCOM_DROP_DB);
5301

5302 5303 5304
	/* 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 */
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
5305

5306
	log_buffer_flush_to_disk();
monty@donna.mysql.fi's avatar
Merge  
monty@donna.mysql.fi committed
5307

5308 5309
	/* Tell the InnoDB server that there might be work for
	utility threads: */
5310

5311
	srv_active_wake_master_thread();
5312

5313
	innobase_commit_low(trx);
heikki@donna.mysql.fi's avatar
heikki@donna.mysql.fi committed
5314

5315
	trx_free_for_mysql(trx);
5316

5317
	error = convert_error_code_to_mysql(error, NULL);
5318

5319
	DBUG_RETURN(error);
5320 5321
}

5322
/*********************************************************************
5323
Removes all tables in the named database inside InnoDB. */
5324
static
5325
void
5326
innobase_drop_database(
5327 5328 5329 5330 5331 5332 5333 5334 5335 5336 5337 5338 5339 5340 5341 5342 5343 5344 5345 5346
/*===================*/
			/* out: error number */
        handlerton *hton, /* in: handlerton of Innodb */
	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;
	trx_t*	parent_trx;
	trx_t*	trx;
	char*	ptr;
	int	error;
	char*	namebuf;
	THD*	thd		= current_thd;

	/* Get the transaction associated with the current thd, or create one
	if not yet created */

	parent_trx = check_trx_exists(thd);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5347 5348 5349 5350

	/* In case MySQL calls this in the middle of a SELECT query, release
	possible adaptive hash latch to avoid deadlocks of threads */

5351
	trx_search_latch_release_if_reserved(parent_trx);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5352

5353
	ptr = strend(path) - 2;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
5354

5355 5356 5357 5358 5359 5360
	while (ptr >= path && *ptr != '\\' && *ptr != '/') {
		ptr--;
		len++;
	}

	ptr++;
5361
	namebuf = (char*) my_malloc((uint) len + 2, MYF(0));
5362 5363 5364 5365

	memcpy(namebuf, ptr, len);
	namebuf[len] = '/';
	namebuf[len + 1] = '\0';
5366
#ifdef	__WIN__
5367
	innobase_casedn_str(namebuf);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5368
#endif
5369
	trx = trx_allocate_for_mysql();
5370 5371
	trx->mysql_thd = thd;
	trx->mysql_query_str = thd_query(thd);
5372

5373
	if (thd_test_options(thd, OPTION_NO_FOREIGN_KEY_CHECKS)) {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5374 5375 5376
		trx->check_foreigns = FALSE;
	}

5377
	error = row_drop_database_for_mysql(namebuf, trx);
5378
	my_free(namebuf, MYF(0));
5379

monty@donna.mysql.fi's avatar
Merge  
monty@donna.mysql.fi committed
5380 5381 5382
	/* 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 */
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
5383

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5384
	log_buffer_flush_to_disk();
monty@donna.mysql.fi's avatar
Merge  
monty@donna.mysql.fi committed
5385

5386 5387 5388 5389 5390
	/* Tell the InnoDB server that there might be work for
	utility threads: */

	srv_active_wake_master_thread();

5391 5392 5393
	innobase_commit_low(trx);
	trx_free_for_mysql(trx);
#ifdef NO_LONGER_INTERESTED_IN_DROP_DB_ERROR
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5394
	error = convert_error_code_to_mysql(error, NULL);
5395 5396

	return(error);
5397 5398 5399
#else
	return;
#endif
5400 5401
}

5402
/*************************************************************************
5403
Renames an InnoDB table. */
5404 5405 5406 5407 5408 5409 5410 5411 5412 5413 5414

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;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5415
	trx_t*	parent_trx;
5416
	trx_t*	trx;
5417 5418
	char	norm_from[1000];
	char	norm_to[1000];
5419
	THD*	thd		= ha_thd();
5420

5421
	DBUG_ENTER("ha_innobase::rename_table");
5422

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5423 5424
	/* Get the transaction associated with the current thd, or create one
	if not yet created */
5425

5426
	parent_trx = check_trx_exists(thd);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5427 5428 5429 5430

	/* In case MySQL calls this in the middle of a SELECT query, release
	possible adaptive hash latch to avoid deadlocks of threads */

5431
	trx_search_latch_release_if_reserved(parent_trx);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5432

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5433 5434 5435 5436 5437 5438
	if (lower_case_table_names) {
		srv_lower_case_table_names = TRUE;
	} else {
		srv_lower_case_table_names = FALSE;
	}

5439
	trx = trx_allocate_for_mysql();
5440 5441
	trx->mysql_thd = thd;
	trx->mysql_query_str = thd_query(thd);
5442

5443
	if (thd_test_options(thd, OPTION_NO_FOREIGN_KEY_CHECKS)) {
5444 5445 5446
		trx->check_foreigns = FALSE;
	}

5447 5448 5449 5450 5451
	name_len1 = strlen(from);
	name_len2 = strlen(to);

	assert(name_len1 < 1000);
	assert(name_len2 < 1000);
5452

5453 5454 5455
	normalize_table_name(norm_from, from);
	normalize_table_name(norm_to, to);

5456
	/* Rename the table in InnoDB */
5457

5458
	error = row_rename_table_for_mysql(norm_from, norm_to, trx);
5459

monty@donna.mysql.fi's avatar
Merge  
monty@donna.mysql.fi committed
5460 5461 5462
	/* 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 */
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
5463

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5464
	log_buffer_flush_to_disk();
monty@donna.mysql.fi's avatar
Merge  
monty@donna.mysql.fi committed
5465

5466
	/* Tell the InnoDB server that there might be work for
5467 5468 5469 5470
	utility threads: */

	srv_active_wake_master_thread();

5471 5472
	innobase_commit_low(trx);
	trx_free_for_mysql(trx);
5473

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5474
	error = convert_error_code_to_mysql(error, NULL);
5475 5476 5477 5478 5479 5480 5481 5482 5483 5484

	DBUG_RETURN(error);
}

/*************************************************************************
Estimates the number of index records in a range. */

ha_rows
ha_innobase::records_in_range(
/*==========================*/
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5485 5486
						/* out: estimated number of
						rows */
5487 5488 5489
	uint			keynr,		/* in: index number */
	key_range		*min_key,	/* in: start key value of the
						   range, may also be 0 */
5490
	key_range		*max_key)	/* in: range end key val, may
5491
						   also be 0 */
5492 5493 5494
{
	KEY*		key;
	dict_index_t*	index;
5495
	uchar*		key_val_buff2	= (uchar*) my_malloc(
5496
						  table->s->reclength
5497
					+ table->s->max_key_length + 100,
osku@127.(none)'s avatar
osku@127.(none) committed
5498
								MYF(MY_FAE));
5499
	ulint		buff2_len = table->s->reclength
5500
					+ table->s->max_key_length + 100;
5501
	dtuple_t*	range_start;
5502
	dtuple_t*	range_end;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5503
	ib_longlong	n_rows;
5504 5505
	ulint		mode1;
	ulint		mode2;
5506 5507
	void*		heap1;
	void*		heap2;
5508

5509
	DBUG_ENTER("records_in_range");
5510

5511
	ut_a(prebuilt->trx == thd_to_trx(ha_thd()));
5512

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5513 5514
	prebuilt->trx->op_info = (char*)"estimating records in index range";

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5515 5516 5517 5518
	/* 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);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
5519

5520 5521 5522
	active_index = keynr;

	key = table->key_info + active_index;
5523

5524
	index = dict_table_get_index_noninline(prebuilt->table, key->name);
5525

5526
	range_start = dtuple_create_for_mysql(&heap1, key->key_parts);
5527
	dict_index_copy_types(range_start, index, key->key_parts);
5528

5529
	range_end = dtuple_create_for_mysql(&heap2, key->key_parts);
5530
	dict_index_copy_types(range_end, index, key->key_parts);
5531

5532
	row_sel_convert_mysql_key_to_innobase(
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5533 5534 5535
				range_start, (byte*) key_val_buff,
				(ulint)upd_and_key_val_buff_len,
				index,
5536
				(byte*) (min_key ? min_key->key :
5537
					 (const uchar*) 0),
5538 5539
				(ulint) (min_key ? min_key->length : 0),
				prebuilt->trx);
5540

5541
	row_sel_convert_mysql_key_to_innobase(
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5542 5543
				range_end, (byte*) key_val_buff2,
				buff2_len, index,
5544
				(byte*) (max_key ? max_key->key :
5545
					 (const uchar*) 0),
5546 5547
				(ulint) (max_key ? max_key->length : 0),
				prebuilt->trx);
5548 5549

	mode1 = convert_search_mode_to_innobase(min_key ? min_key->flag :
5550
						HA_READ_KEY_EXACT);
5551
	mode2 = convert_search_mode_to_innobase(max_key ? max_key->flag :
5552
						HA_READ_KEY_EXACT);
5553

5554 5555 5556 5557 5558 5559 5560 5561 5562 5563
	if (mode1 != PAGE_CUR_UNSUPP && mode2 != PAGE_CUR_UNSUPP) {

		n_rows = btr_estimate_n_rows_in_range(index, range_start,
						      mode1, range_end,
						      mode2);
	} else {

		n_rows = 0;
	}

5564 5565
	dtuple_free_for_mysql(heap1);
	dtuple_free_for_mysql(heap2);
5566

5567
	my_free(key_val_buff2, MYF(0));
5568

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5569 5570
	prebuilt->trx->op_info = (char*)"";

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5571 5572 5573 5574 5575 5576 5577
	/* 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) {
5578
		n_rows = 1;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5579 5580
	}

5581 5582 5583
	DBUG_RETURN((ha_rows) n_rows);
}

5584 5585
/*************************************************************************
Gives an UPPER BOUND to the number of rows in a table. This is used in
5586
filesort.cc. */
5587 5588

ha_rows
sergefp@mysql.com's avatar
sergefp@mysql.com committed
5589
ha_innobase::estimate_rows_upper_bound(void)
5590
/*======================================*/
5591
			/* out: upper bound of rows */
5592
{
5593 5594
	dict_index_t*	index;
	ulonglong	estimate;
5595
	ulonglong	local_data_file_length;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
5596

5597
	DBUG_ENTER("estimate_rows_upper_bound");
5598

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5599 5600 5601 5602
	/* 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. */

5603
	update_thd(ha_thd());
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5604

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5605
	prebuilt->trx->op_info = (char*)
5606
				 "calculating upper bound for table rows";
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5607

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5608 5609 5610 5611
	/* 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);
5612

5613
	index = dict_table_get_first_index_noninline(prebuilt->table);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
5614

5615
	local_data_file_length = ((ulonglong) index->stat_n_leaf_pages)
5616
							* UNIV_PAGE_SIZE;
5617

heikki@donna.mysql.fi's avatar
heikki@donna.mysql.fi committed
5618 5619
	/* Calculate a minimum length for a clustered index record and from
	that an upper bound for the number of rows. Since we only calculate
5620 5621
	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. */
heikki@donna.mysql.fi's avatar
heikki@donna.mysql.fi committed
5622

5623 5624
	estimate = 2 * local_data_file_length /
					 dict_index_calc_min_rec_len(index);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
5625

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5626 5627
	prebuilt->trx->op_info = (char*)"";

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
5628
	DBUG_RETURN((ha_rows) estimate);
5629 5630
}

5631 5632 5633 5634 5635 5636 5637 5638 5639 5640
/*************************************************************************
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 */
{
heikki@donna.mysql.fi's avatar
heikki@donna.mysql.fi committed
5641 5642 5643 5644
	/* 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. */
5645

heikki@donna.mysql.fi's avatar
heikki@donna.mysql.fi committed
5646
	return((double) (prebuilt->table->stat_clustered_index_size));
5647 5648
}

5649 5650 5651
/**********************************************************************
Calculate the time it takes to read a set of ranges through an index
This enables us to optimise reads for clustered indexes. */
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
5652

5653 5654 5655 5656
double
ha_innobase::read_time(
/*===================*/
			/* out: estimated time measured in disk seeks */
5657
	uint	index,	/* in: key number */
5658 5659
	uint	ranges,	/* in: how many ranges */
	ha_rows rows)	/* in: estimated number of rows in the ranges */
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
5660
{
5661
	ha_rows total_rows;
5662 5663
	double	time_for_scan;

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5664
	if (index != table->s->primary_key) {
5665 5666
		/* Not clustered */
		return(handler::read_time(index, ranges, rows));
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5667
	}
5668

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5669
	if (rows <= 2) {
5670

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5671 5672
		return((double) rows);
	}
5673 5674 5675 5676

	/* Assume that the read time is proportional to the scan time for all
	rows + at most one seek per range. */

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5677
	time_for_scan = scan_time();
5678

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5679
	if ((total_rows = estimate_rows_upper_bound()) < rows) {
5680

5681
		return(time_for_scan);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5682
	}
5683

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5684
	return(ranges + (double) rows / (double) total_rows * time_for_scan);
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
5685 5686
}

5687 5688 5689 5690
/*************************************************************************
Returns statistics information of the table to the MySQL interpreter,
in various fields of the handle object. */

5691
int
5692 5693 5694 5695 5696 5697
ha_innobase::info(
/*==============*/
	uint flag)	/* in: what information MySQL requests */
{
	dict_table_t*	ib_table;
	dict_index_t*	index;
5698
	ha_rows		rec_per_key;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5699
	ib_longlong	n_rows;
5700 5701
	ulong		j;
	ulong		i;
5702
	char		path[FN_REFLEN];
5703
	os_file_stat_t	stat_info;
5704

5705
	DBUG_ENTER("info");
5706

5707
	/* If we are forcing recovery at a high level, we will suppress
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5708 5709 5710
	statistics calculation on tables, because that may crash the
	server if an index is badly corrupted. */

5711
	if (srv_force_recovery >= SRV_FORCE_NO_IBUF_MERGE) {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5712

5713 5714 5715 5716 5717 5718
		/* We return success (0) instead of HA_ERR_CRASHED,
		because we want MySQL to process this query and not
		stop, like it would do if it received the error code
		HA_ERR_CRASHED. */

		DBUG_RETURN(0);
5719
	}
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5720

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5721 5722 5723 5724
	/* 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. */

5725
	update_thd(ha_thd());
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5726 5727 5728 5729

	/* In case MySQL calls this in the middle of a SELECT query, release
	possible adaptive hash latch to avoid deadlocks of threads */

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5730 5731
	prebuilt->trx->op_info = (char*)"returning various info to MySQL";

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5732
	trx_search_latch_release_if_reserved(prebuilt->trx);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
5733

5734
	ib_table = prebuilt->table;
5735

5736
	if (flag & HA_STATUS_TIME) {
5737
		if (srv_stats_on_metadata) {
5738 5739
			/* In sql_show we call with this flag: update
			then statistics so that they are up-to-date */
5740

5741
			prebuilt->trx->op_info = "updating table statistics";
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5742

5743
			dict_update_statistics(ib_table);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5744

5745
			prebuilt->trx->op_info = "returning various info to MySQL";
5746
		}
5747

5748
		my_snprintf(path, sizeof(path), "%s/%s%s",
5749 5750
				mysql_data_home, ib_table->name, reg_ext);

5751
		unpack_filename(path,path);
5752

5753
		/* Note that we do not know the access time of the table,
5754 5755
		nor the CHECK TABLE time, nor the UPDATE or INSERT time. */

5756
		if (os_file_get_status(path,&stat_info)) {
5757
			stats.create_time = stat_info.ctime;
5758
		}
5759
	}
5760 5761

	if (flag & HA_STATUS_VARIABLE) {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5762 5763 5764 5765 5766 5767 5768 5769 5770 5771
		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
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5772
		HA_STATUS_TIME flag set, while the left join optimizer does not
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5773 5774 5775 5776 5777 5778 5779 5780 5781 5782 5783 5784
		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++;
		}

5785 5786 5787
		stats.records = (ha_rows)n_rows;
		stats.deleted = 0;
		stats.data_file_length = ((ulonglong)
5788
				ib_table->stat_clustered_index_size)
5789
					* UNIV_PAGE_SIZE;
5790
		stats.index_file_length = ((ulonglong)
5791
				ib_table->stat_sum_of_other_index_sizes)
5792
					* UNIV_PAGE_SIZE;
5793 5794
		stats.delete_length =
			fsp_get_available_space_in_free_extents(
5795
				ib_table->space);
5796
		stats.check_time = 0;
5797

5798 5799
		if (stats.records == 0) {
			stats.mean_rec_length = 0;
5800
		} else {
5801
			stats.mean_rec_length = (ulong) (stats.data_file_length / stats.records);
5802 5803
		}
	}
5804 5805 5806 5807 5808 5809 5810

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

5812
		for (i = 0; i < table->s->keys; i++) {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5813
			if (index == NULL) {
5814
				sql_print_error("Table %s contains fewer "
5815 5816 5817 5818
						"indexes inside InnoDB than "
						"are defined in the MySQL "
						".frm file. Have you mixed up "
						".frm files from different "
5819
						"installations? See "
5820
"http://dev.mysql.com/doc/refman/5.1/en/innodb-troubleshooting.html\n",
5821

5822
						ib_table->name);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5823 5824 5825
				break;
			}

5826 5827
			for (j = 0; j < table->key_info[i].key_parts; j++) {

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5828
				if (j + 1 > index->n_uniq) {
5829 5830 5831 5832
					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? "
5833
"See http://dev.mysql.com/doc/refman/5.1/en/innodb-troubleshooting.html\n",
5834 5835 5836 5837
							index->name,
							ib_table->name,
							(unsigned long)
							index->n_uniq, j + 1);
5838
					break;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5839 5840
				}

5841 5842
				if (index->stat_n_diff_key_vals[j + 1] == 0) {

5843
					rec_per_key = stats.records;
5844
				} else {
5845
					rec_per_key = (ha_rows)(stats.records /
5846
					 index->stat_n_diff_key_vals[j + 1]);
5847 5848
				}

heikki@donna.mysql.fi's avatar
heikki@donna.mysql.fi committed
5849 5850 5851 5852 5853 5854 5855
				/* 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;

5856 5857 5858
				if (rec_per_key == 0) {
					rec_per_key = 1;
				}
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
5859

5860
				table->key_info[i].rec_per_key[j]=
5861
				  rec_per_key >= ~(ulong) 0 ? ~(ulong) 0 :
5862
				  (ulong) rec_per_key;
5863
			}
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
5864

5865
			index = dict_table_get_next_index_noninline(index);
5866 5867
		}
	}
5868

5869
	if (flag & HA_STATUS_ERRKEY) {
5870 5871
		ut_a(prebuilt->trx);
		ut_a(prebuilt->trx->magic_n == TRX_MAGIC_N);
heikki@donna.mysql.fi's avatar
heikki@donna.mysql.fi committed
5872

5873
		errkey = (unsigned int) row_get_mysql_key_number_for_index(
5874 5875
			(dict_index_t*) trx_get_error_info(prebuilt->trx));
	}
5876

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5877
	if (flag & HA_STATUS_AUTO && table->found_next_number_field) {
5878
		ulonglong	auto_inc;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5879 5880 5881 5882
		int		ret;

		/* The following function call can the first time fail in
		a lock wait timeout error because it reserves the auto-inc
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5883
		lock on the table. If it fails, then someone is already initing
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5884 5885 5886
		the auto-inc counter, and the second call is guaranteed to
		succeed. */

5887
		ret = innobase_read_and_init_auto_inc(&auto_inc);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5888 5889 5890 5891 5892 5893 5894 5895 5896 5897 5898

		if (ret != 0) {
			ret = innobase_read_and_init_auto_inc(&auto_inc);

			if (ret != 0) {
				sql_print_error("Cannot get table %s auto-inc"
						"counter value in ::info\n",
						ib_table->name);
				auto_inc = 0;
			}
		}
5899

5900
		stats.auto_increment_value = auto_inc;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5901 5902
	}

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5903 5904
	prebuilt->trx->op_info = (char*)"";

5905
  	DBUG_RETURN(0);
5906 5907
}

5908
/**************************************************************************
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5909 5910
Updates index cardinalities of the table, based on 8 random dives into
each index tree. This does NOT calculate exact statistics on the table. */
5911 5912 5913

int
ha_innobase::analyze(
5914
/*=================*/
5915 5916 5917 5918 5919 5920 5921 5922 5923 5924
					/* 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);
}

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5925
/**************************************************************************
5926
This is mapped to "ALTER TABLE tablename ENGINE=InnoDB", which rebuilds
5927
the table in MySQL. */
5928

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5929 5930 5931 5932 5933
int
ha_innobase::optimize(
/*==================*/
	THD*		thd,		/* in: connection thread handle */
	HA_CHECK_OPT*	check_opt)	/* in: currently ignored */
5934
{
5935
	return(HA_ADMIN_TRY_ALTER);
5936 5937
}

5938 5939 5940 5941 5942 5943 5944 5945 5946 5947
/***********************************************************************
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 */
5948 5949
	THD*		thd,		/* in: user thread handle */
	HA_CHECK_OPT*	check_opt)	/* in: check options, currently
5950 5951 5952
					ignored */
{
	ulint		ret;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
5953

5954 5955 5956 5957
	DBUG_ASSERT(thd == ha_thd());
	ut_a(prebuilt->trx);
	ut_a(prebuilt->trx->magic_n == TRX_MAGIC_N);
	ut_a(prebuilt->trx == thd_to_trx(thd));
heikki@donna.mysql.fi's avatar
heikki@donna.mysql.fi committed
5958

5959 5960 5961 5962 5963 5964 5965 5966 5967 5968 5969 5970
	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);
	}
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
5971

5972
	return(HA_ADMIN_CORRUPT);
5973 5974
}

5975
/*****************************************************************
monty@donna.mysql.fi's avatar
Merge  
monty@donna.mysql.fi committed
5976 5977 5978
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. */
5979 5980 5981 5982

char*
ha_innobase::update_table_comment(
/*==============================*/
monty@donna.mysql.fi's avatar
Merge  
monty@donna.mysql.fi committed
5983 5984
				/* out: table comment + InnoDB free space +
				info on foreign keys */
5985
	const char*	comment)/* in: table comment defined by user */
5986
{
5987 5988
	uint	length = (uint) strlen(comment);
	char*	str;
5989
	long	flen;
5990

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
5991 5992 5993 5994
	/* 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. */

monty@mishka.local's avatar
monty@mishka.local committed
5995
	if (length > 64000 - 3) {
5996 5997 5998
		return((char*)comment); /* string too long */
	}

5999
	update_thd(ha_thd());
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6000

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6001 6002
	prebuilt->trx->op_info = (char*)"returning table comment";

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6003 6004 6005 6006
	/* 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);
6007
	str = NULL;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6008

6009
	/* output the data to a temporary file */
monty@donna.mysql.fi's avatar
Merge  
monty@donna.mysql.fi committed
6010

6011 6012
	mutex_enter_noninline(&srv_dict_tmpfile_mutex);
	rewind(srv_dict_tmpfile);
6013

6014 6015 6016
	fprintf(srv_dict_tmpfile, "InnoDB free: %llu kB",
		fsp_get_available_space_in_free_extents(
			prebuilt->table->space));
6017 6018

	dict_print_info_on_foreign_keys(FALSE, srv_dict_tmpfile,
6019
				prebuilt->trx, prebuilt->table);
6020 6021 6022 6023 6024 6025
	flen = ftell(srv_dict_tmpfile);
	if (flen < 0) {
		flen = 0;
	} else if (length + flen + 3 > 64000) {
		flen = 64000 - 3 - length;
	}
6026

6027 6028
	/* allocate buffer for the full string, and
	read the contents of the temporary file */
6029

6030
	str = (char*) my_malloc(length + flen + 3, MYF(0));
6031

6032 6033 6034 6035 6036 6037
	if (str) {
		char* pos	= str + length;
		if (length) {
			memcpy(str, comment, length);
			*pos++ = ';';
			*pos++ = ' ';
6038
		}
6039 6040 6041
		rewind(srv_dict_tmpfile);
		flen = (uint) fread(pos, 1, flen, srv_dict_tmpfile);
		pos[flen] = 0;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6042
	}
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
6043

6044 6045 6046
	mutex_exit_noninline(&srv_dict_tmpfile_mutex);

	prebuilt->trx->op_info = (char*)"";
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6047

6048
	return(str ? str : (char*) comment);
6049 6050
}

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6051 6052 6053 6054 6055 6056 6057 6058 6059 6060
/***********************************************************************
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 */
{
6061
	char*	str	= 0;
6062
	long	flen;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6063

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6064
	ut_a(prebuilt != NULL);
6065

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6066 6067 6068 6069
	/* 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. */

6070
	update_thd(ha_thd());
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6071

6072
	prebuilt->trx->op_info = (char*)"getting info on foreign keys";
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6073

6074 6075 6076
	/* In case MySQL calls this in the middle of a SELECT query,
	release possible adaptive hash latch to avoid
	deadlocks of threads */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6077

6078
	trx_search_latch_release_if_reserved(prebuilt->trx);
6079

6080 6081
	mutex_enter_noninline(&srv_dict_tmpfile_mutex);
	rewind(srv_dict_tmpfile);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
6082

6083 6084
	/* output the data to a temporary file */
	dict_print_info_on_foreign_keys(TRUE, srv_dict_tmpfile,
6085
				prebuilt->trx, prebuilt->table);
6086
	prebuilt->trx->op_info = (char*)"";
6087

6088 6089 6090 6091 6092 6093
	flen = ftell(srv_dict_tmpfile);
	if (flen < 0) {
		flen = 0;
	} else if (flen > 64000 - 1) {
		flen = 64000 - 1;
	}
6094

6095 6096
	/* allocate buffer for the string, and
	read the contents of the temporary file */
6097

6098
	str = (char*) my_malloc(flen + 1, MYF(0));
6099

6100 6101 6102 6103
	if (str) {
		rewind(srv_dict_tmpfile);
		flen = (uint) fread(str, 1, flen, srv_dict_tmpfile);
		str[flen] = 0;
6104
	}
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6105

6106 6107 6108
	mutex_exit_noninline(&srv_dict_tmpfile_mutex);

	return(str);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6109
}
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6110

6111

6112
int
6113 6114 6115 6116 6117 6118
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");
  ut_a(prebuilt != NULL);
6119
  update_thd(ha_thd());
6120 6121
  prebuilt->trx->op_info = (char*)"getting list of foreign keys";
  trx_search_latch_release_if_reserved(prebuilt->trx);
6122
  mutex_enter_noninline(&(dict_sys->mutex));
6123 6124
  foreign = UT_LIST_GET_FIRST(prebuilt->table->foreign_list);

6125 6126 6127 6128
  while (foreign != NULL) {
	  uint i;
	  FOREIGN_KEY_INFO f_key_info;
	  LEX_STRING *name= 0;
6129
          uint ulen;
6130 6131
          char uname[NAME_LEN+1];           /* Unencoded name */
          char db_name[NAME_LEN+1];
6132 6133 6134 6135 6136 6137 6138
	  const char *tmp_buff;

	  tmp_buff= foreign->id;
	  i= 0;
	  while (tmp_buff[i] != '/')
		  i++;
	  tmp_buff+= i + 1;
6139 6140
	  f_key_info.forein_id = thd_make_lex_string(thd, 0,
		  tmp_buff, (uint) strlen(tmp_buff), 1);
6141
	  tmp_buff= foreign->referenced_table_name;
6142 6143

          /* Database name */
6144 6145
	  i= 0;
	  while (tmp_buff[i] != '/')
6146 6147 6148 6149 6150 6151
          {
            db_name[i]= tmp_buff[i];
            i++;
          }
          db_name[i]= 0;
          ulen= filename_to_tablename(db_name, uname, sizeof(uname));
6152 6153
	  f_key_info.referenced_db = thd_make_lex_string(thd, 0,
		  uname, ulen, 1);
6154 6155

          /* Table name */
6156
	  tmp_buff+= i + 1;
6157
          ulen= filename_to_tablename(tmp_buff, uname, sizeof(uname));
6158 6159
	  f_key_info.referenced_table = thd_make_lex_string(thd, 0,
		  uname, ulen, 1);
6160 6161 6162

	  for (i= 0;;) {
		  tmp_buff= foreign->foreign_col_names[i];
6163 6164
		  name = thd_make_lex_string(thd, name,
			  tmp_buff, (uint) strlen(tmp_buff), 1);
6165 6166
		  f_key_info.foreign_fields.push_back(name);
		  tmp_buff= foreign->referenced_col_names[i];
6167 6168
		  name = thd_make_lex_string(thd, name,
			tmp_buff, (uint) strlen(tmp_buff), 1);
6169 6170 6171 6172 6173
		  f_key_info.referenced_fields.push_back(name);
		  if (++i >= foreign->n_fields)
			  break;
	  }

6174 6175 6176 6177 6178 6179 6180 6181 6182 6183 6184 6185 6186 6187 6188 6189 6190 6191 6192 6193 6194
          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";
          }
6195 6196
	  f_key_info.delete_method = thd_make_lex_string(
		  thd, f_key_info.delete_method, tmp_buff, length, 1);
6197 6198 6199 6200 6201 6202 6203 6204 6205 6206 6207 6208 6209 6210 6211 6212 6213 6214 6215 6216 6217 6218
 
 
          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";
          }
6219 6220
	  f_key_info.update_method = thd_make_lex_string(
		  thd, f_key_info.update_method, tmp_buff, length, 1);
6221 6222 6223
          if (foreign->referenced_index &&
              foreign->referenced_index->name)
          {
6224 6225 6226 6227
	    f_key_info.referenced_key_name = thd_make_lex_string(
		    thd, f_key_info.referenced_key_name,
		    foreign->referenced_index->name,
		    strlen(foreign->referenced_index->name), 1);
6228
          }
6229 6230
          else
            f_key_info.referenced_key_name= 0;
6231

6232 6233
	  FOREIGN_KEY_INFO *pf_key_info = (FOREIGN_KEY_INFO *)
		  thd_memdup(thd, &f_key_info, sizeof(FOREIGN_KEY_INFO));
6234 6235
	  f_key_list->push_back(pf_key_info);
	  foreign = UT_LIST_GET_NEXT(foreign_list, foreign);
6236
  }
6237
  mutex_exit_noninline(&(dict_sys->mutex));
6238
  prebuilt->trx->op_info = (char*)"";
6239

6240 6241 6242
  DBUG_RETURN(0);
}

6243 6244 6245 6246 6247 6248 6249 6250 6251 6252 6253
/*********************************************************************
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)
/*=================================*/
{
	bool	can_switch;

6254
	DBUG_ENTER("ha_innobase::can_switch_engines");
6255

6256
	ut_a(prebuilt->trx == thd_to_trx(ha_thd()));
6257

6258 6259 6260 6261 6262 6263 6264 6265 6266 6267 6268 6269 6270
	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);
}

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6271 6272 6273 6274 6275 6276 6277 6278 6279 6280 6281 6282 6283 6284 6285 6286 6287 6288
/***********************************************************************
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 */
{
	if (dict_table_referenced_by_foreign_key(prebuilt->table)) {

		return(1);
	}

	return(0);
}
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6289 6290 6291 6292 6293 6294 6295 6296

/***********************************************************************
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(
/*======================================*/
6297
	char*	str)	/* in, own: create info string to free	*/
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6298 6299
{
	if (str) {
6300
		my_free(str, MYF(0));
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6301
	}
6302 6303
}

heikki@donna.mysql.fi's avatar
heikki@donna.mysql.fi committed
6304 6305 6306 6307 6308 6309 6310 6311
/***********************************************************************
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)
6312
			   /* in: HA_EXTRA_FLUSH or some other flag */
heikki@donna.mysql.fi's avatar
heikki@donna.mysql.fi committed
6313 6314 6315 6316 6317 6318
{
	/* 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) {
6319 6320 6321 6322 6323 6324
		case HA_EXTRA_FLUSH:
			if (prebuilt->blob_heap) {
				row_mysql_prebuilt_free_blob_heap(prebuilt);
			}
			break;
		case HA_EXTRA_RESET_STATE:
6325
			reset_template(prebuilt);
6326
			break;
heikki@donna.mysql.fi's avatar
heikki@donna.mysql.fi committed
6327
		case HA_EXTRA_NO_KEYREAD:
6328 6329 6330 6331 6332
			prebuilt->read_just_key = 0;
			break;
		case HA_EXTRA_KEYREAD:
			prebuilt->read_just_key = 1;
			break;
6333 6334 6335
		case HA_EXTRA_KEYREAD_PRESERVE_FIELDS:
			prebuilt->keep_other_fields_on_keyread = 1;
			break;
6336 6337 6338 6339 6340 6341 6342

			/* IMPORTANT: prebuilt->trx can be obsolete in
			this method, because it is not sure that MySQL
			calls external_lock before this method with the
			parameters below.  We must not invoke update_thd()
			either, because the calling threads may change.
			CAREFUL HERE, OR MEMORY CORRUPTION MAY OCCUR! */
antony@ppcg5.local's avatar
antony@ppcg5.local committed
6343
		case HA_EXTRA_IGNORE_DUP_KEY:
6344
			thd_to_trx(ha_thd())->duplicates |= TRX_DUP_IGNORE;
antony@ppcg5.local's avatar
antony@ppcg5.local committed
6345 6346
			break;
		case HA_EXTRA_WRITE_CAN_REPLACE:
6347
			thd_to_trx(ha_thd())->duplicates |= TRX_DUP_REPLACE;
antony@ppcg5.local's avatar
antony@ppcg5.local committed
6348 6349
			break;
		case HA_EXTRA_WRITE_CANNOT_REPLACE:
6350
			thd_to_trx(ha_thd())->duplicates &= ~TRX_DUP_REPLACE;
antony@ppcg5.local's avatar
antony@ppcg5.local committed
6351 6352
			break;
		case HA_EXTRA_NO_IGNORE_DUP_KEY:
6353 6354
			thd_to_trx(ha_thd())->duplicates &=
				~(TRX_DUP_IGNORE | TRX_DUP_REPLACE);
antony@ppcg5.local's avatar
antony@ppcg5.local committed
6355
			break;
heikki@donna.mysql.fi's avatar
heikki@donna.mysql.fi committed
6356 6357 6358 6359 6360 6361 6362
		default:/* Do nothing */
			;
	}

	return(0);
}

6363 6364 6365 6366 6367
int ha_innobase::reset()
{
  if (prebuilt->blob_heap) {
    row_mysql_prebuilt_free_blob_heap(prebuilt);
  }
6368
  reset_template(prebuilt);
6369 6370 6371 6372
  return 0;
}


6373
/**********************************************************************
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6374 6375 6376 6377
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
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6378 6379 6380 6381
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
6382
locks (thd_in_lock_tables(thd) holds in store_lock()) before executing the
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6383
procedure. */
6384 6385

int
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6386 6387
ha_innobase::start_stmt(
/*====================*/
6388 6389 6390
				/* out: 0 or error code */
	THD*		thd,	/* in: handle to the user thread */
	thr_lock_type	lock_type)
6391 6392 6393 6394 6395 6396 6397
{
	trx_t*		trx;

	update_thd(thd);

	trx = prebuilt->trx;

6398 6399 6400 6401 6402 6403 6404
	/* 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. */

6405 6406
	innobase_release_stat_resources(trx);

6407 6408 6409
	/* Reset the AUTOINC statement level counter for multi-row INSERTs. */
	trx->n_autoinc_rows = 0;

6410
	prebuilt->sql_stat_start = TRUE;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6411
	prebuilt->hint_need_to_fetch_extra_cols = 0;
6412
	reset_template(prebuilt);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6413

6414
	if (!prebuilt->mysql_has_locked) {
6415 6416 6417 6418 6419 6420
		/* 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;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6421 6422
	} else {
		if (trx->isolation_level != TRX_ISO_SERIALIZABLE
6423
			&& thd_sql_command(thd) == SQLCOM_SELECT
6424 6425
			&& lock_type == TL_READ) {

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6426 6427 6428 6429 6430 6431 6432 6433 6434
			/* 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(),
6435 6436
			2) ::external_lock(),
			3) ::init_table_handle_for_HANDLER(), and
6437
			4) ::transactional_table_lock(). */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6438 6439 6440 6441 6442 6443

			prebuilt->select_lock_type =
				prebuilt->stored_select_lock_type;
		}
	}

6444 6445
	trx->detailed_error[0] = '\0';

6446
	/* Set the MySQL flag to mark that there is an active transaction */
6447
	if (trx->active_trans == 0) {
6448

6449
		innobase_register_trx_and_stmt(ht, thd);
6450 6451
		trx->active_trans = 1;
	} else {
6452
		innobase_register_stmt(ht, thd);
6453
	}
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6454 6455

	return(0);
6456 6457
}

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6458 6459 6460 6461 6462 6463 6464 6465 6466 6467 6468
/**********************************************************************
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);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6469
		case ISO_READ_COMMITTED: return(TRX_ISO_READ_COMMITTED);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6470 6471 6472
		case ISO_SERIALIZABLE: return(TRX_ISO_SERIALIZABLE);
		case ISO_READ_UNCOMMITTED: return(TRX_ISO_READ_UNCOMMITTED);
		default: ut_a(0); return(0);
6473
	}
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6474
}
6475

heikki@donna.mysql.fi's avatar
heikki@donna.mysql.fi committed
6476 6477
/**********************************************************************
As MySQL will execute an external lock for every new table it uses when it
6478 6479 6480
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
heikki@donna.mysql.fi's avatar
heikki@donna.mysql.fi committed
6481 6482 6483 6484 6485 6486 6487
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(
/*=======================*/
6488
				/* out: 0 */
heikki@donna.mysql.fi's avatar
heikki@donna.mysql.fi committed
6489
	THD*	thd,		/* in: handle to the user thread */
6490
	int	lock_type)	/* in: lock type */
heikki@donna.mysql.fi's avatar
heikki@donna.mysql.fi committed
6491 6492 6493
{
	trx_t*		trx;

6494
	DBUG_ENTER("ha_innobase::external_lock");
6495
	DBUG_PRINT("enter",("lock_type: %d", lock_type));
heikki@donna.mysql.fi's avatar
heikki@donna.mysql.fi committed
6496 6497 6498

	update_thd(thd);

6499 6500 6501 6502 6503 6504 6505 6506 6507 6508 6509 6510 6511 6512 6513 6514 6515 6516 6517 6518 6519
	/* Statement based binlogging does not work in isolation level
	READ UNCOMMITTED and READ COMMITTED since the necessary
	locks cannot be taken. In this case, we print an
	informative error message and return with an error. */
	if (lock_type == F_WRLCK)
	{
		ulong const binlog_format= thd_binlog_format(thd);
		ulong const tx_isolation = thd_tx_isolation(current_thd);
		if (tx_isolation <= ISO_READ_COMMITTED &&
		    binlog_format == BINLOG_FORMAT_STMT)
		{
			char buf[256];
			my_snprintf(buf, sizeof(buf),
				    "Transaction level '%s' in"
				    " InnoDB is not safe for binlog mode '%s'",
				    tx_isolation_names[tx_isolation],
				    binlog_format_names[binlog_format]);
			my_error(ER_BINLOG_LOGGING_IMPOSSIBLE, MYF(0), buf);
			DBUG_RETURN(HA_ERR_LOGGING_IMPOSSIBLE);
		}
	}
6520 6521


heikki@donna.mysql.fi's avatar
heikki@donna.mysql.fi committed
6522 6523 6524
	trx = prebuilt->trx;

	prebuilt->sql_stat_start = TRUE;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6525
	prebuilt->hint_need_to_fetch_extra_cols = 0;
heikki@donna.mysql.fi's avatar
heikki@donna.mysql.fi committed
6526

6527
	reset_template(prebuilt);
heikki@donna.mysql.fi's avatar
heikki@donna.mysql.fi committed
6528 6529 6530 6531 6532 6533

	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;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6534
		prebuilt->stored_select_lock_type = LOCK_X;
heikki@donna.mysql.fi's avatar
heikki@donna.mysql.fi committed
6535 6536 6537
	}

	if (lock_type != F_UNLCK) {
6538
		/* MySQL is setting a new table lock */
heikki@donna.mysql.fi's avatar
heikki@donna.mysql.fi committed
6539

6540
		trx->detailed_error[0] = '\0';
6541

6542 6543
		/* Set the MySQL flag to mark that there is an active
		transaction */
6544
		if (trx->active_trans == 0) {
6545

6546
			innobase_register_trx_and_stmt(ht, thd);
6547 6548
			trx->active_trans = 1;
		} else if (trx->n_mysql_tables_in_use == 0) {
6549
			innobase_register_stmt(ht, thd);
6550
		}
6551

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6552
		if (trx->isolation_level == TRX_ISO_SERIALIZABLE
6553
			&& prebuilt->select_lock_type == LOCK_NONE
antony@ppcg5.local's avatar
antony@ppcg5.local committed
6554 6555
			&& thd_test_options(thd,
				OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6556

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6557 6558
			/* To get serializable execution, we let InnoDB
			conceptually add 'LOCK IN SHARE MODE' to all SELECTs
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6559 6560 6561 6562 6563
			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. */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6564 6565

			prebuilt->select_lock_type = LOCK_S;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6566
			prebuilt->stored_select_lock_type = LOCK_S;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6567 6568
		}

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6569 6570 6571 6572
		/* 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
6573 6574 6575
		VERY easily deadlocks.

		We do not set InnoDB table locks if user has not explicitly
6576 6577
		requested a table lock. Note that thd_in_lock_tables(thd)
		can hold in some cases, e.g., at the start of a stored
6578
		procedure call (SQLCOM_CALL). */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6579

heikki@donna.mysql.fi's avatar
heikki@donna.mysql.fi committed
6580
		if (prebuilt->select_lock_type != LOCK_NONE) {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6581

6582 6583 6584 6585
			if (thd_sql_command(thd) == SQLCOM_LOCK_TABLES
			    && THDVAR(thd, table_locks)
			    && thd_test_options(thd, OPTION_NOT_AUTOCOMMIT)
			    && thd_in_lock_tables(thd)) {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6586

6587 6588
				ulint	error = row_lock_table_for_mysql(
					prebuilt, NULL, 0);
6589 6590 6591

				if (error != DB_SUCCESS) {
					error = convert_error_code_to_mysql(
6592
						(int) error, thd);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6593
					DBUG_RETURN((int) error);
6594 6595
				}
			}
heikki@donna.mysql.fi's avatar
heikki@donna.mysql.fi committed
6596

6597
			trx->mysql_n_tables_locked++;
heikki@donna.mysql.fi's avatar
heikki@donna.mysql.fi committed
6598 6599
		}

6600 6601 6602
		trx->n_mysql_tables_in_use++;
		prebuilt->mysql_has_locked = TRUE;

6603
		DBUG_RETURN(0);
6604
	}
heikki@donna.mysql.fi's avatar
heikki@donna.mysql.fi committed
6605

6606
	/* MySQL is releasing a table lock */
heikki@donna.mysql.fi's avatar
heikki@donna.mysql.fi committed
6607

6608 6609
	trx->n_mysql_tables_in_use--;
	prebuilt->mysql_has_locked = FALSE;
6610

6611 6612 6613 6614 6615 6616
	/* 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. */

	innobase_release_stat_resources(trx);

6617 6618
	/* If the MySQL lock count drops to zero we know that the current SQL
	statement has ended */
heikki@donna.mysql.fi's avatar
heikki@donna.mysql.fi committed
6619

6620
	if (trx->n_mysql_tables_in_use == 0) {
heikki@donna.mysql.fi's avatar
heikki@donna.mysql.fi committed
6621

6622
		trx->mysql_n_tables_locked = 0;
6623
		prebuilt->used_in_HANDLER = FALSE;
6624

antony@ppcg5.local's avatar
antony@ppcg5.local committed
6625
		if (!thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) {
6626
			if (trx->active_trans != 0) {
6627
				innobase_commit(ht, thd, TRUE);
6628 6629
			}
		} else {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6630
			if (trx->isolation_level <= TRX_ISO_READ_COMMITTED
6631
						&& trx->global_read_view) {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6632

6633
				/* At low transaction isolation levels we let
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6634 6635
				each consistent read set its own snapshot */

6636
				read_view_close_for_mysql(trx);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6637
			}
heikki@donna.mysql.fi's avatar
heikki@donna.mysql.fi committed
6638 6639 6640
		}
	}

6641
	DBUG_RETURN(0);
heikki@donna.mysql.fi's avatar
heikki@donna.mysql.fi committed
6642 6643
}

6644 6645 6646 6647 6648 6649 6650
/**********************************************************************
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(
/*==================================*/
6651
				/* out: error code */
6652
	THD*	thd,		/* in: handle to the user thread */
6653
	int	lock_type)	/* in: lock type */
6654 6655 6656
{
	trx_t*		trx;

6657
	DBUG_ENTER("ha_innobase::transactional_table_lock");
6658 6659 6660 6661 6662 6663 6664 6665
	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);

6666
	if (prebuilt->table->ibd_file_missing && !thd_tablespace_op(thd)) {
6667
		ut_print_timestamp(stderr);
6668 6669 6670 6671 6672 6673 6674 6675 6676 6677 6678
		fprintf(stderr,
			"  InnoDB: MySQL is trying to use a table handle"
			" but the .ibd file for\n"
			"InnoDB: table %s does not exist.\n"
			"InnoDB: Have you deleted the .ibd file"
			" from the database directory under\n"
			"InnoDB: the MySQL datadir?"
			"InnoDB: See"
			" http://dev.mysql.com/doc/refman/5.1/en/innodb-troubleshooting.html\n"
			"InnoDB: how you can resolve the problem.\n",
			prebuilt->table->name);
6679 6680 6681 6682 6683 6684 6685 6686
		DBUG_RETURN(HA_ERR_CRASHED);
	}

	trx = prebuilt->trx;

	prebuilt->sql_stat_start = TRUE;
	prebuilt->hint_need_to_fetch_extra_cols = 0;

6687
	reset_template(prebuilt);
6688 6689 6690 6691 6692

	if (lock_type == F_WRLCK) {
		prebuilt->select_lock_type = LOCK_X;
		prebuilt->stored_select_lock_type = LOCK_X;
	} else if (lock_type == F_RDLCK) {
jan@hundin.mysql.fi's avatar
jan@hundin.mysql.fi committed
6693 6694
		prebuilt->select_lock_type = LOCK_S;
		prebuilt->stored_select_lock_type = LOCK_S;
6695
	} else {
6696 6697
		ut_print_timestamp(stderr);
		fprintf(stderr, "  InnoDB error:\n"
6698 6699 6700 6701 6702 6703 6704 6705 6706
"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 */
6707
	if (trx->active_trans == 0) {
serg@serg.mylan's avatar
serg@serg.mylan committed
6708

6709
		innobase_register_trx_and_stmt(ht, thd);
6710 6711
		trx->active_trans = 1;
	}
6712

6713
	if (THDVAR(thd, table_locks) && thd_in_lock_tables(thd)) {
6714 6715
		ulint	error = DB_SUCCESS;

6716
		error = row_lock_table_for_mysql(prebuilt, NULL, 0);
6717 6718

		if (error != DB_SUCCESS) {
6719
			error = convert_error_code_to_mysql((int) error, thd);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6720
			DBUG_RETURN((int) error);
6721 6722
		}

antony@ppcg5.local's avatar
antony@ppcg5.local committed
6723
		if (thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) {
6724

6725 6726
			/* Store the current undo_no of the transaction
			so that we know where to roll back if we have
6727 6728 6729 6730 6731 6732 6733 6734 6735
			to roll back the next SQL statement */

			trx_mark_sql_stat_end(trx);
		}
	}

	DBUG_RETURN(0);
}

6736 6737
/****************************************************************************
Here we export InnoDB status variables to MySQL.  */
6738
static
6739
int
6740
innodb_export_status()
6741
/*==================*/
6742
{
6743 6744 6745 6746 6747
	if (innodb_inited) {
		srv_export_innodb_status();
	}

	return 0;
6748 6749
}

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6750
/****************************************************************************
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6751
Implements the SHOW INNODB STATUS command. Sends the output of the InnoDB
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6752
Monitor to the client. */
6753
static
6754
bool
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6755 6756
innodb_show_status(
/*===============*/
6757
	handlerton*	hton,	/* in: the innodb handlerton */
6758 6759
	THD*	thd,	/* in: the MySQL query thread of the caller */
	stat_print_fn *stat_print)
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6760
{
6761 6762 6763 6764 6765
	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;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6766

6767
	DBUG_ENTER("innodb_show_status");
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6768

6769
	trx = check_trx_exists(thd);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6770 6771 6772

	innobase_release_stat_resources(trx);

6773 6774
	/* We let the InnoDB Monitor to output at most MAX_STATUS_SIZE
	bytes of text. */
6775

6776
	long	flen, usable_len;
6777
	char*	str;
6778

6779
	mutex_enter_noninline(&srv_monitor_file_mutex);
6780
	rewind(srv_monitor_file);
6781 6782
	srv_printf_innodb_monitor(srv_monitor_file,
				&trx_list_start, &trx_list_end);
6783
	flen = ftell(srv_monitor_file);
6784
	os_file_set_eof(srv_monitor_file);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6785

6786 6787
	if (flen < 0) {
		flen = 0;
6788 6789 6790 6791 6792 6793
	}

	if (flen > MAX_STATUS_SIZE) {
		usable_len = MAX_STATUS_SIZE;
	} else {
		usable_len = flen;
6794
	}
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6795

6796 6797
	/* allocate buffer for the string, and
	read the contents of the temporary file */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6798

6799
	if (!(str = (char*) my_malloc(usable_len + 1, MYF(0)))) {
6800 6801 6802
	  mutex_exit_noninline(&srv_monitor_file_mutex);
	  DBUG_RETURN(TRUE);
	}
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6803

monty@mishka.local's avatar
monty@mishka.local committed
6804
	rewind(srv_monitor_file);
6805 6806
	if (flen < MAX_STATUS_SIZE) {
		/* Display the entire output. */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6807
		flen = (long) fread(str, 1, flen, srv_monitor_file);
6808 6809 6810 6811 6812
	} 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. */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6813
		long len = (long) fread(str, 1, trx_list_start, srv_monitor_file);
6814 6815 6816 6817
		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);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6818
		len += (long) fread(str + len, 1, usable_len, srv_monitor_file);
6819 6820 6821
		flen = len;
	} else {
		/* Omit the end of the output. */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6822
		flen = (long) fread(str, 1, MAX_STATUS_SIZE - 1, srv_monitor_file);
6823
	}
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6824

6825
	mutex_exit_noninline(&srv_monitor_file_mutex);
6826

6827
	bool result = FALSE;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6828

6829
	if (stat_print(thd, innobase_hton_name, strlen(innobase_hton_name),
6830
			STRING_WITH_LEN(""), str, flen)) {
6831
		result= TRUE;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6832
	}
6833
	my_free(str, MYF(0));
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6834

6835
	DBUG_RETURN(FALSE);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
6836 6837
}

vtkachenko@intelp4d.mysql.com's avatar
vtkachenko@intelp4d.mysql.com committed
6838 6839
/****************************************************************************
Implements the SHOW MUTEX STATUS command. . */
6840
static
vtkachenko@intelp4d.mysql.com's avatar
vtkachenko@intelp4d.mysql.com committed
6841 6842
bool
innodb_mutex_show_status(
6843
/*=====================*/
6844
	handlerton*	hton,	/* in: the innodb handlerton */
6845 6846 6847 6848 6849 6850
	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;
6851
#ifdef UNIV_DEBUG
6852 6853 6854 6855 6856 6857
	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;
6858
#endif /* UNIV_DEBUG */
6859
	uint	  hton_name_len= strlen(innobase_hton_name), buf1len, buf2len;
6860
	DBUG_ENTER("innodb_mutex_show_status");
vtkachenko@intelp4d.mysql.com's avatar
vtkachenko@intelp4d.mysql.com committed
6861

6862
	mutex_enter_noninline(&mutex_list_mutex);
vtkachenko@intelp4d.mysql.com's avatar
vtkachenko@intelp4d.mysql.com committed
6863

6864 6865 6866
	mutex = UT_LIST_GET_FIRST(mutex_list);

	while (mutex != NULL) {
6867
#ifdef UNIV_DEBUG
6868 6869 6870 6871 6872 6873 6874 6875 6876 6877 6878 6879 6880 6881 6882
		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,
6883
					(ulong) (mutex->lspent_time/1000));
6884

6885
				if (stat_print(thd, innobase_hton_name,
6886 6887
						hton_name_len, buf1, buf1len,
						buf2, buf2len)) {
6888 6889
					mutex_exit_noninline(
						&mutex_list_mutex);
6890 6891 6892 6893 6894 6895 6896 6897 6898 6899 6900 6901
					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;
		}
6902 6903 6904 6905 6906 6907 6908 6909 6910 6911 6912 6913 6914
#else /* UNIV_DEBUG */
		buf1len= my_snprintf(buf1, sizeof(buf1), "%s:%lu",
				     mutex->cfile_name, (ulong) mutex->cline);
		buf2len= my_snprintf(buf2, sizeof(buf2), "os_waits=%lu",
				     mutex->count_os_wait);

		if (stat_print(thd, innobase_hton_name,
			       hton_name_len, buf1, buf1len,
			       buf2, buf2len)) {
			mutex_exit_noninline(&mutex_list_mutex);
			DBUG_RETURN(1);
		}
#endif /* UNIV_DEBUG */
vtkachenko@intelp4d.mysql.com's avatar
vtkachenko@intelp4d.mysql.com committed
6915

6916 6917 6918
		mutex = UT_LIST_GET_NEXT(list, mutex);
	}

6919 6920 6921
	mutex_exit_noninline(&mutex_list_mutex);

#ifdef UNIV_DEBUG
6922 6923 6924 6925 6926 6927
	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,
6928
		(ulong) (rw_lock_wait_time/1000));
6929

6930
	if (stat_print(thd, innobase_hton_name, hton_name_len,
6931 6932 6933
			STRING_WITH_LEN("rw_lock_mutexes"), buf2, buf2len)) {
		DBUG_RETURN(1);
	}
6934
#endif /* UNIV_DEBUG */
6935 6936

	DBUG_RETURN(FALSE);
vtkachenko@intelp4d.mysql.com's avatar
vtkachenko@intelp4d.mysql.com committed
6937 6938
}

6939
static
6940 6941 6942
bool innobase_show_status(handlerton *hton, THD* thd, 
                          stat_print_fn* stat_print,
                          enum ha_stat_type stat_type)
6943 6944 6945
{
	switch (stat_type) {
	case HA_ENGINE_STATUS:
6946
		return innodb_show_status(hton, thd, stat_print);
6947
	case HA_ENGINE_MUTEX:
6948
		return innodb_mutex_show_status(hton, thd, stat_print);
6949 6950 6951
	default:
		return FALSE;
	}
6952 6953 6954
}


6955 6956 6957 6958 6959
/****************************************************************************
 Handling the shared INNOBASE_SHARE structure that is needed to provide table
 locking.
****************************************************************************/

6960
static uchar* innobase_get_key(INNOBASE_SHARE* share, size_t *length,
6961 6962 6963 6964
	my_bool not_used __attribute__((unused)))
{
	*length=share->table_name_length;

6965
	return (uchar*) share->table_name;
6966 6967
}

6968
static INNOBASE_SHARE* get_share(const char* table_name)
6969
{
6970 6971 6972 6973 6974
	INNOBASE_SHARE *share;
	pthread_mutex_lock(&innobase_share_mutex);
	uint length=(uint) strlen(table_name);

	if (!(share=(INNOBASE_SHARE*) hash_search(&innobase_open_tables,
6975
				(uchar*) table_name,
6976 6977 6978 6979 6980 6981 6982 6983 6984 6985
				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,
6986
				(uchar*) share)) {
6987
			pthread_mutex_unlock(&innobase_share_mutex);
6988
			my_free(share,0);
6989 6990 6991 6992 6993 6994 6995 6996 6997 6998 6999 7000 7001 7002 7003 7004 7005 7006 7007

			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) {
7008
		hash_delete(&innobase_open_tables, (uchar*) share);
7009 7010
		thr_lock_delete(&share->lock);
		pthread_mutex_destroy(&share->mutex);
7011
		my_free(share, MYF(0));
7012 7013 7014
	}

	pthread_mutex_unlock(&innobase_share_mutex);
7015
}
7016 7017

/*********************************************************************
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7018
Converts a MySQL table lock stored in the 'lock' field of the handle to
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7019 7020 7021 7022 7023 7024
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. */
7025 7026 7027 7028 7029 7030 7031 7032 7033 7034 7035 7036

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 */
7037
	enum thr_lock_type	lock_type)	/* in: lock type to store in
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7038 7039
						'lock'; this may also be
						TL_IGNORE */
7040
{
7041 7042
	trx_t*		trx;

7043 7044 7045
	/* Note that trx in this function is NOT necessarily prebuilt->trx
	because we call update_thd() later, in ::external_lock()! Failure to
	understand this caused a serious memory corruption bug in 5.1.11. */
7046

7047
	trx = check_trx_exists(thd);
7048

7049
	/* NOTE: MySQL can call this function with lock 'type' TL_IGNORE!
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7050 7051 7052
	Be careful to ignore TL_IGNORE if we are going to do something with
	only 'real' locks! */

7053
	/* If no MySQL table is in use, we need to set the isolation level
7054 7055 7056
	of the transaction. */

	if (lock_type != TL_IGNORE
7057
	    && trx->n_mysql_tables_in_use == 0) {
7058
		trx->isolation_level = innobase_map_isolation_level(
7059
			(enum_tx_isolation) thd_tx_isolation(thd));
7060 7061 7062 7063 7064 7065 7066 7067 7068

		if (trx->isolation_level <= TRX_ISO_READ_COMMITTED
		    && trx->global_read_view) {

			/* At low transaction isolation levels we let
			each consistent read set its own snapshot */

			read_view_close_for_mysql(trx);
		}
7069 7070
	}

serg@janus.mylan's avatar
serg@janus.mylan committed
7071
	DBUG_ASSERT(thd == current_thd);
7072
	const bool in_lock_tables = thd_in_lock_tables(thd);
7073
	const uint sql_command = thd_sql_command(thd);
7074

antony@ppcg5.local's avatar
antony@ppcg5.local committed
7075
	if (sql_command == SQLCOM_DROP_TABLE) {
7076 7077 7078 7079 7080

		/* MySQL calls this function in DROP TABLE though this table
		handle may belong to another thd that is running a query. Let
		us in that case skip any changes to the prebuilt struct. */ 

7081 7082 7083 7084 7085 7086
	} else if ((lock_type == TL_READ && in_lock_tables)
		   || (lock_type == TL_READ_HIGH_PRIORITY && in_lock_tables)
		   || lock_type == TL_READ_WITH_SHARED_LOCKS
		   || lock_type == TL_READ_NO_INSERT
		   || (lock_type != TL_IGNORE
		       && sql_command != SQLCOM_SELECT)) {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7087

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7088
		/* The OR cases above are in this order:
7089 7090
		1) MySQL is doing LOCK TABLES ... READ LOCAL, or we
		are processing a stored procedure or function, or
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7091 7092 7093
		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
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7094
		INSERT INTO ... SELECT ... and the logical logging (MySQL
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7095
		binlog) requires the use of a locking read, or
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7096 7097 7098
		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
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7099 7100 7101 7102 7103 7104
		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. */
7105

7106 7107 7108 7109 7110
		ulint	isolation_level;

		isolation_level = trx->isolation_level;

		if ((srv_locks_unsafe_for_binlog
7111 7112 7113 7114
		     || isolation_level == TRX_ISO_READ_COMMITTED)
		    && isolation_level != TRX_ISO_SERIALIZABLE
		    && (lock_type == TL_READ || lock_type == TL_READ_NO_INSERT)
		    && (sql_command == SQLCOM_INSERT_SELECT
antony@ppcg5.local's avatar
antony@ppcg5.local committed
7115 7116
			|| sql_command == SQLCOM_UPDATE
			|| sql_command == SQLCOM_CREATE_TABLE)) {
7117

7118 7119 7120
			/* 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
7121
			is not set to serializable and MySQL is doing
7122 7123 7124 7125
			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. */
7126 7127 7128

			prebuilt->select_lock_type = LOCK_NONE;
			prebuilt->stored_select_lock_type = LOCK_NONE;
antony@ppcg5.local's avatar
antony@ppcg5.local committed
7129
		} else if (sql_command == SQLCOM_CHECKSUM) {
7130
			/* Use consistent read for checksum table */
7131

7132 7133 7134 7135 7136 7137
			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;
		}
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7138

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7139 7140
	} else if (lock_type != TL_IGNORE) {

7141
		/* We set possible LOCK_X value in external_lock, not yet
7142
		here even if this would be SELECT ... FOR UPDATE */
7143

7144
		prebuilt->select_lock_type = LOCK_NONE;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7145
		prebuilt->stored_select_lock_type = LOCK_NONE;
7146 7147 7148 7149
	}

	if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK) {

7150
		/* Starting from 5.0.7, we weaken also the table locks
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7151 7152
		set at the start of a MySQL stored procedure call, just like
		we weaken the locks set at the start of an SQL statement.
7153
		MySQL does set in_lock_tables TRUE there, but in reality
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7154 7155 7156 7157
		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). */

7158
		if (lock_type == TL_READ
antony@ppcg5.local's avatar
antony@ppcg5.local committed
7159
		    && sql_command == SQLCOM_LOCK_TABLES) {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7160 7161 7162 7163 7164 7165 7166
			/* 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
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7167 7168 7169
			equivalent to READ. This will change the InnoDB
			behavior in mysqldump, so that dumps of InnoDB tables
			are consistent with dumps of MyISAM tables. */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7170 7171 7172 7173

			lock_type = TL_READ_NO_INSERT;
		}

7174
		/* If we are not doing a LOCK TABLE, DISCARD/IMPORT
7175
		TABLESPACE or TRUNCATE TABLE then allow multiple
7176 7177
		writers. Note that ALTER TABLE uses a TL_WRITE_ALLOW_READ
		< TL_WRITE_CONCURRENT_INSERT.
7178

7179 7180
		We especially allow multiple writers if MySQL is at the
		start of a stored procedure call (SQLCOM_CALL) or a
7181
		stored function call (MySQL does have in_lock_tables
7182
		TRUE there). */
7183

7184
		if ((lock_type >= TL_WRITE_CONCURRENT_INSERT
7185 7186 7187 7188 7189 7190 7191
		     && lock_type <= TL_WRITE)
		    && !(in_lock_tables
			 && sql_command == SQLCOM_LOCK_TABLES)
		    && !thd_tablespace_op(thd)
		    && sql_command != SQLCOM_TRUNCATE
		    && sql_command != SQLCOM_OPTIMIZE
		    && sql_command != SQLCOM_CREATE_TABLE) {
7192 7193

			lock_type = TL_WRITE_ALLOW_WRITE;
7194
		}
7195

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7196 7197 7198 7199
		/* 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
7200 7201 7202 7203
		concurrent inserts to t2.

		We especially allow concurrent inserts if MySQL is at the
		start of a stored procedure call (SQLCOM_CALL)
antony@ppcg5.local's avatar
antony@ppcg5.local committed
7204
		(MySQL does have thd_in_lock_tables() TRUE there). */
7205

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7206
		if (lock_type == TL_READ_NO_INSERT
antony@ppcg5.local's avatar
antony@ppcg5.local committed
7207
		    && sql_command != SQLCOM_LOCK_TABLES) {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7208

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7209 7210
			lock_type = TL_READ;
		}
7211

7212 7213 7214 7215
		lock.type = lock_type;
	}

	*to++= &lock;
7216

7217 7218 7219
	return(to);
}

7220
/***********************************************************************
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7221 7222
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
7223
counter if it already has been initialized. In parameter ret returns
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7224
the value of the auto-inc counter. */
7225

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7226 7227 7228
int
ha_innobase::innobase_read_and_init_auto_inc(
/*=========================================*/
7229 7230
						/* out: 0 or generic MySQL
						error code */
7231
        ulonglong*	value)			/* out: the autoinc value */
7232
{
7233
	ulonglong	auto_inc;
7234
	ibool		stmt_start;
7235 7236 7237
	int		mysql_error = 0;
	dict_table_t*	innodb_table = prebuilt->table;
	ibool		trx_was_not_started	= FALSE;
7238

7239
	ut_a(prebuilt);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7240
	ut_a(prebuilt->table);
7241

7242 7243 7244 7245
	/* Remember if we are in the beginning of an SQL statement.
	This function must not change that flag. */
	stmt_start = prebuilt->sql_stat_start;

7246
	/* Prepare prebuilt->trx in the table handle */
7247
	update_thd(ha_thd());
7248

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7249 7250 7251 7252
	if (prebuilt->trx->conc_state == TRX_NOT_STARTED) {
		trx_was_not_started = TRUE;
	}

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7253 7254 7255 7256 7257
	/* 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);

7258
	dict_table_autoinc_lock(prebuilt->table);
7259

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7260
	auto_inc = dict_table_autoinc_read(prebuilt->table);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7261

7262 7263 7264 7265 7266
	/* Was the AUTOINC counter reset during normal processing, if
	so then we simply start count from 1. No need to go to the index.*/
	if (auto_inc == 0 && innodb_table->autoinc_inited) {
		++auto_inc;
		dict_table_autoinc_initialize(innodb_table, auto_inc);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7267
	}
7268

7269 7270
	if (auto_inc == 0) {
		dict_index_t* index;
7271
		ulint error;
7272
		const char* autoinc_col_name;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7273

7274
		ut_a(!innodb_table->autoinc_inited);
7275

7276
		index = innobase_get_index(table->s->next_number_index);
7277

7278
		autoinc_col_name = table->found_next_number_field->field_name;
7279

7280 7281
		error = row_search_max_autoinc(
			index, autoinc_col_name, &auto_inc);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7282

7283
		if (error == DB_SUCCESS) {
7284 7285 7286
			if (auto_inc < ~0x0ULL) {
				++auto_inc;
			}
7287
			dict_table_autoinc_initialize(innodb_table, auto_inc);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7288
		} else {
7289 7290
			ut_print_timestamp(stderr);
			fprintf(stderr, "  InnoDB: Error: (%lu) Couldn't read "
7291 7292
				"the max AUTOINC value from the index (%s).\n",
				error, index->name);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7293

7294
			mysql_error = 1;
7295 7296
		}
	}
7297

7298
	*value = auto_inc;
7299

7300
	dict_table_autoinc_unlock(prebuilt->table);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7301

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7302
	/* Since MySQL does not seem to call autocommit after SHOW TABLE
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7303
	STATUS (even if we would register the trx here), we commit our
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7304
	transaction here if it was started here. This is to eliminate a
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7305 7306 7307
	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. */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7308 7309 7310 7311 7312 7313

	if (trx_was_not_started) {

		innobase_commit_low(prebuilt->trx);
	}

7314 7315
	prebuilt->sql_stat_start = stmt_start;

7316 7317 7318 7319 7320 7321 7322 7323 7324
	return(mysql_error);
}

/*******************************************************************************
Read the next autoinc value, initialize the table if it's not initialized.
On return if there is no error then the tables AUTOINC lock is locked.*/

ulong
ha_innobase::innobase_get_auto_increment(
7325
/*=====================================*/
7326 7327
	ulonglong*	value)		/* out: autoinc value */
{
7328
	ulong		error;
7329

7330 7331 7332 7333 7334
	*value = 0;

	/* Note: If the table is not initialized when we attempt the
	read below. We initialize the table's auto-inc counter  and
	always do a reread of the AUTOINC value. */
7335 7336 7337 7338
	do {
		error = innobase_autoinc_lock();

		if (error == DB_SUCCESS) {
7339
			ulonglong	autoinc;
7340 7341 7342 7343 7344 7345

			/* Determine the first value of the interval */
			autoinc = dict_table_autoinc_read(prebuilt->table);

			/* We need to initialize the AUTO-INC value, for
			that we release all locks.*/
7346
			if (autoinc == 0) {
7347 7348 7349 7350 7351
				trx_t*		trx;

				trx = prebuilt->trx;
				dict_table_autoinc_unlock(prebuilt->table);

7352 7353 7354 7355
				/* If we had reserved the AUTO-INC
				lock in this SQL statement we release
				it before retrying.*/
				row_unlock_table_autoinc_for_mysql(trx);
7356 7357 7358 7359 7360 7361 7362 7363 7364

				/* Just to make sure */
				ut_a(!trx->auto_inc_lock);

				int	mysql_error;

				mysql_error = innobase_read_and_init_auto_inc(
					&autoinc);

7365
				if (mysql_error) {
7366 7367 7368
					error = DB_ERROR;
				}
			} else {
7369
				*value = autoinc;
7370
			}
7371 7372 7373 7374
		/* A deadlock error during normal processing is OK
		and can be ignored. */
		} else if (error != DB_DEADLOCK) {

7375
			sql_print_error("InnoDB: Error: %lu in "
7376 7377
					"::innobase_get_auto_increment()",
					error);
7378
		}
7379

7380 7381
	} while (*value == 0 && error == DB_SUCCESS);

7382
	return(error);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7383 7384
}

7385
/*******************************************************************************
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7386 7387 7388
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
7389 7390 7391
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)            */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7392

7393 7394
void
ha_innobase::get_auto_increment(
7395
/*============================*/
7396 7397 7398 7399 7400
        ulonglong	offset,              /* in: */
        ulonglong	increment,           /* in: table autoinc increment */
        ulonglong	nb_desired_values,   /* in: number of values reqd */
        ulonglong	*first_value,        /* out: the autoinc value */
        ulonglong	*nb_reserved_values) /* out: count of reserved values */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7401
{
7402
	trx_t*		trx;
7403 7404
	ulint		error;
	ulonglong	autoinc = 0;
7405

7406
	/* Prepare prebuilt->trx in the table handle */
7407
	update_thd(ha_thd());
7408

7409
	error = innobase_get_auto_increment(&autoinc);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7410

7411 7412
	if (error != DB_SUCCESS) {
		*first_value = (~(ulonglong) 0);
7413
		return;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7414
	}
7415

7416 7417 7418 7419 7420 7421
	/* This is a hack, since nb_desired_values seems to be accurate only
	for the first call to get_auto_increment() for multi-row INSERT and
	meaningless for other statements e.g, LOAD etc. Subsequent calls to
	this method for the same statement results in different values which
	don't make sense. Therefore we store the value the first time we are
	called and count down from that as rows are written (see write_row()).
7422
	*/
7423

7424
	trx = prebuilt->trx;
7425

7426 7427 7428 7429 7430
	/* Note: We can't rely on *first_value since some MySQL engines,
	in particular the partition engine, don't initialize it to 0 when
	invoking this method. So we are not sure if it's guaranteed to
	be 0 or not. */

7431 7432
	/* Called for the first time ? */
	if (trx->n_autoinc_rows == 0) {
7433

7434
		trx->n_autoinc_rows = (ulint) nb_desired_values;
7435

7436 7437 7438
		/* It's possible for nb_desired_values to be 0:
		e.g., INSERT INTO T1(C) SELECT C FROM T2; */
		if (nb_desired_values == 0) {
7439

7440
			trx->n_autoinc_rows = 1;
7441
		}
7442

antony@pcg5ppc.xiphis.org's avatar
antony@pcg5ppc.xiphis.org committed
7443
		set_if_bigger(*first_value, autoinc);
7444 7445
	/* Not in the middle of a mult-row INSERT. */
	} else if (prebuilt->last_value == 0) {
antony@pcg5ppc.xiphis.org's avatar
antony@pcg5ppc.xiphis.org committed
7446
		set_if_bigger(*first_value, autoinc);
7447
	}
7448

7449
	*nb_reserved_values = trx->n_autoinc_rows;
7450

7451 7452 7453
	/* With old style AUTOINC locking we only update the table's
	AUTOINC counter after attempting to insert the row. */
	if (innobase_autoinc_lock_mode != AUTOINC_OLD_STYLE_LOCKING) {
7454 7455 7456 7457 7458 7459 7460 7461 7462 7463
		ulonglong	have;
		ulonglong	need;

		/* Check for overflow conditions. */
		need = *nb_reserved_values * increment;
		have = ~0x0ULL - *first_value;

		if (have < need) {
			need = have;
		}
7464

7465
		/* Compute the last value in the interval */
7466
		prebuilt->last_value = *first_value + need;
7467

7468 7469 7470 7471 7472 7473 7474 7475 7476 7477
		ut_a(prebuilt->last_value >= *first_value);

		/* Update the table autoinc variable */
		dict_table_autoinc_update(
			prebuilt->table, prebuilt->last_value);
	} else {
		/* This will force write_row() into attempting an update
		of the table's AUTOINC counter. */
		prebuilt->last_value = 0;
	}
7478 7479 7480 7481 7482 7483 7484

	/* The increment to be used to increase the AUTOINC value, we use
	this in write_row() and update_row() to increase the autoinc counter
	for columns that are filled by the user.*/
	prebuilt->table->autoinc_increment = increment;

	dict_table_autoinc_unlock(prebuilt->table);
7485 7486
}

7487 7488
/* See comment in handler.h */
int
7489 7490 7491
ha_innobase::reset_auto_increment(
/*==============================*/
	ulonglong	value)		/* in: new value for table autoinc */
7492 7493 7494
{
	DBUG_ENTER("ha_innobase::reset_auto_increment");

7495
	int	error;
7496

7497
	update_thd(ha_thd());
7498

7499 7500 7501 7502 7503 7504
	error = row_lock_table_autoinc_for_mysql(prebuilt);

	if (error != DB_SUCCESS) {
		error = convert_error_code_to_mysql(error, user_thd);

		DBUG_RETURN(error);
7505
	}
7506

7507
	innobase_reset_autoinc(value);
7508 7509 7510 7511

	DBUG_RETURN(0);
}

7512 7513 7514 7515
/* See comment in handler.cc */
bool
ha_innobase::get_error_message(int error, String *buf)
{
7516
	trx_t*	trx = check_trx_exists(ha_thd());
7517 7518 7519 7520 7521 7522 7523

	buf->copy(trx->detailed_error, strlen(trx->detailed_error),
		system_charset_info);

	return FALSE;
}

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7524 7525 7526 7527
/***********************************************************************
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. */
monty@mysql.com's avatar
monty@mysql.com committed
7528

7529 7530
int
ha_innobase::cmp_ref(
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7531 7532 7533
/*=================*/
				/* out: < 0 if ref1 < ref2, 0 if equal, else
				> 0 */
7534
	const uchar*	ref1,	/* in: an (internal) primary key value in the
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7535
				MySQL key value format */
7536
	const uchar*	ref2)	/* in: an (internal) primary key value in the
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7537
				MySQL key value format */
7538 7539
{
	enum_field_types mysql_type;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7540 7541 7542 7543 7544
	Field*		field;
	KEY_PART_INFO*	key_part;
	KEY_PART_INFO*	key_part_end;
	uint		len1;
	uint		len2;
7545
	int		result;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7546 7547 7548 7549 7550 7551 7552 7553 7554 7555 7556 7557 7558 7559 7560

	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;

7561 7562 7563
	for (; key_part != key_part_end; ++key_part) {
		field = key_part->field;
		mysql_type = field->type();
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7564

7565 7566 7567 7568
		if (mysql_type == MYSQL_TYPE_TINY_BLOB
			|| mysql_type == MYSQL_TYPE_MEDIUM_BLOB
			|| mysql_type == MYSQL_TYPE_BLOB
			|| mysql_type == MYSQL_TYPE_LONG_BLOB) {
7569

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7570 7571 7572 7573 7574 7575
			/* 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);

7576 7577
			ref1 += 2;
			ref2 += 2;
7578 7579
			result = ((Field_blob*)field)->cmp( ref1, len1,
                                                            ref2, len2);
7580
		} else {
7581
			result = field->key_cmp(ref1, ref2);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7582 7583 7584 7585 7586
		}

		if (result) {

			return(result);
7587 7588
		}

7589 7590
		ref1 += key_part->store_length;
		ref2 += key_part->store_length;
7591
	}
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7592 7593

	return(0);
7594 7595
}

7596 7597 7598 7599 7600 7601 7602 7603 7604 7605 7606 7607 7608 7609 7610 7611 7612 7613 7614 7615 7616 7617 7618 7619 7620 7621 7622
/***********************************************************************
Ask InnoDB if a query to a table can be cached. */

my_bool
ha_innobase::register_query_cache_table(
/*====================================*/
					/* out: TRUE if query caching
					of the table is permitted */
	THD*		thd,		/* in: user thread handle */
	char*		table_key,	/* in: concatenation of database name,
					the null character '\0',
					and the table name */
	uint		key_length,	/* in: length of the full name, i.e.
					len(dbname) + len(tablename) + 1 */
	qc_engine_callback*
			call_back,	/* out: pointer to function for
					checking if query caching
					is permitted */
	ulonglong	*engine_data)	/* in/out: data to call_back */
{
	*call_back = innobase_query_caching_of_table_permitted;
	*engine_data = 0;
	return(innobase_query_caching_of_table_permitted(thd, table_key,
							 key_length,
							 engine_data));
}

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7623 7624
char*
ha_innobase::get_mysql_bin_log_name()
guilhem@mysql.com's avatar
guilhem@mysql.com committed
7625
{
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7626
	return(trx_sys_mysql_bin_log_name);
guilhem@mysql.com's avatar
guilhem@mysql.com committed
7627 7628
}

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7629 7630
ulonglong
ha_innobase::get_mysql_bin_log_pos()
guilhem@mysql.com's avatar
guilhem@mysql.com committed
7631
{
7632
	/* trx... is ib_longlong, which is a typedef for a 64-bit integer
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7633 7634
	(__int64 or longlong) so it's ok to cast it to ulonglong. */

7635
	return(trx_sys_mysql_bin_log_pos);
guilhem@mysql.com's avatar
guilhem@mysql.com committed
7636 7637
}

7638
/**********************************************************************
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7639 7640 7641 7642 7643 7644 7645
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! */
7646
extern "C"
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7647 7648 7649 7650 7651
ulint
innobase_get_at_most_n_mbchars(
/*===========================*/
				/* out: number of bytes occupied by the first
				n characters */
7652
	ulint charset_id,	/* in: character set id */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7653 7654 7655
	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) */
7656
	ulint data_len,		/* in: length of the string in bytes */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7657
	const char* str)	/* in: character string */
7658
{
7659
	ulint char_length;	/* character length in bytes */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7660
	ulint n_chars;		/* number of characters in prefix */
7661
	CHARSET_INFO* charset;	/* charset used in the field */
7662

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7663
	charset = get_charset((uint) charset_id, MYF(MY_WME));
7664

7665 7666
	ut_ad(charset);
	ut_ad(charset->mbmaxlen);
7667

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7668
	/* Calculate how many characters at most the prefix index contains */
7669

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7670
	n_chars = prefix_len / charset->mbmaxlen;
7671

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7672 7673 7674
	/* 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
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7675
	character. */
7676

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7677 7678
	if (charset->mbmaxlen > 1) {
		/* my_charpos() returns the byte length of the first n_chars
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7679 7680 7681 7682 7683 7684 7685 7686 7687 7688 7689 7690 7691 7692 7693 7694
		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. */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7695

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7696
		char_length = my_charpos(charset, str,
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7697
						str + data_len, (int) n_chars);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7698 7699
		if (char_length > data_len) {
			char_length = data_len;
7700
		}
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7701
	} else {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7702 7703 7704 7705 7706
		if (data_len < prefix_len) {
			char_length = data_len;
		} else {
			char_length = prefix_len;
		}
7707
	}
7708

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7709
	return(char_length);
7710
}
guilhem@mysql.com's avatar
guilhem@mysql.com committed
7711

7712 7713
/***********************************************************************
This function is used to prepare X/Open XA distributed transaction   */
7714
static
7715
int
7716 7717
innobase_xa_prepare(
/*================*/
7718
			/* out: 0 or error number */
7719
        handlerton *hton,
7720 7721 7722 7723 7724 7725
	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;
7726
	trx_t* trx = check_trx_exists(thd);
serg@serg.mylan's avatar
serg@serg.mylan committed
7727

antony@ppcg5.local's avatar
antony@ppcg5.local committed
7728 7729
	if (thd_sql_command(thd) != SQLCOM_XA_PREPARE &&
	    (all || !thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))
7730
	{
serg@serg.mylan's avatar
serg@serg.mylan committed
7731

7732 7733
		/* For ibbackup to work the order of transactions in binlog
		and InnoDB must be the same. Consider the situation
serg@serg.mylan's avatar
serg@serg.mylan committed
7734

7735 7736 7737 7738
		  thread1> prepare; write to binlog; ...
			  <context switch>
		  thread2> prepare; write to binlog; commit
		  thread1>			     ... commit
serg@serg.mylan's avatar
serg@serg.mylan committed
7739

7740 7741
		To ensure this will not happen we're taking the mutex on
		prepare, and releasing it on commit.
serg@serg.mylan's avatar
serg@serg.mylan committed
7742

7743 7744 7745 7746 7747 7748 7749 7750 7751 7752 7753
		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;
	}
7754

antony@ppcg5.local's avatar
antony@ppcg5.local committed
7755
	if (!THDVAR(thd, support_xa)) {
7756 7757 7758 7759

		return(0);
	}

7760
	thd_get_xid(thd, (MYSQL_XID*) &trx->xid);
7761 7762 7763 7764 7765 7766 7767 7768 7769

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

7770 7771
	  sql_print_error("trx->active_trans == 0, but trx->conc_state != "
			  "TRX_NOT_STARTED");
7772 7773
	}

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7774
	if (all
antony@ppcg5.local's avatar
antony@ppcg5.local committed
7775
		|| (!thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) {
serg@serg.mylan's avatar
serg@serg.mylan committed
7776

7777 7778
		/* We were instructed to prepare the whole transaction, or
		this is an SQL statement end and autocommit is on */
7779

7780
		ut_ad(trx->active_trans);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7781

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7782
		error = (int) trx_prepare_for_mysql(trx);
7783
	} else {
7784
		/* We just mark the SQL statement ended and do not do a
7785 7786
		transaction prepare */

7787 7788
		/* If we had reserved the auto-inc lock for some
		table in this SQL statement we release it now */
7789

7790
		row_unlock_table_autoinc_for_mysql(trx);
7791

7792 7793 7794 7795 7796 7797 7798 7799 7800 7801 7802 7803
		/* 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();

7804
	return error;
7805 7806 7807 7808
}

/***********************************************************************
This function is used to recover X/Open XA distributed transactions   */
7809
static
7810
int
7811 7812
innobase_xa_recover(
/*================*/
7813
				/* out: number of prepared transactions
7814
				stored in xid_list */
7815
        handlerton *hton,
7816
	XID*	xid_list,	/* in/out: prepared transactions */
7817 7818 7819
	uint	len)		/* in: number of slots in xid_list */
{
	if (len == 0 || xid_list == NULL) {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7820 7821

		return(0);
7822 7823
	}

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7824
	return(trx_recover_for_mysql(xid_list, len));
7825 7826 7827 7828 7829
}

/***********************************************************************
This function is used to commit one X/Open XA distributed transaction
which is in the prepared state */
7830
static
7831
int
7832 7833
innobase_commit_by_xid(
/*===================*/
7834
			/* out: 0 or error number */
7835
        handlerton *hton,
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7836
	XID*	xid)	/* in: X/Open XA transaction identification */
7837 7838 7839 7840 7841 7842 7843
{
	trx_t*	trx;

	trx = trx_get_trx_by_xid(xid);

	if (trx) {
		innobase_commit_low(trx);
7844

7845 7846 7847 7848 7849 7850 7851 7852 7853
		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 */
7854
static
7855
int
7856 7857
innobase_rollback_by_xid(
/*=====================*/
7858
			/* out: 0 or error number */
7859
        handlerton *hton,
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7860
	XID	*xid)	/* in: X/Open XA transaction identification */
7861 7862 7863 7864 7865 7866 7867 7868 7869 7870 7871 7872
{
	trx_t*	trx;

	trx = trx_get_trx_by_xid(xid);

	if (trx) {
		return(innobase_rollback_trx(trx));
	} else {
		return(XAER_NOTA);
	}
}

7873
/***********************************************************************
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7874 7875
Create a consistent view for a cursor based on current transaction
which is created if the corresponding MySQL thread still lacks one.
7876
This consistent view is then used inside of MySQL when accessing records
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7877
using a cursor. */
7878
static
7879
void*
7880
innobase_create_cursor_view(
7881
/*========================*/
7882 7883 7884
                          /* out: pointer to cursor view or NULL */
        handlerton *hton, /* in: innobase hton */
	THD* thd)	  /* in: user thread handle */
7885
{
7886
	return(read_cursor_view_create_for_mysql(check_trx_exists(thd)));
7887 7888 7889
}

/***********************************************************************
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7890
Close the given consistent cursor view of a transaction and restore
7891
global read view to a transaction read view. Transaction is created if the
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7892
corresponding MySQL thread still lacks one. */
7893
static
7894 7895
void
innobase_close_cursor_view(
7896
/*=======================*/
7897
        handlerton *hton,
7898
	THD*	thd,	/* in: user thread handle */
7899 7900
	void*	curview)/* in: Consistent read view to be closed */
{
7901 7902
	read_cursor_view_close_for_mysql(check_trx_exists(thd),
					 (cursor_view_t*) curview);
7903 7904 7905
}

/***********************************************************************
7906 7907
Set the given consistent cursor view to a transaction which is created
if the corresponding MySQL thread still lacks one. If the given
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7908 7909
consistent cursor view is NULL global read view of a transaction is
restored to a transaction read view. */
7910
static
7911 7912 7913
void
innobase_set_cursor_view(
/*=====================*/
7914
        handlerton *hton,
7915
	THD*	thd,	/* in: user thread handle */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
7916
	void*	curview)/* in: Consistent cursor view to be set */
7917
{
7918 7919
	read_cursor_set_for_mysql(check_trx_exists(thd),
				  (cursor_view_t*) curview);
7920 7921
}

7922

7923 7924 7925
bool ha_innobase::check_if_incompatible_data(
	HA_CREATE_INFO*	info,
	uint		table_changes)
7926
{
7927
	if (table_changes != IS_EQUAL_YES) {
7928

7929 7930 7931 7932 7933 7934
		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) {
7935

7936 7937 7938 7939
		return COMPATIBLE_DATA_NO;
	}

	/* Check that row format didn't change */
7940
	if ((info->used_fields & HA_CREATE_USED_ROW_FORMAT) &&
7941 7942 7943 7944 7945 7946 7947
		get_row_type() != info->row_type) {

		return COMPATIBLE_DATA_NO;
	}

	return COMPATIBLE_DATA_YES;
}
acurtis@xiphis.org's avatar
acurtis@xiphis.org committed
7948

brian@zim.(none)'s avatar
brian@zim.(none) committed
7949 7950 7951 7952 7953 7954 7955 7956
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;
}

7957
static SHOW_VAR innodb_status_variables_export[]= {
brian@zim.(none)'s avatar
brian@zim.(none) committed
7958 7959 7960 7961
  {"Innodb",                   (char*) &show_innodb_vars, SHOW_FUNC},
  {NullS, NullS, SHOW_LONG}
};

7962
static struct st_mysql_storage_engine innobase_storage_engine=
7963
{ MYSQL_HANDLERTON_INTERFACE_VERSION };
acurtis@xiphis.org's avatar
acurtis@xiphis.org committed
7964

antony@ppcg5.local's avatar
antony@ppcg5.local committed
7965 7966 7967 7968 7969 7970 7971 7972 7973 7974 7975 7976 7977 7978 7979 7980 7981 7982 7983 7984 7985 7986 7987 7988 7989 7990
/* plugin options */
static MYSQL_SYSVAR_BOOL(checksums, innobase_use_checksums,
  PLUGIN_VAR_NOCMDARG | PLUGIN_VAR_READONLY,
  "Enable InnoDB checksums validation (enabled by default). "
  "Disable with --skip-innodb-checksums.",
  NULL, NULL, TRUE);

static MYSQL_SYSVAR_STR(data_home_dir, innobase_data_home_dir,
  PLUGIN_VAR_READONLY,
  "The common part for InnoDB table spaces.",
  NULL, NULL, NULL);

static MYSQL_SYSVAR_BOOL(doublewrite, innobase_use_doublewrite,
  PLUGIN_VAR_NOCMDARG | PLUGIN_VAR_READONLY,
  "Enable InnoDB doublewrite buffer (enabled by default). "
  "Disable with --skip-innodb-doublewrite.",
  NULL, NULL, TRUE);

static MYSQL_SYSVAR_ULONG(fast_shutdown, innobase_fast_shutdown,
  PLUGIN_VAR_OPCMDARG,
  "Speeds up the shutdown process of the InnoDB storage engine. Possible "
  "values are 0, 1 (faster)"
  /*
    NetWare can't close unclosed files, can't automatically kill remaining
    threads, etc, so on this OS we disable the crash-like InnoDB shutdown.
  */
7991
  IF_NETWARE("", " or 2 (fastest - crash-like)")
antony@ppcg5.local's avatar
antony@ppcg5.local committed
7992 7993 7994 7995 7996 7997 7998 7999 8000 8001
  ".",
  NULL, NULL, 1, 0, IF_NETWARE(1,2), 0);

static MYSQL_SYSVAR_BOOL(file_per_table, innobase_file_per_table,
  PLUGIN_VAR_NOCMDARG | PLUGIN_VAR_READONLY,
  "Stores each InnoDB table to an .ibd file in the database dir.",
  NULL, NULL, FALSE);

static MYSQL_SYSVAR_ULONG(flush_log_at_trx_commit, srv_flush_log_at_trx_commit,
  PLUGIN_VAR_OPCMDARG,
8002 8003 8004
  "Set to 0 (write and flush once per second),"
  " 1 (write and flush at each commit)"
  " or 2 (write at commit, flush once per second).",
antony@ppcg5.local's avatar
antony@ppcg5.local committed
8005 8006 8007 8008 8009 8010 8011 8012 8013 8014 8015
  NULL, NULL, 1, 0, 2, 0);

static MYSQL_SYSVAR_STR(flush_method, innobase_unix_file_flush_method,
  PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
  "With which method to flush data.", NULL, NULL, NULL);

static MYSQL_SYSVAR_BOOL(locks_unsafe_for_binlog, innobase_locks_unsafe_for_binlog,
  PLUGIN_VAR_NOCMDARG | PLUGIN_VAR_READONLY,
  "Force InnoDB to not use next-key locking, to use only row-level locking.",
  NULL, NULL, FALSE);

8016
#ifdef UNIV_LOG_ARCHIVE
antony@ppcg5.local's avatar
antony@ppcg5.local committed
8017 8018 8019 8020 8021 8022 8023
static MYSQL_SYSVAR_STR(log_arch_dir, innobase_log_arch_dir,
  PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
  "Where full logs should be archived.", NULL, NULL, NULL);

static MYSQL_SYSVAR_BOOL(log_archive, innobase_log_archive,
  PLUGIN_VAR_OPCMDARG | PLUGIN_VAR_READONLY,
  "Set to 1 if you want to have logs archived.", NULL, NULL, FALSE);
antony@ppcg5.local's avatar
antony@ppcg5.local committed
8024
#endif /* UNIV_LOG_ARCHIVE */
antony@ppcg5.local's avatar
antony@ppcg5.local committed
8025 8026 8027 8028 8029 8030 8031 8032 8033 8034 8035 8036 8037 8038 8039 8040 8041 8042 8043 8044 8045 8046 8047 8048 8049

static MYSQL_SYSVAR_STR(log_group_home_dir, innobase_log_group_home_dir,
  PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
  "Path to InnoDB log files.", NULL, NULL, NULL);

static MYSQL_SYSVAR_ULONG(max_dirty_pages_pct, srv_max_buf_pool_modified_pct,
  PLUGIN_VAR_RQCMDARG,
  "Percentage of dirty pages allowed in bufferpool.",
  NULL, NULL, 90, 0, 100, 0);

static MYSQL_SYSVAR_ULONG(max_purge_lag, srv_max_purge_lag,
  PLUGIN_VAR_RQCMDARG,
  "Desired maximum length of the purge queue (0 = no limit)",
  NULL, NULL, 0, 0, ~0L, 0);

static MYSQL_SYSVAR_BOOL(rollback_on_timeout, innobase_rollback_on_timeout,
  PLUGIN_VAR_OPCMDARG | PLUGIN_VAR_READONLY,
  "Roll back the complete transaction on lock wait timeout, for 4.x compatibility (disabled by default)",
  NULL, NULL, FALSE);

static MYSQL_SYSVAR_BOOL(status_file, innobase_create_status_file,
  PLUGIN_VAR_OPCMDARG | PLUGIN_VAR_NOSYSVAR,
  "Enable SHOW INNODB STATUS output in the innodb_status.<pid> file",
  NULL, NULL, FALSE);

8050 8051 8052 8053 8054
static MYSQL_SYSVAR_BOOL(stats_on_metadata, innobase_stats_on_metadata,
  PLUGIN_VAR_OPCMDARG | PLUGIN_VAR_NOSYSVAR,
  "Enable statistics gathering for metadata commands such as SHOW TABLE STATUS (on by default)",
  NULL, NULL, TRUE);

8055
static MYSQL_SYSVAR_BOOL(adaptive_hash_index, innobase_adaptive_hash_index,
8056
  PLUGIN_VAR_OPCMDARG | PLUGIN_VAR_READONLY,
8057 8058
  "Enable InnoDB adaptive hash index (enabled by default).  "
  "Disable with --skip-innodb-adaptive-hash-index.",
8059 8060
  NULL, NULL, TRUE);

antony@ppcg5.local's avatar
antony@ppcg5.local committed
8061 8062 8063 8064 8065 8066 8067 8068 8069 8070 8071 8072 8073 8074 8075 8076 8077 8078 8079 8080 8081 8082 8083 8084 8085 8086 8087 8088 8089 8090 8091 8092 8093 8094 8095 8096 8097 8098 8099 8100 8101 8102 8103 8104 8105 8106 8107 8108 8109 8110 8111 8112 8113 8114 8115 8116 8117 8118 8119 8120 8121 8122 8123 8124 8125 8126 8127 8128 8129 8130 8131 8132 8133 8134 8135 8136 8137 8138 8139 8140 8141 8142 8143 8144 8145
static MYSQL_SYSVAR_LONG(additional_mem_pool_size, innobase_additional_mem_pool_size,
  PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
  "Size of a memory pool InnoDB uses to store data dictionary information and other internal data structures.",
  NULL, NULL, 1*1024*1024L, 512*1024L, ~0L, 1024);

static MYSQL_SYSVAR_ULONG(autoextend_increment, srv_auto_extend_increment,
  PLUGIN_VAR_RQCMDARG,
  "Data file autoextend increment in megabytes",
  NULL, NULL, 8L, 1L, 1000L, 0);

static MYSQL_SYSVAR_LONGLONG(buffer_pool_size, innobase_buffer_pool_size,
  PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
  "The size of the memory buffer InnoDB uses to cache data and indexes of its tables.",
  NULL, NULL, 8*1024*1024L, 1024*1024L, LONGLONG_MAX, 1024*1024L);

static MYSQL_SYSVAR_ULONG(commit_concurrency, srv_commit_concurrency,
  PLUGIN_VAR_RQCMDARG,
  "Helps in performance tuning in heavily concurrent environments.",
  NULL, NULL, 0, 0, 1000, 0);

static MYSQL_SYSVAR_ULONG(concurrency_tickets, srv_n_free_tickets_to_enter,
  PLUGIN_VAR_RQCMDARG,
  "Number of times a thread is allowed to enter InnoDB within the same SQL query after it has once got the ticket",
  NULL, NULL, 500L, 1L, ~0L, 0);

static MYSQL_SYSVAR_LONG(file_io_threads, innobase_file_io_threads,
  PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
  "Number of file I/O threads in InnoDB.",
  NULL, NULL, 4, 4, 64, 0);

static MYSQL_SYSVAR_LONG(force_recovery, innobase_force_recovery,
  PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
  "Helps to save your data in case the disk image of the database becomes corrupt.",
  NULL, NULL, 0, 0, 6, 0);

static MYSQL_SYSVAR_LONG(lock_wait_timeout, innobase_lock_wait_timeout,
  PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
  "Timeout in seconds an InnoDB transaction may wait for a lock before being rolled back.",
  NULL, NULL, 50, 1, 1024 * 1024 * 1024, 0);

static MYSQL_SYSVAR_LONG(log_buffer_size, innobase_log_buffer_size,
  PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
  "The size of the buffer which InnoDB uses to write log to the log files on disk.",
  NULL, NULL, 1024*1024L, 256*1024L, ~0L, 1024);

static MYSQL_SYSVAR_LONGLONG(log_file_size, innobase_log_file_size,
  PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
  "Size of each log file in a log group.",
  NULL, NULL, 5*1024*1024L, 1*1024*1024L, LONGLONG_MAX, 1024*1024L);

static MYSQL_SYSVAR_LONG(log_files_in_group, innobase_log_files_in_group,
  PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
  "Number of log files in the log group. InnoDB writes to the files in a circular fashion. Value 3 is recommended here.",
  NULL, NULL, 2, 2, 100, 0);

static MYSQL_SYSVAR_LONG(mirrored_log_groups, innobase_mirrored_log_groups,
  PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
  "Number of identical copies of log groups we keep for the database. Currently this should be set to 1.",
  NULL, NULL, 1, 1, 10, 0);

static MYSQL_SYSVAR_LONG(open_files, innobase_open_files,
  PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
  "How many files at the maximum InnoDB keeps open at the same time.",
  NULL, NULL, 300L, 10L, ~0L, 0);

static MYSQL_SYSVAR_ULONG(sync_spin_loops, srv_n_spin_wait_rounds,
  PLUGIN_VAR_RQCMDARG,
  "Count of spin-loop rounds in InnoDB mutexes",
  NULL, NULL, 20L, 0L, ~0L, 0);

static MYSQL_SYSVAR_ULONG(thread_concurrency, srv_thread_concurrency,
  PLUGIN_VAR_RQCMDARG,
  "Helps in performance tuning in heavily concurrent environments. Sets the maximum number of threads allowed inside InnoDB. Value 0 will disable the thread throttling.",
  NULL, NULL, 8, 0, 1000, 0);

static MYSQL_SYSVAR_ULONG(thread_sleep_delay, srv_thread_sleep_delay,
  PLUGIN_VAR_RQCMDARG,
  "Time of innodb thread sleeping before joining InnoDB queue (usec). Value 0 disable a sleep",
  NULL, NULL, 10000L, 0L, ~0L, 0);

static MYSQL_SYSVAR_STR(data_file_path, innobase_data_file_path,
  PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
  "Path to individual files and their sizes.",
  NULL, NULL, NULL);

8146
static MYSQL_SYSVAR_LONG(autoinc_lock_mode, innobase_autoinc_lock_mode,
8147
  PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
8148 8149 8150 8151 8152
  "The AUTOINC lock modes supported by InnoDB:               "
  "0 => Old style AUTOINC locking (for backward"
  " compatibility)                                           "
  "1 => New style AUTOINC locking                            "
  "2 => No AUTOINC locking (unsafe for SBR)",
8153 8154 8155 8156 8157
  NULL, NULL,
  AUTOINC_NEW_STYLE_LOCKING,	/* Default setting */
  AUTOINC_OLD_STYLE_LOCKING,	/* Minimum value */
  AUTOINC_NO_LOCKING, 0);	/* Maximum value */

antony@ppcg5.local's avatar
antony@ppcg5.local committed
8158 8159 8160 8161 8162 8163 8164 8165 8166 8167 8168 8169 8170 8171 8172 8173 8174 8175
static struct st_mysql_sys_var* innobase_system_variables[]= {
  MYSQL_SYSVAR(additional_mem_pool_size),
  MYSQL_SYSVAR(autoextend_increment),
  MYSQL_SYSVAR(buffer_pool_size),
  MYSQL_SYSVAR(checksums),
  MYSQL_SYSVAR(commit_concurrency),
  MYSQL_SYSVAR(concurrency_tickets),
  MYSQL_SYSVAR(data_file_path),
  MYSQL_SYSVAR(data_home_dir),
  MYSQL_SYSVAR(doublewrite),
  MYSQL_SYSVAR(fast_shutdown),
  MYSQL_SYSVAR(file_io_threads),
  MYSQL_SYSVAR(file_per_table),
  MYSQL_SYSVAR(flush_log_at_trx_commit),
  MYSQL_SYSVAR(flush_method),
  MYSQL_SYSVAR(force_recovery),
  MYSQL_SYSVAR(locks_unsafe_for_binlog),
  MYSQL_SYSVAR(lock_wait_timeout),
antony@ppcg5.local's avatar
antony@ppcg5.local committed
8176
#ifdef UNIV_LOG_ARCHIVE
8177
  MYSQL_SYSVAR(log_arch_dir),
antony@ppcg5.local's avatar
antony@ppcg5.local committed
8178
  MYSQL_SYSVAR(log_archive),
antony@ppcg5.local's avatar
antony@ppcg5.local committed
8179
#endif /* UNIV_LOG_ARCHIVE */
antony@ppcg5.local's avatar
antony@ppcg5.local committed
8180 8181 8182 8183 8184 8185 8186 8187 8188
  MYSQL_SYSVAR(log_buffer_size),
  MYSQL_SYSVAR(log_file_size),
  MYSQL_SYSVAR(log_files_in_group),
  MYSQL_SYSVAR(log_group_home_dir),
  MYSQL_SYSVAR(max_dirty_pages_pct),
  MYSQL_SYSVAR(max_purge_lag),
  MYSQL_SYSVAR(mirrored_log_groups),
  MYSQL_SYSVAR(open_files),
  MYSQL_SYSVAR(rollback_on_timeout),
8189
  MYSQL_SYSVAR(stats_on_metadata),
8190
  MYSQL_SYSVAR(adaptive_hash_index),
antony@ppcg5.local's avatar
antony@ppcg5.local committed
8191 8192 8193 8194 8195 8196
  MYSQL_SYSVAR(status_file),
  MYSQL_SYSVAR(support_xa),
  MYSQL_SYSVAR(sync_spin_loops),
  MYSQL_SYSVAR(table_locks),
  MYSQL_SYSVAR(thread_concurrency),
  MYSQL_SYSVAR(thread_sleep_delay),
8197
  MYSQL_SYSVAR(autoinc_lock_mode),
antony@ppcg5.local's avatar
antony@ppcg5.local committed
8198 8199 8200
  NULL
};

acurtis@xiphis.org's avatar
acurtis@xiphis.org committed
8201 8202 8203
mysql_declare_plugin(innobase)
{
  MYSQL_STORAGE_ENGINE_PLUGIN,
8204
  &innobase_storage_engine,
8205
  innobase_hton_name,
acurtis@xiphis.org's avatar
acurtis@xiphis.org committed
8206
  "Innobase OY",
8207
  "Supports transactions, row-level locking, and foreign keys",
8208
  PLUGIN_LICENSE_GPL,
8209
  innobase_init, /* Plugin Init */
acurtis@xiphis.org's avatar
acurtis@xiphis.org committed
8210 8211
  NULL, /* Plugin Deinit */
  0x0100 /* 1.0 */,
8212
  innodb_status_variables_export,/* status variables             */
antony@ppcg5.local's avatar
antony@ppcg5.local committed
8213 8214
  innobase_system_variables, /* system variables */
  NULL /* reserved */
acurtis@xiphis.org's avatar
acurtis@xiphis.org committed
8215 8216
}
mysql_declare_plugin_end;