2004-05-06 15:53:01 +02:00
|
|
|
/* Copyright (C) 2000-2004 MySQL AB
|
2000-07-31 21:29:14 +02:00
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|
|
|
|
|
|
|
/* drop and alter of tables */
|
|
|
|
|
|
|
|
#include "mysql_priv.h"
|
2002-09-19 16:49:41 +02:00
|
|
|
#include <hash.h>
|
2000-07-31 21:29:14 +02:00
|
|
|
#include <myisam.h>
|
2003-04-27 21:12:08 +02:00
|
|
|
#include <my_dir.h>
|
2005-07-19 18:06:49 +02:00
|
|
|
#include "sp_head.h"
|
|
|
|
#include "sql_trigger.h"
|
2005-12-22 06:39:02 +01:00
|
|
|
#include "sql_show.h"
|
2000-07-31 21:29:14 +02:00
|
|
|
|
|
|
|
#ifdef __WIN__
|
|
|
|
#include <io.h>
|
|
|
|
#endif
|
|
|
|
|
2006-02-13 08:49:28 +01:00
|
|
|
int creating_table= 0; // How many mysql_create_table are running
|
|
|
|
|
2004-09-07 16:56:31 +02:00
|
|
|
const char *primary_key_name="PRIMARY";
|
2000-07-31 21:29:14 +02:00
|
|
|
|
|
|
|
static bool check_if_keyname_exists(const char *name,KEY *start, KEY *end);
|
|
|
|
static char *make_unique_key_name(const char *field_name,KEY *start,KEY *end);
|
|
|
|
static int copy_data_between_tables(TABLE *from,TABLE *to,
|
2006-07-02 00:12:53 +02:00
|
|
|
List<create_field> &create, bool ignore,
|
2004-04-08 16:56:45 +02:00
|
|
|
uint order_num, ORDER *order,
|
2000-08-07 23:28:56 +02:00
|
|
|
ha_rows *copied,ha_rows *deleted);
|
2005-05-06 10:39:30 +02:00
|
|
|
static bool prepare_blob_field(THD *thd, create_field *sql_field);
|
2005-06-17 23:14:44 +02:00
|
|
|
static bool check_engine(THD *thd, const char *table_name,
|
2006-02-17 17:12:35 +01:00
|
|
|
HA_CREATE_INFO *create_info);
|
2006-01-17 08:40:00 +01:00
|
|
|
static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
|
|
|
|
List<create_field> *fields,
|
|
|
|
List<Key> *keys, bool tmp_table,
|
|
|
|
uint *db_options,
|
|
|
|
handler *file, KEY **key_info_buffer,
|
|
|
|
uint *key_count, int select_field_count);
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2005-12-31 09:34:39 +01:00
|
|
|
#define MYSQL50_TABLE_NAME_PREFIX "#mysql50#"
|
|
|
|
#define MYSQL50_TABLE_NAME_PREFIX_LENGTH 9
|
|
|
|
|
|
|
|
uint filename_to_tablename(const char *from, char *to, uint to_length)
|
|
|
|
{
|
|
|
|
uint errors, res= strconvert(&my_charset_filename, from,
|
|
|
|
system_charset_info, to, to_length, &errors);
|
|
|
|
if (errors) // Old 5.0 name
|
|
|
|
{
|
|
|
|
res= strxnmov(to, to_length, MYSQL50_TABLE_NAME_PREFIX, from, NullS) - to;
|
|
|
|
sql_print_error("Invalid (old?) table or database name '%s'", from);
|
|
|
|
/*
|
|
|
|
TODO: add a stored procedure for fix table and database names,
|
|
|
|
and mention its name in error log.
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
uint tablename_to_filename(const char *from, char *to, uint to_length)
|
|
|
|
{
|
2006-04-11 15:16:14 +02:00
|
|
|
uint errors, length;
|
2006-01-12 08:41:57 +01:00
|
|
|
if (from[0] == '#' && !strncmp(from, MYSQL50_TABLE_NAME_PREFIX,
|
|
|
|
MYSQL50_TABLE_NAME_PREFIX_LENGTH))
|
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes
Changes that requires code changes in other code of other storage engines.
(Note that all changes are very straightforward and one should find all issues
by compiling a --debug build and fixing all compiler errors and all
asserts in field.cc while running the test suite),
- New optional handler function introduced: reset()
This is called after every DML statement to make it easy for a handler to
statement specific cleanups.
(The only case it's not called is if force the file to be closed)
- handler::extra(HA_EXTRA_RESET) is removed. Code that was there before
should be moved to handler::reset()
- table->read_set contains a bitmap over all columns that are needed
in the query. read_row() and similar functions only needs to read these
columns
- table->write_set contains a bitmap over all columns that will be updated
in the query. write_row() and update_row() only needs to update these
columns.
The above bitmaps should now be up to date in all context
(including ALTER TABLE, filesort()).
The handler is informed of any changes to the bitmap after
fix_fields() by calling the virtual function
handler::column_bitmaps_signal(). If the handler does caching of
these bitmaps (instead of using table->read_set, table->write_set),
it should redo the caching in this code. as the signal() may be sent
several times, it's probably best to set a variable in the signal
and redo the caching on read_row() / write_row() if the variable was
set.
- Removed the read_set and write_set bitmap objects from the handler class
- Removed all column bit handling functions from the handler class.
(Now one instead uses the normal bitmap functions in my_bitmap.c instead
of handler dedicated bitmap functions)
- field->query_id is removed. One should instead instead check
table->read_set and table->write_set if a field is used in the query.
- handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and
handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now
instead use table->read_set to check for which columns to retrieve.
- If a handler needs to call Field->val() or Field->store() on columns
that are not used in the query, one should install a temporary
all-columns-used map while doing so. For this, we provide the following
functions:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
field->val();
dbug_tmp_restore_column_map(table->read_set, old_map);
and similar for the write map:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
field->val();
dbug_tmp_restore_column_map(table->write_set, old_map);
If this is not done, you will sooner or later hit a DBUG_ASSERT
in the field store() / val() functions.
(For not DBUG binaries, the dbug_tmp_restore_column_map() and
dbug_tmp_restore_column_map() are inline dummy functions and should
be optimized away be the compiler).
- If one needs to temporary set the column map for all binaries (and not
just to avoid the DBUG_ASSERT() in the Field::store() / Field::val()
methods) one should use the functions tmp_use_all_columns() and
tmp_restore_column_map() instead of the above dbug_ variants.
- All 'status' fields in the handler base class (like records,
data_file_length etc) are now stored in a 'stats' struct. This makes
it easier to know what status variables are provided by the base
handler. This requires some trivial variable names in the extra()
function.
- New virtual function handler::records(). This is called to optimize
COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true.
(stats.records is not supposed to be an exact value. It's only has to
be 'reasonable enough' for the optimizer to be able to choose a good
optimization path).
- Non virtual handler::init() function added for caching of virtual
constants from engine.
- Removed has_transactions() virtual method. Now one should instead return
HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support
transactions.
- The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument
that is to be used with 'new handler_name()' to allocate the handler
in the right area. The xxxx_create_handler() function is also
responsible for any initialization of the object before returning.
For example, one should change:
static handler *myisam_create_handler(TABLE_SHARE *table)
{
return new ha_myisam(table);
}
->
static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root)
{
return new (mem_root) ha_myisam(table);
}
- New optional virtual function: use_hidden_primary_key().
This is called in case of an update/delete when
(table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
but we don't have a primary key. This allows the handler to take precisions
in remembering any hidden primary key to able to update/delete any
found row. The default handler marks all columns to be read.
- handler::table_flags() now returns a ulonglong (to allow for more flags).
- New/changed table_flags()
- HA_HAS_RECORDS Set if ::records() is supported
- HA_NO_TRANSACTIONS Set if engine doesn't support transactions
- HA_PRIMARY_KEY_REQUIRED_FOR_DELETE
Set if we should mark all primary key columns for
read when reading rows as part of a DELETE
statement. If there is no primary key,
all columns are marked for read.
- HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some
cases (based on table->read_set)
- HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS
Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION.
- HA_DUPP_POS Renamed to HA_DUPLICATE_POS
- HA_REQUIRES_KEY_COLUMNS_FOR_DELETE
Set this if we should mark ALL key columns for
read when when reading rows as part of a DELETE
statement. In case of an update we will mark
all keys for read for which key part changed
value.
- HA_STATS_RECORDS_IS_EXACT
Set this if stats.records is exact.
(This saves us some extra records() calls
when optimizing COUNT(*))
- Removed table_flags()
- HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if
handler::records() gives an exact count() and
HA_STATS_RECORDS_IS_EXACT if stats.records is exact.
- HA_READ_RND_SAME Removed (no one supported this one)
- Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk()
- Renamed handler::dupp_pos to handler::dup_pos
- Removed not used variable handler::sortkey
Upper level handler changes:
- ha_reset() now does some overall checks and calls ::reset()
- ha_table_flags() added. This is a cached version of table_flags(). The
cache is updated on engine creation time and updated on open.
MySQL level changes (not obvious from the above):
- DBUG_ASSERT() added to check that column usage matches what is set
in the column usage bit maps. (This found a LOT of bugs in current
column marking code).
- In 5.1 before, all used columns was marked in read_set and only updated
columns was marked in write_set. Now we only mark columns for which we
need a value in read_set.
- Column bitmaps are created in open_binary_frm() and open_table_from_share().
(Before this was in table.cc)
- handler::table_flags() calls are replaced with handler::ha_table_flags()
- For calling field->val() you must have the corresponding bit set in
table->read_set. For calling field->store() you must have the
corresponding bit set in table->write_set. (There are asserts in
all store()/val() functions to catch wrong usage)
- thd->set_query_id is renamed to thd->mark_used_columns and instead
of setting this to an integer value, this has now the values:
MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE
Changed also all variables named 'set_query_id' to mark_used_columns.
- In filesort() we now inform the handler of exactly which columns are needed
doing the sort and choosing the rows.
- The TABLE_SHARE object has a 'all_set' column bitmap one can use
when one needs a column bitmap with all columns set.
(This is used for table->use_all_columns() and other places)
- The TABLE object has 3 column bitmaps:
- def_read_set Default bitmap for columns to be read
- def_write_set Default bitmap for columns to be written
- tmp_set Can be used as a temporary bitmap when needed.
The table object has also two pointer to bitmaps read_set and write_set
that the handler should use to find out which columns are used in which way.
- count() optimization now calls handler::records() instead of using
handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true).
- Added extra argument to Item::walk() to indicate if we should also
traverse sub queries.
- Added TABLE parameter to cp_buffer_from_ref()
- Don't close tables created with CREATE ... SELECT but keep them in
the table cache. (Faster usage of newly created tables).
New interfaces:
- table->clear_column_bitmaps() to initialize the bitmaps for tables
at start of new statements.
- table->column_bitmaps_set() to set up new column bitmaps and signal
the handler about this.
- table->column_bitmaps_set_no_signal() for some few cases where we need
to setup new column bitmaps but don't signal the handler (as the handler
has already been signaled about these before). Used for the momement
only in opt_range.cc when doing ROR scans.
- table->use_all_columns() to install a bitmap where all columns are marked
as use in the read and the write set.
- table->default_column_bitmaps() to install the normal read and write
column bitmaps, but not signaling the handler about this.
This is mainly used when creating TABLE instances.
- table->mark_columns_needed_for_delete(),
table->mark_columns_needed_for_delete() and
table->mark_columns_needed_for_insert() to allow us to put additional
columns in column usage maps if handler so requires.
(The handler indicates what it neads in handler->table_flags())
- table->prepare_for_position() to allow us to tell handler that it
needs to read primary key parts to be able to store them in
future table->position() calls.
(This replaces the table->file->ha_retrieve_all_pk function)
- table->mark_auto_increment_column() to tell handler are going to update
columns part of any auto_increment key.
- table->mark_columns_used_by_index() to mark all columns that is part of
an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow
it to quickly know that it only needs to read colums that are part
of the key. (The handler can also use the column map for detecting this,
but simpler/faster handler can just monitor the extra() call).
- table->mark_columns_used_by_index_no_reset() to in addition to other columns,
also mark all columns that is used by the given key.
- table->restore_column_maps_after_mark_index() to restore to default
column maps after a call to table->mark_columns_used_by_index().
- New item function register_field_in_read_map(), for marking used columns
in table->read_map. Used by filesort() to mark all used columns
- Maintain in TABLE->merge_keys set of all keys that are used in query.
(Simplices some optimization loops)
- Maintain Field->part_of_key_not_clustered which is like Field->part_of_key
but the field in the clustered key is not assumed to be part of all index.
(used in opt_range.cc for faster loops)
- dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map()
tmp_use_all_columns() and tmp_restore_column_map() functions to temporally
mark all columns as usable. The 'dbug_' version is primarily intended
inside a handler when it wants to just call Field:store() & Field::val()
functions, but don't need the column maps set for any other usage.
(ie:: bitmap_is_set() is never called)
- We can't use compare_records() to skip updates for handlers that returns
a partial column set and the read_set doesn't cover all columns in the
write set. The reason for this is that if we have a column marked only for
write we can't in the MySQL level know if the value changed or not.
The reason this worked before was that MySQL marked all to be written
columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden
bug'.
- open_table_from_share() does not anymore setup temporary MEM_ROOT
object as a thread specific variable for the handler. Instead we
send the to-be-used MEMROOT to get_new_handler().
(Simpler, faster code)
Bugs fixed:
- Column marking was not done correctly in a lot of cases.
(ALTER TABLE, when using triggers, auto_increment fields etc)
(Could potentially result in wrong values inserted in table handlers
relying on that the old column maps or field->set_query_id was correct)
Especially when it comes to triggers, there may be cases where the
old code would cause lost/wrong values for NDB and/or InnoDB tables.
- Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags:
OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG.
This allowed me to remove some wrong warnings about:
"Some non-transactional changed tables couldn't be rolled back"
- Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset
(thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose
some warnings about
"Some non-transactional changed tables couldn't be rolled back")
- Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table()
which could cause delete_table to report random failures.
- Fixed core dumps for some tests when running with --debug
- Added missing FN_LIBCHAR in mysql_rm_tmp_tables()
(This has probably caused us to not properly remove temporary files after
crash)
- slow_logs was not properly initialized, which could maybe cause
extra/lost entries in slow log.
- If we get an duplicate row on insert, change column map to read and
write all columns while retrying the operation. This is required by
the definition of REPLACE and also ensures that fields that are only
part of UPDATE are properly handled. This fixed a bug in NDB and
REPLACE where REPLACE wrongly copied some column values from the replaced
row.
- For table handler that doesn't support NULL in keys, we would give an error
when creating a primary key with NULL fields, even after the fields has been
automaticly converted to NOT NULL.
- Creating a primary key on a SPATIAL key, would fail if field was not
declared as NOT NULL.
Cleanups:
- Removed not used condition argument to setup_tables
- Removed not needed item function reset_query_id_processor().
- Field->add_index is removed. Now this is instead maintained in
(field->flags & FIELD_IN_ADD_INDEX)
- Field->fieldnr is removed (use field->field_index instead)
- New argument to filesort() to indicate that it should return a set of
row pointers (not used columns). This allowed me to remove some references
to sql_command in filesort and should also enable us to return column
results in some cases where we couldn't before.
- Changed column bitmap handling in opt_range.cc to be aligned with TABLE
bitmap, which allowed me to use bitmap functions instead of looping over
all fields to create some needed bitmaps. (Faster and smaller code)
- Broke up found too long lines
- Moved some variable declaration at start of function for better code
readability.
- Removed some not used arguments from functions.
(setup_fields(), mysql_prepare_insert_check_table())
- setup_fields() now takes an enum instead of an int for marking columns
usage.
- For internal temporary tables, use handler::write_row(),
handler::delete_row() and handler::update_row() instead of
handler::ha_xxxx() for faster execution.
- Changed some constants to enum's and define's.
- Using separate column read and write sets allows for easier checking
of timestamp field was set by statement.
- Remove calls to free_io_cache() as this is now done automaticly in ha_reset()
- Don't build table->normalized_path as this is now identical to table->path
(after bar's fixes to convert filenames)
- Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to
do comparision with the 'convert-dbug-for-diff' tool.
Things left to do in 5.1:
- We wrongly log failed CREATE TABLE ... SELECT in some cases when using
row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result)
Mats has promised to look into this.
- Test that my fix for CREATE TABLE ... SELECT is indeed correct.
(I added several test cases for this, but in this case it's better that
someone else also tests this throughly).
Lars has promosed to do this.
2006-06-04 17:52:22 +02:00
|
|
|
return (uint) (strmake(to, from+MYSQL50_TABLE_NAME_PREFIX_LENGTH,
|
|
|
|
to_length-1) -
|
|
|
|
(from + MYSQL50_TABLE_NAME_PREFIX_LENGTH));
|
2006-04-11 15:16:14 +02:00
|
|
|
length= strconvert(system_charset_info, from,
|
|
|
|
&my_charset_filename, to, to_length, &errors);
|
|
|
|
if (check_if_legal_tablename(to) &&
|
|
|
|
length + 4 < to_length)
|
|
|
|
{
|
|
|
|
memcpy(to + length, "@@@", 4);
|
|
|
|
length+= 3;
|
|
|
|
}
|
|
|
|
return length;
|
2005-12-31 09:34:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-05-26 05:26:40 +02:00
|
|
|
/*
|
2005-12-31 06:01:26 +01:00
|
|
|
Creates path to a file: mysql_data_dir/db/table.ext
|
2005-05-26 05:26:40 +02:00
|
|
|
|
|
|
|
SYNOPSIS
|
2005-12-31 06:01:26 +01:00
|
|
|
build_table_filename()
|
|
|
|
buff where to write result
|
|
|
|
bufflen buff size
|
|
|
|
db database name, in system_charset_info
|
|
|
|
table table name, in system_charset_info
|
|
|
|
ext file extension
|
|
|
|
|
|
|
|
NOTES
|
|
|
|
|
|
|
|
Uses database and table name, and extension to create
|
|
|
|
a file name in mysql_data_dir. Database and table
|
|
|
|
names are converted from system_charset_info into "fscs".
|
|
|
|
'ext' is not converted.
|
2005-05-26 05:26:40 +02:00
|
|
|
|
|
|
|
RETURN
|
|
|
|
|
2005-12-31 06:01:26 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
uint build_table_filename(char *buff, size_t bufflen, const char *db,
|
|
|
|
const char *table, const char *ext)
|
|
|
|
{
|
|
|
|
uint length;
|
|
|
|
char dbbuff[FN_REFLEN];
|
|
|
|
char tbbuff[FN_REFLEN];
|
|
|
|
VOID(tablename_to_filename(table, tbbuff, sizeof(tbbuff)));
|
|
|
|
VOID(tablename_to_filename(db, dbbuff, sizeof(dbbuff)));
|
|
|
|
strxnmov(buff, bufflen,
|
|
|
|
mysql_data_home, "/", dbbuff, "/", tbbuff, ext, NullS);
|
|
|
|
length= unpack_filename(buff, buff);
|
|
|
|
return length;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-01-23 13:28:42 +01:00
|
|
|
uint build_tmptable_filename(THD* thd, char *buff, size_t bufflen)
|
2005-05-26 05:26:40 +02:00
|
|
|
{
|
2005-12-31 06:01:26 +01:00
|
|
|
uint length;
|
|
|
|
char tbbuff[FN_REFLEN];
|
2006-01-23 13:28:42 +01:00
|
|
|
char tmp_table_name[tmp_file_prefix_length+22+22+22+3];
|
|
|
|
my_snprintf(tmp_table_name, sizeof(tmp_table_name),
|
|
|
|
"%s%lx_%lx_%x",
|
|
|
|
tmp_file_prefix, current_pid,
|
|
|
|
thd->thread_id, thd->tmp_table++);
|
|
|
|
VOID(tablename_to_filename(tmp_table_name, tbbuff, sizeof(tbbuff)));
|
|
|
|
strxnmov(buff, bufflen, mysql_tmpdir, "/", tbbuff, reg_ext, NullS);
|
2005-12-31 06:01:26 +01:00
|
|
|
length= unpack_filename(buff, buff);
|
|
|
|
return length;
|
2005-05-26 05:26:40 +02:00
|
|
|
}
|
|
|
|
|
2006-01-12 10:05:07 +01:00
|
|
|
/*
|
|
|
|
Return values for compare_tables().
|
|
|
|
If you make compare_tables() non-static, move them to a header file.
|
|
|
|
*/
|
|
|
|
#define ALTER_TABLE_DATA_CHANGED 1
|
|
|
|
#define ALTER_TABLE_INDEX_CHANGED 2
|
|
|
|
|
2005-05-26 05:26:40 +02:00
|
|
|
|
2006-01-17 08:40:00 +01:00
|
|
|
/*
|
|
|
|
SYNOPSIS
|
|
|
|
mysql_copy_create_list()
|
|
|
|
orig_create_list Original list of created fields
|
|
|
|
inout::new_create_list Copy of original list
|
|
|
|
|
|
|
|
RETURN VALUES
|
|
|
|
FALSE Success
|
|
|
|
TRUE Memory allocation error
|
|
|
|
|
|
|
|
DESCRIPTION
|
|
|
|
mysql_prepare_table destroys the create_list and in some cases we need
|
|
|
|
this lists for more purposes. Thus we copy it specifically for use
|
|
|
|
by mysql_prepare_table
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int mysql_copy_create_list(List<create_field> *orig_create_list,
|
2006-02-16 17:15:36 +01:00
|
|
|
List<create_field> *new_create_list)
|
2006-01-17 08:40:00 +01:00
|
|
|
{
|
|
|
|
List_iterator<create_field> prep_field_it(*orig_create_list);
|
|
|
|
create_field *prep_field;
|
|
|
|
DBUG_ENTER("mysql_copy_create_list");
|
|
|
|
|
|
|
|
while ((prep_field= prep_field_it++))
|
|
|
|
{
|
|
|
|
create_field *field= new create_field(*prep_field);
|
|
|
|
if (!field || new_create_list->push_back(field))
|
|
|
|
{
|
|
|
|
mem_alloc_error(2);
|
|
|
|
DBUG_RETURN(TRUE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DBUG_RETURN(FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
SYNOPSIS
|
|
|
|
mysql_copy_key_list()
|
|
|
|
orig_key Original list of keys
|
|
|
|
inout::new_key Copy of original list
|
|
|
|
|
|
|
|
RETURN VALUES
|
|
|
|
FALSE Success
|
|
|
|
TRUE Memory allocation error
|
|
|
|
|
|
|
|
DESCRIPTION
|
|
|
|
mysql_prepare_table destroys the key list and in some cases we need
|
|
|
|
this lists for more purposes. Thus we copy it specifically for use
|
|
|
|
by mysql_prepare_table
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int mysql_copy_key_list(List<Key> *orig_key,
|
|
|
|
List<Key> *new_key)
|
|
|
|
{
|
|
|
|
List_iterator<Key> prep_key_it(*orig_key);
|
|
|
|
Key *prep_key;
|
2006-02-16 17:15:36 +01:00
|
|
|
DBUG_ENTER("mysql_copy_key_list");
|
2006-01-17 08:40:00 +01:00
|
|
|
|
|
|
|
while ((prep_key= prep_key_it++))
|
|
|
|
{
|
|
|
|
List<key_part_spec> prep_columns;
|
|
|
|
List_iterator<key_part_spec> prep_col_it(prep_key->columns);
|
|
|
|
key_part_spec *prep_col;
|
|
|
|
Key *temp_key;
|
|
|
|
|
|
|
|
while ((prep_col= prep_col_it++))
|
|
|
|
{
|
|
|
|
key_part_spec *prep_key_part;
|
2006-02-16 17:15:36 +01:00
|
|
|
|
|
|
|
if (!(prep_key_part= new key_part_spec(*prep_col)))
|
2006-01-17 08:40:00 +01:00
|
|
|
{
|
|
|
|
mem_alloc_error(sizeof(key_part_spec));
|
|
|
|
DBUG_RETURN(TRUE);
|
|
|
|
}
|
|
|
|
if (prep_columns.push_back(prep_key_part))
|
|
|
|
{
|
|
|
|
mem_alloc_error(2);
|
|
|
|
DBUG_RETURN(TRUE);
|
|
|
|
}
|
|
|
|
}
|
2006-02-16 17:15:36 +01:00
|
|
|
if (!(temp_key= new Key(prep_key->type, prep_key->name,
|
2006-05-03 18:40:52 +02:00
|
|
|
&prep_key->key_create_info,
|
2006-02-16 17:15:36 +01:00
|
|
|
prep_key->generated,
|
2006-05-03 14:59:17 +02:00
|
|
|
prep_columns)))
|
2006-01-17 08:40:00 +01:00
|
|
|
{
|
|
|
|
mem_alloc_error(sizeof(Key));
|
|
|
|
DBUG_RETURN(TRUE);
|
|
|
|
}
|
|
|
|
if (new_key->push_back(temp_key))
|
|
|
|
{
|
|
|
|
mem_alloc_error(2);
|
|
|
|
DBUG_RETURN(TRUE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DBUG_RETURN(FALSE);
|
|
|
|
}
|
|
|
|
|
2006-02-07 10:45:07 +01:00
|
|
|
/*
|
|
|
|
--------------------------------------------------------------------------
|
|
|
|
|
2006-03-25 00:19:13 +01:00
|
|
|
MODULE: DDL log
|
2006-02-07 10:45:07 +01:00
|
|
|
-----------------
|
|
|
|
|
|
|
|
This module is used to ensure that we can recover from crashes that occur
|
|
|
|
in the middle of a meta-data operation in MySQL. E.g. DROP TABLE t1, t2;
|
|
|
|
We need to ensure that both t1 and t2 are dropped and not only t1 and
|
|
|
|
also that each table drop is entirely done and not "half-baked".
|
|
|
|
|
|
|
|
To support this we create log entries for each meta-data statement in the
|
2006-03-25 00:19:13 +01:00
|
|
|
ddl log while we are executing. These entries are dropped when the
|
2006-02-07 10:45:07 +01:00
|
|
|
operation is completed.
|
|
|
|
|
|
|
|
At recovery those entries that were not completed will be executed.
|
|
|
|
|
2006-03-25 00:19:13 +01:00
|
|
|
There is only one ddl log in the system and it is protected by a mutex
|
2006-02-07 10:45:07 +01:00
|
|
|
and there is a global struct that contains information about its current
|
|
|
|
state.
|
|
|
|
|
2006-02-08 15:14:15 +01:00
|
|
|
History:
|
|
|
|
First version written in 2006 by Mikael Ronstrom
|
2006-02-07 10:45:07 +01:00
|
|
|
--------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
2006-03-25 00:19:13 +01:00
|
|
|
typedef struct st_global_ddl_log
|
2006-02-07 10:45:07 +01:00
|
|
|
{
|
2006-04-16 03:49:13 +02:00
|
|
|
/*
|
|
|
|
We need to adjust buffer size to be able to handle downgrades/upgrades
|
|
|
|
where IO_SIZE has changed. We'll set the buffer size such that we can
|
|
|
|
handle that the buffer size was upto 4 times bigger in the version
|
|
|
|
that wrote the DDL log.
|
|
|
|
*/
|
2006-03-25 00:19:13 +01:00
|
|
|
char file_entry_buf[4*IO_SIZE];
|
2006-02-07 10:45:07 +01:00
|
|
|
char file_name_str[FN_REFLEN];
|
|
|
|
char *file_name;
|
2006-03-25 00:19:13 +01:00
|
|
|
DDL_LOG_MEMORY_ENTRY *first_free;
|
|
|
|
DDL_LOG_MEMORY_ENTRY *first_used;
|
|
|
|
uint num_entries;
|
2006-02-07 10:45:07 +01:00
|
|
|
File file_id;
|
|
|
|
uint name_len;
|
2006-02-09 11:05:05 +01:00
|
|
|
uint io_size;
|
2006-03-25 00:19:13 +01:00
|
|
|
bool inited;
|
2006-04-01 16:37:36 +02:00
|
|
|
bool recovery_phase;
|
2006-03-25 00:19:13 +01:00
|
|
|
} GLOBAL_DDL_LOG;
|
2006-02-07 10:45:07 +01:00
|
|
|
|
2006-03-25 00:19:13 +01:00
|
|
|
GLOBAL_DDL_LOG global_ddl_log;
|
2006-02-07 10:45:07 +01:00
|
|
|
|
2006-03-25 00:19:13 +01:00
|
|
|
pthread_mutex_t LOCK_gdl;
|
2006-02-07 10:45:07 +01:00
|
|
|
|
2006-03-25 00:19:13 +01:00
|
|
|
#define DDL_LOG_ENTRY_TYPE_POS 0
|
|
|
|
#define DDL_LOG_ACTION_TYPE_POS 1
|
|
|
|
#define DDL_LOG_PHASE_POS 2
|
|
|
|
#define DDL_LOG_NEXT_ENTRY_POS 4
|
|
|
|
#define DDL_LOG_NAME_POS 8
|
2006-02-10 22:36:01 +01:00
|
|
|
|
2006-03-25 00:19:13 +01:00
|
|
|
#define DDL_LOG_NUM_ENTRY_POS 0
|
|
|
|
#define DDL_LOG_NAME_LEN_POS 4
|
2006-04-16 03:49:13 +02:00
|
|
|
#define DDL_LOG_IO_SIZE_POS 8
|
2006-01-17 08:40:00 +01:00
|
|
|
|
2006-02-07 16:26:34 +01:00
|
|
|
/*
|
2006-03-25 00:19:13 +01:00
|
|
|
Read one entry from ddl log file
|
2006-02-07 16:26:34 +01:00
|
|
|
SYNOPSIS
|
2006-03-25 00:19:13 +01:00
|
|
|
read_ddl_log_file_entry()
|
2006-02-10 22:36:01 +01:00
|
|
|
entry_no Entry number to read
|
2006-02-07 16:26:34 +01:00
|
|
|
RETURN VALUES
|
2006-02-10 22:36:01 +01:00
|
|
|
TRUE Error
|
|
|
|
FALSE Success
|
2006-02-07 16:26:34 +01:00
|
|
|
*/
|
|
|
|
|
2006-03-25 00:19:13 +01:00
|
|
|
static bool read_ddl_log_file_entry(uint entry_no)
|
2006-02-07 16:26:34 +01:00
|
|
|
{
|
|
|
|
bool error= FALSE;
|
2006-03-25 00:19:13 +01:00
|
|
|
File file_id= global_ddl_log.file_id;
|
|
|
|
char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
|
|
|
|
uint io_size= global_ddl_log.io_size;
|
|
|
|
DBUG_ENTER("read_ddl_log_file_entry");
|
2006-02-07 16:26:34 +01:00
|
|
|
|
2006-04-22 13:29:05 +02:00
|
|
|
if (my_pread(file_id, (byte*)file_entry_buf, io_size, io_size * entry_no,
|
2006-03-25 00:19:13 +01:00
|
|
|
MYF(MY_WME)) != io_size)
|
2006-02-07 16:26:34 +01:00
|
|
|
error= TRUE;
|
|
|
|
DBUG_RETURN(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2006-03-25 00:19:13 +01:00
|
|
|
Write one entry from ddl log file
|
2006-02-07 16:26:34 +01:00
|
|
|
SYNOPSIS
|
2006-03-25 00:19:13 +01:00
|
|
|
write_ddl_log_file_entry()
|
2006-02-07 16:26:34 +01:00
|
|
|
entry_no Entry number to read
|
|
|
|
RETURN VALUES
|
|
|
|
TRUE Error
|
|
|
|
FALSE Success
|
|
|
|
*/
|
|
|
|
|
2006-03-25 00:19:13 +01:00
|
|
|
static bool write_ddl_log_file_entry(uint entry_no)
|
2006-02-07 16:26:34 +01:00
|
|
|
{
|
|
|
|
bool error= FALSE;
|
2006-03-25 00:19:13 +01:00
|
|
|
File file_id= global_ddl_log.file_id;
|
|
|
|
char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
|
|
|
|
DBUG_ENTER("write_ddl_log_file_entry");
|
2006-02-07 16:26:34 +01:00
|
|
|
|
2006-04-22 13:29:05 +02:00
|
|
|
if (my_pwrite(file_id, (byte*)file_entry_buf,
|
2006-02-14 14:22:21 +01:00
|
|
|
IO_SIZE, IO_SIZE * entry_no, MYF(MY_WME)) != IO_SIZE)
|
2006-02-07 16:26:34 +01:00
|
|
|
error= TRUE;
|
|
|
|
DBUG_RETURN(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2006-03-25 00:19:13 +01:00
|
|
|
Write ddl log header
|
2006-02-07 16:26:34 +01:00
|
|
|
SYNOPSIS
|
2006-03-25 00:19:13 +01:00
|
|
|
write_ddl_log_header()
|
2006-02-07 16:26:34 +01:00
|
|
|
RETURN VALUES
|
|
|
|
TRUE Error
|
|
|
|
FALSE Success
|
|
|
|
*/
|
|
|
|
|
2006-03-25 00:19:13 +01:00
|
|
|
static bool write_ddl_log_header()
|
2006-02-07 16:26:34 +01:00
|
|
|
{
|
|
|
|
uint16 const_var;
|
2006-02-08 15:14:15 +01:00
|
|
|
bool error= FALSE;
|
2006-03-25 00:19:13 +01:00
|
|
|
DBUG_ENTER("write_ddl_log_header");
|
2006-02-07 16:26:34 +01:00
|
|
|
|
2006-03-25 00:19:13 +01:00
|
|
|
int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NUM_ENTRY_POS],
|
|
|
|
global_ddl_log.num_entries);
|
2006-02-09 00:04:58 +01:00
|
|
|
const_var= FN_LEN;
|
2006-03-25 00:19:13 +01:00
|
|
|
int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_LEN_POS],
|
2006-02-10 22:36:01 +01:00
|
|
|
const_var);
|
2006-02-09 11:05:05 +01:00
|
|
|
const_var= IO_SIZE;
|
2006-03-25 00:19:13 +01:00
|
|
|
int4store(&global_ddl_log.file_entry_buf[DDL_LOG_IO_SIZE_POS],
|
2006-02-10 22:36:01 +01:00
|
|
|
const_var);
|
2006-03-25 00:19:13 +01:00
|
|
|
if (write_ddl_log_file_entry(0UL))
|
2006-04-01 16:37:36 +02:00
|
|
|
{
|
|
|
|
sql_print_error("Error writing ddl log header");
|
|
|
|
DBUG_RETURN(TRUE);
|
|
|
|
}
|
|
|
|
VOID(sync_ddl_log());
|
2006-02-08 15:14:15 +01:00
|
|
|
DBUG_RETURN(error);
|
2006-02-06 21:47:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-02-08 15:14:15 +01:00
|
|
|
/*
|
2006-03-25 00:19:13 +01:00
|
|
|
Create ddl log file name
|
2006-02-08 15:14:15 +01:00
|
|
|
SYNOPSIS
|
2006-03-25 00:19:13 +01:00
|
|
|
create_ddl_log_file_name()
|
2006-02-08 15:14:15 +01:00
|
|
|
file_name Filename setup
|
|
|
|
RETURN VALUES
|
|
|
|
NONE
|
|
|
|
*/
|
|
|
|
|
2006-03-25 00:19:13 +01:00
|
|
|
static inline void create_ddl_log_file_name(char *file_name)
|
2006-02-08 15:14:15 +01:00
|
|
|
{
|
2006-03-25 00:19:13 +01:00
|
|
|
strxmov(file_name, mysql_data_home, "/", "ddl_log.log", NullS);
|
2006-02-08 15:14:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-02-06 21:47:03 +01:00
|
|
|
/*
|
2006-03-25 00:19:13 +01:00
|
|
|
Read header of ddl log file
|
2006-02-06 21:47:03 +01:00
|
|
|
SYNOPSIS
|
2006-03-25 00:19:13 +01:00
|
|
|
read_ddl_log_header()
|
2006-02-06 21:47:03 +01:00
|
|
|
RETURN VALUES
|
2006-03-25 00:19:13 +01:00
|
|
|
> 0 Last entry in ddl log
|
|
|
|
0 No entries in ddl log
|
2006-02-06 21:47:03 +01:00
|
|
|
DESCRIPTION
|
2006-03-25 00:19:13 +01:00
|
|
|
When we read the ddl log header we get information about maximum sizes
|
|
|
|
of names in the ddl log and we also get information about the number
|
|
|
|
of entries in the ddl log.
|
2006-02-06 21:47:03 +01:00
|
|
|
*/
|
|
|
|
|
2006-03-25 00:19:13 +01:00
|
|
|
static uint read_ddl_log_header()
|
2006-02-06 21:47:03 +01:00
|
|
|
{
|
2006-03-25 00:19:13 +01:00
|
|
|
char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
|
2006-02-08 15:14:15 +01:00
|
|
|
char file_name[FN_REFLEN];
|
2006-02-09 00:04:58 +01:00
|
|
|
uint entry_no;
|
2006-02-09 11:05:05 +01:00
|
|
|
bool successful_open= FALSE;
|
2006-03-25 00:19:13 +01:00
|
|
|
DBUG_ENTER("read_ddl_log_header");
|
2006-02-07 10:45:07 +01:00
|
|
|
|
2006-03-25 00:19:13 +01:00
|
|
|
create_ddl_log_file_name(file_name);
|
2006-04-19 22:08:59 +02:00
|
|
|
if ((global_ddl_log.file_id= my_open(file_name,
|
|
|
|
O_RDWR | O_BINARY, MYF(MY_WME))) >= 0)
|
2006-02-07 10:45:07 +01:00
|
|
|
{
|
2006-03-25 00:19:13 +01:00
|
|
|
if (read_ddl_log_file_entry(0UL))
|
2006-02-08 15:14:15 +01:00
|
|
|
{
|
2006-04-01 16:37:36 +02:00
|
|
|
/* Write message into error log */
|
|
|
|
sql_print_error("Failed to read ddl log file in recovery");
|
2006-02-08 15:14:15 +01:00
|
|
|
}
|
2006-02-09 11:05:05 +01:00
|
|
|
else
|
|
|
|
successful_open= TRUE;
|
2006-02-07 10:45:07 +01:00
|
|
|
}
|
2006-03-25 00:19:13 +01:00
|
|
|
entry_no= uint4korr(&file_entry_buf[DDL_LOG_NUM_ENTRY_POS]);
|
|
|
|
global_ddl_log.name_len= uint4korr(&file_entry_buf[DDL_LOG_NAME_LEN_POS]);
|
2006-04-16 03:49:13 +02:00
|
|
|
if (successful_open)
|
|
|
|
{
|
2006-03-25 00:19:13 +01:00
|
|
|
global_ddl_log.io_size= uint4korr(&file_entry_buf[DDL_LOG_IO_SIZE_POS]);
|
2006-04-16 03:49:13 +02:00
|
|
|
DBUG_ASSERT(global_ddl_log.io_size <=
|
|
|
|
sizeof(global_ddl_log.file_entry_buf));
|
|
|
|
}
|
2006-02-20 22:22:19 +01:00
|
|
|
else
|
2006-04-01 16:37:36 +02:00
|
|
|
{
|
|
|
|
entry_no= 0;
|
|
|
|
}
|
2006-03-25 00:19:13 +01:00
|
|
|
global_ddl_log.first_free= NULL;
|
|
|
|
global_ddl_log.first_used= NULL;
|
|
|
|
global_ddl_log.num_entries= 0;
|
|
|
|
VOID(pthread_mutex_init(&LOCK_gdl, MY_MUTEX_INIT_FAST));
|
2006-02-07 10:45:07 +01:00
|
|
|
DBUG_RETURN(entry_no);
|
2006-02-06 21:47:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2006-03-25 00:19:13 +01:00
|
|
|
Read a ddl log entry
|
2006-02-06 21:47:03 +01:00
|
|
|
SYNOPSIS
|
2006-03-25 00:19:13 +01:00
|
|
|
read_ddl_log_entry()
|
2006-02-06 21:47:03 +01:00
|
|
|
read_entry Number of entry to read
|
|
|
|
out:entry_info Information from entry
|
|
|
|
RETURN VALUES
|
|
|
|
TRUE Error
|
|
|
|
FALSE Success
|
|
|
|
DESCRIPTION
|
2006-03-25 00:19:13 +01:00
|
|
|
Read a specified entry in the ddl log
|
2006-02-06 21:47:03 +01:00
|
|
|
*/
|
|
|
|
|
2006-03-25 00:19:13 +01:00
|
|
|
bool read_ddl_log_entry(uint read_entry, DDL_LOG_ENTRY *ddl_log_entry)
|
2006-02-06 21:47:03 +01:00
|
|
|
{
|
2006-03-25 00:19:13 +01:00
|
|
|
char *file_entry_buf= (char*)&global_ddl_log.file_entry_buf;
|
2006-02-09 00:04:58 +01:00
|
|
|
uint inx;
|
2006-04-20 02:04:00 +02:00
|
|
|
uchar single_char;
|
2006-03-25 00:19:13 +01:00
|
|
|
DBUG_ENTER("read_ddl_log_entry");
|
2006-02-08 15:14:15 +01:00
|
|
|
|
2006-03-25 00:19:13 +01:00
|
|
|
if (read_ddl_log_file_entry(read_entry))
|
2006-02-08 15:14:15 +01:00
|
|
|
{
|
|
|
|
DBUG_RETURN(TRUE);
|
|
|
|
}
|
2006-03-25 00:19:13 +01:00
|
|
|
ddl_log_entry->entry_pos= read_entry;
|
2006-04-20 02:04:00 +02:00
|
|
|
single_char= file_entry_buf[DDL_LOG_ENTRY_TYPE_POS];
|
|
|
|
ddl_log_entry->entry_type= (enum ddl_log_entry_code)single_char;
|
|
|
|
single_char= file_entry_buf[DDL_LOG_ACTION_TYPE_POS];
|
|
|
|
ddl_log_entry->action_type= (enum ddl_log_action_code)single_char;
|
2006-03-25 00:19:13 +01:00
|
|
|
ddl_log_entry->phase= file_entry_buf[DDL_LOG_PHASE_POS];
|
|
|
|
ddl_log_entry->next_entry= uint4korr(&file_entry_buf[DDL_LOG_NEXT_ENTRY_POS]);
|
|
|
|
ddl_log_entry->name= &file_entry_buf[DDL_LOG_NAME_POS];
|
|
|
|
inx= DDL_LOG_NAME_POS + global_ddl_log.name_len;
|
|
|
|
ddl_log_entry->from_name= &file_entry_buf[inx];
|
|
|
|
inx+= global_ddl_log.name_len;
|
|
|
|
ddl_log_entry->handler_name= &file_entry_buf[inx];
|
2006-02-06 21:47:03 +01:00
|
|
|
DBUG_RETURN(FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2006-03-25 00:19:13 +01:00
|
|
|
Initialise ddl log
|
2006-02-06 21:47:03 +01:00
|
|
|
SYNOPSIS
|
2006-03-25 00:19:13 +01:00
|
|
|
init_ddl_log()
|
2006-05-03 18:40:52 +02:00
|
|
|
|
2006-02-06 21:47:03 +01:00
|
|
|
DESCRIPTION
|
2006-03-25 00:19:13 +01:00
|
|
|
Write the header of the ddl log file and length of names. Also set
|
2006-02-06 21:47:03 +01:00
|
|
|
number of entries to zero.
|
2006-05-03 18:40:52 +02:00
|
|
|
|
|
|
|
RETURN VALUES
|
|
|
|
TRUE Error
|
|
|
|
FALSE Success
|
2006-02-06 21:47:03 +01:00
|
|
|
*/
|
|
|
|
|
2006-03-25 00:19:13 +01:00
|
|
|
static bool init_ddl_log()
|
2006-02-06 21:47:03 +01:00
|
|
|
{
|
2006-02-07 16:26:34 +01:00
|
|
|
bool error= FALSE;
|
2006-02-08 15:14:15 +01:00
|
|
|
char file_name[FN_REFLEN];
|
2006-03-25 00:19:13 +01:00
|
|
|
DBUG_ENTER("init_ddl_log");
|
2006-02-07 16:26:34 +01:00
|
|
|
|
2006-03-25 00:19:13 +01:00
|
|
|
if (global_ddl_log.inited)
|
2006-05-03 18:40:52 +02:00
|
|
|
goto end;
|
|
|
|
|
2006-03-25 00:19:13 +01:00
|
|
|
global_ddl_log.io_size= IO_SIZE;
|
2006-04-19 22:08:59 +02:00
|
|
|
create_ddl_log_file_name(file_name);
|
2006-03-25 00:19:13 +01:00
|
|
|
if ((global_ddl_log.file_id= my_create(file_name,
|
|
|
|
CREATE_MODE,
|
|
|
|
O_RDWR | O_TRUNC | O_BINARY,
|
|
|
|
MYF(MY_WME))) < 0)
|
2006-02-08 15:14:15 +01:00
|
|
|
{
|
2006-03-25 00:19:13 +01:00
|
|
|
/* Couldn't create ddl log file, this is serious error */
|
2006-04-01 16:37:36 +02:00
|
|
|
sql_print_error("Failed to open ddl log file");
|
|
|
|
DBUG_RETURN(TRUE);
|
2006-02-08 15:14:15 +01:00
|
|
|
}
|
2006-04-03 18:26:35 +02:00
|
|
|
global_ddl_log.inited= TRUE;
|
2006-03-25 00:19:13 +01:00
|
|
|
if (write_ddl_log_header())
|
2006-02-08 15:14:15 +01:00
|
|
|
{
|
2006-05-03 18:40:52 +02:00
|
|
|
VOID(my_close(global_ddl_log.file_id, MYF(MY_WME)));
|
2006-04-03 18:26:35 +02:00
|
|
|
global_ddl_log.inited= FALSE;
|
2006-04-01 16:37:36 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
2006-02-08 15:14:15 +01:00
|
|
|
}
|
2006-05-03 18:40:52 +02:00
|
|
|
|
|
|
|
end:
|
2006-04-01 16:37:36 +02:00
|
|
|
DBUG_RETURN(FALSE);
|
2006-02-06 21:47:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2006-03-25 00:19:13 +01:00
|
|
|
Execute one action in a ddl log entry
|
2006-02-06 21:47:03 +01:00
|
|
|
SYNOPSIS
|
2006-03-25 00:19:13 +01:00
|
|
|
execute_ddl_log_action()
|
|
|
|
ddl_log_entry Information in action entry to execute
|
2006-02-06 21:47:03 +01:00
|
|
|
RETURN VALUES
|
2006-02-08 15:14:15 +01:00
|
|
|
TRUE Error
|
|
|
|
FALSE Success
|
2006-02-06 21:47:03 +01:00
|
|
|
*/
|
|
|
|
|
2006-04-20 02:57:42 +02:00
|
|
|
static int execute_ddl_log_action(THD *thd, DDL_LOG_ENTRY *ddl_log_entry)
|
2006-02-06 21:47:03 +01:00
|
|
|
{
|
2006-02-14 11:08:58 +01:00
|
|
|
bool frm_action= FALSE;
|
|
|
|
LEX_STRING handler_name;
|
2006-04-20 02:04:00 +02:00
|
|
|
handler *file= NULL;
|
2006-02-20 21:07:03 +01:00
|
|
|
MEM_ROOT mem_root;
|
2006-04-20 02:57:42 +02:00
|
|
|
int error= TRUE;
|
2006-04-20 02:04:00 +02:00
|
|
|
char to_path[FN_REFLEN];
|
2006-02-14 11:08:58 +01:00
|
|
|
char from_path[FN_REFLEN];
|
2006-02-20 21:07:03 +01:00
|
|
|
char *par_ext= (char*)".par";
|
|
|
|
handlerton *hton;
|
2006-03-25 00:19:13 +01:00
|
|
|
DBUG_ENTER("execute_ddl_log_action");
|
2006-02-14 11:08:58 +01:00
|
|
|
|
2006-03-25 00:19:13 +01:00
|
|
|
if (ddl_log_entry->entry_type == DDL_IGNORE_LOG_ENTRY_CODE)
|
2006-02-14 11:08:58 +01:00
|
|
|
{
|
|
|
|
DBUG_RETURN(FALSE);
|
|
|
|
}
|
2006-03-25 00:19:13 +01:00
|
|
|
handler_name.str= (char*)ddl_log_entry->handler_name;
|
|
|
|
handler_name.length= strlen(ddl_log_entry->handler_name);
|
2006-02-14 11:08:58 +01:00
|
|
|
init_sql_alloc(&mem_root, TABLE_ALLOC_BLOCK_SIZE, 0);
|
2006-04-20 02:04:00 +02:00
|
|
|
if (!strcmp(ddl_log_entry->handler_name, reg_ext))
|
2006-02-14 11:08:58 +01:00
|
|
|
frm_action= TRUE;
|
|
|
|
else
|
|
|
|
{
|
2006-02-20 21:07:03 +01:00
|
|
|
TABLE_SHARE dummy;
|
2006-04-19 22:08:59 +02:00
|
|
|
|
|
|
|
hton= ha_resolve_by_name(thd, &handler_name);
|
|
|
|
if (!hton)
|
|
|
|
{
|
|
|
|
my_error(ER_ILLEGAL_HA, MYF(0), ddl_log_entry->handler_name);
|
|
|
|
goto error;
|
|
|
|
}
|
2006-02-20 21:07:03 +01:00
|
|
|
bzero(&dummy, sizeof(TABLE_SHARE));
|
|
|
|
file= get_new_handler(&dummy, &mem_root, hton);
|
2006-02-14 11:08:58 +01:00
|
|
|
if (!file)
|
2006-02-14 14:22:21 +01:00
|
|
|
{
|
|
|
|
mem_alloc_error(sizeof(handler));
|
2006-02-14 11:08:58 +01:00
|
|
|
goto error;
|
2006-02-14 14:22:21 +01:00
|
|
|
}
|
2006-02-14 11:08:58 +01:00
|
|
|
}
|
2006-03-25 00:19:13 +01:00
|
|
|
switch (ddl_log_entry->action_type)
|
2006-02-20 21:07:03 +01:00
|
|
|
{
|
2006-03-25 00:19:13 +01:00
|
|
|
case DDL_LOG_REPLACE_ACTION:
|
2006-04-01 16:37:36 +02:00
|
|
|
case DDL_LOG_DELETE_ACTION:
|
2006-02-20 21:07:03 +01:00
|
|
|
{
|
2006-04-01 16:37:36 +02:00
|
|
|
if (ddl_log_entry->phase == 0)
|
2006-02-14 11:08:58 +01:00
|
|
|
{
|
|
|
|
if (frm_action)
|
|
|
|
{
|
2006-04-20 02:04:00 +02:00
|
|
|
strxmov(to_path, ddl_log_entry->name, reg_ext, NullS);
|
|
|
|
if ((error= my_delete(to_path, MYF(MY_WME))))
|
|
|
|
{
|
2006-04-20 02:57:42 +02:00
|
|
|
if (my_errno != ENOENT)
|
2006-04-20 02:04:00 +02:00
|
|
|
break;
|
|
|
|
}
|
2006-04-01 16:37:36 +02:00
|
|
|
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
2006-04-20 02:04:00 +02:00
|
|
|
strxmov(to_path, ddl_log_entry->name, par_ext, NullS);
|
|
|
|
VOID(my_delete(to_path, MYF(MY_WME)));
|
2006-04-01 16:37:36 +02:00
|
|
|
#endif
|
2006-02-14 11:08:58 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-04-20 02:04:00 +02:00
|
|
|
if ((error= file->delete_table(ddl_log_entry->name)))
|
|
|
|
{
|
|
|
|
if (error != ENOENT && error != HA_ERR_NO_SUCH_TABLE)
|
|
|
|
break;
|
|
|
|
}
|
2006-02-14 11:08:58 +01:00
|
|
|
}
|
2006-04-01 16:37:36 +02:00
|
|
|
if ((deactivate_ddl_log_entry(ddl_log_entry->entry_pos)))
|
2006-04-20 02:04:00 +02:00
|
|
|
break;
|
|
|
|
VOID(sync_ddl_log());
|
|
|
|
error= FALSE;
|
2006-04-01 16:37:36 +02:00
|
|
|
if (ddl_log_entry->action_type == DDL_LOG_DELETE_ACTION)
|
|
|
|
break;
|
2006-02-14 11:08:58 +01:00
|
|
|
}
|
2006-04-01 16:37:36 +02:00
|
|
|
DBUG_ASSERT(ddl_log_entry->action_type == DDL_LOG_REPLACE_ACTION);
|
|
|
|
/*
|
|
|
|
Fall through and perform the rename action of the replace
|
|
|
|
action. We have already indicated the success of the delete
|
|
|
|
action in the log entry by stepping up the phase.
|
|
|
|
*/
|
2006-02-20 21:07:03 +01:00
|
|
|
}
|
2006-03-25 00:19:13 +01:00
|
|
|
case DDL_LOG_RENAME_ACTION:
|
2006-02-20 21:07:03 +01:00
|
|
|
{
|
2006-02-14 11:08:58 +01:00
|
|
|
error= TRUE;
|
|
|
|
if (frm_action)
|
|
|
|
{
|
2006-04-20 02:04:00 +02:00
|
|
|
strxmov(to_path, ddl_log_entry->name, reg_ext, NullS);
|
2006-03-25 00:19:13 +01:00
|
|
|
strxmov(from_path, ddl_log_entry->from_name, reg_ext, NullS);
|
2006-04-20 02:04:00 +02:00
|
|
|
if (my_rename(from_path, to_path, MYF(MY_WME)))
|
2006-02-14 11:08:58 +01:00
|
|
|
break;
|
2006-04-01 16:37:36 +02:00
|
|
|
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
2006-04-20 02:04:00 +02:00
|
|
|
strxmov(to_path, ddl_log_entry->name, par_ext, NullS);
|
2006-03-25 00:19:13 +01:00
|
|
|
strxmov(from_path, ddl_log_entry->from_name, par_ext, NullS);
|
2006-04-20 02:04:00 +02:00
|
|
|
VOID(my_rename(from_path, to_path, MYF(MY_WME)));
|
2006-04-01 16:37:36 +02:00
|
|
|
#endif
|
2006-02-14 11:08:58 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-04-20 02:04:00 +02:00
|
|
|
if (file->rename_table(ddl_log_entry->from_name,
|
|
|
|
ddl_log_entry->name))
|
2006-02-14 11:08:58 +01:00
|
|
|
break;
|
2006-04-01 16:37:36 +02:00
|
|
|
}
|
|
|
|
if ((deactivate_ddl_log_entry(ddl_log_entry->entry_pos)))
|
2006-04-20 02:04:00 +02:00
|
|
|
break;
|
|
|
|
VOID(sync_ddl_log());
|
|
|
|
error= FALSE;
|
2006-02-14 11:08:58 +01:00
|
|
|
break;
|
2006-02-20 21:07:03 +01:00
|
|
|
}
|
2006-02-14 11:08:58 +01:00
|
|
|
default:
|
|
|
|
DBUG_ASSERT(0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
delete file;
|
|
|
|
error:
|
|
|
|
free_root(&mem_root, MYF(0));
|
|
|
|
DBUG_RETURN(error);
|
2006-02-06 21:47:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-02-07 10:45:07 +01:00
|
|
|
/*
|
2006-03-25 00:19:13 +01:00
|
|
|
Get a free entry in the ddl log
|
2006-02-07 10:45:07 +01:00
|
|
|
SYNOPSIS
|
2006-03-25 00:19:13 +01:00
|
|
|
get_free_ddl_log_entry()
|
|
|
|
out:active_entry A ddl log memory entry returned
|
2006-02-07 10:45:07 +01:00
|
|
|
RETURN VALUES
|
2006-02-08 15:14:15 +01:00
|
|
|
TRUE Error
|
|
|
|
FALSE Success
|
2006-02-07 10:45:07 +01:00
|
|
|
*/
|
|
|
|
|
2006-03-25 00:19:13 +01:00
|
|
|
static bool get_free_ddl_log_entry(DDL_LOG_MEMORY_ENTRY **active_entry,
|
|
|
|
bool *write_header)
|
2006-02-07 10:45:07 +01:00
|
|
|
{
|
2006-03-25 00:19:13 +01:00
|
|
|
DDL_LOG_MEMORY_ENTRY *used_entry;
|
|
|
|
DDL_LOG_MEMORY_ENTRY *first_used= global_ddl_log.first_used;
|
|
|
|
DBUG_ENTER("get_free_ddl_log_entry");
|
2006-02-09 00:04:58 +01:00
|
|
|
|
2006-03-25 00:19:13 +01:00
|
|
|
if (global_ddl_log.first_free == NULL)
|
2006-02-08 15:14:15 +01:00
|
|
|
{
|
2006-03-25 00:19:13 +01:00
|
|
|
if (!(used_entry= (DDL_LOG_MEMORY_ENTRY*)my_malloc(
|
|
|
|
sizeof(DDL_LOG_MEMORY_ENTRY), MYF(MY_WME))))
|
2006-02-08 15:14:15 +01:00
|
|
|
{
|
2006-04-01 16:37:36 +02:00
|
|
|
sql_print_error("Failed to allocate memory for ddl log free list");
|
2006-02-08 15:14:15 +01:00
|
|
|
DBUG_RETURN(TRUE);
|
|
|
|
}
|
2006-03-25 00:19:13 +01:00
|
|
|
global_ddl_log.num_entries++;
|
2006-04-01 16:37:36 +02:00
|
|
|
used_entry->entry_pos= global_ddl_log.num_entries;
|
2006-02-09 00:04:58 +01:00
|
|
|
*write_header= TRUE;
|
2006-02-08 15:14:15 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-03-25 00:19:13 +01:00
|
|
|
used_entry= global_ddl_log.first_free;
|
|
|
|
global_ddl_log.first_free= used_entry->next_log_entry;
|
2006-02-09 00:04:58 +01:00
|
|
|
*write_header= FALSE;
|
2006-02-08 15:14:15 +01:00
|
|
|
}
|
|
|
|
/*
|
|
|
|
Move from free list to used list
|
|
|
|
*/
|
|
|
|
used_entry->next_log_entry= first_used;
|
|
|
|
used_entry->prev_log_entry= NULL;
|
2006-03-25 00:19:13 +01:00
|
|
|
global_ddl_log.first_used= used_entry;
|
2006-02-08 15:14:15 +01:00
|
|
|
if (first_used)
|
|
|
|
first_used->prev_log_entry= used_entry;
|
|
|
|
|
|
|
|
*active_entry= used_entry;
|
2006-02-09 00:04:58 +01:00
|
|
|
DBUG_RETURN(FALSE);
|
2006-02-07 10:45:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2006-03-25 00:19:13 +01:00
|
|
|
External interface methods for the DDL log Module
|
2006-02-08 15:14:15 +01:00
|
|
|
---------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2006-02-07 10:45:07 +01:00
|
|
|
SYNOPSIS
|
2006-03-25 00:19:13 +01:00
|
|
|
write_ddl_log_entry()
|
|
|
|
ddl_log_entry Information about log entry
|
|
|
|
out:entry_written Entry information written into
|
2006-02-08 15:14:15 +01:00
|
|
|
|
2006-02-07 10:45:07 +01:00
|
|
|
RETURN VALUES
|
2006-02-08 15:14:15 +01:00
|
|
|
TRUE Error
|
|
|
|
FALSE Success
|
|
|
|
|
|
|
|
DESCRIPTION
|
2006-03-25 00:19:13 +01:00
|
|
|
A careful write of the ddl log is performed to ensure that we can
|
2006-02-08 15:14:15 +01:00
|
|
|
handle crashes occurring during CREATE and ALTER TABLE processing.
|
2006-02-07 10:45:07 +01:00
|
|
|
*/
|
|
|
|
|
2006-03-25 00:19:13 +01:00
|
|
|
bool write_ddl_log_entry(DDL_LOG_ENTRY *ddl_log_entry,
|
|
|
|
DDL_LOG_MEMORY_ENTRY **active_entry)
|
2006-02-07 10:45:07 +01:00
|
|
|
{
|
2006-02-09 00:04:58 +01:00
|
|
|
bool error, write_header;
|
2006-03-25 00:19:13 +01:00
|
|
|
DBUG_ENTER("write_ddl_log_entry");
|
|
|
|
|
|
|
|
if (init_ddl_log())
|
|
|
|
{
|
|
|
|
DBUG_RETURN(TRUE);
|
|
|
|
}
|
2006-04-20 02:04:00 +02:00
|
|
|
global_ddl_log.file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]=
|
|
|
|
(char)DDL_LOG_ENTRY_CODE;
|
2006-03-25 00:19:13 +01:00
|
|
|
global_ddl_log.file_entry_buf[DDL_LOG_ACTION_TYPE_POS]=
|
2006-04-20 02:04:00 +02:00
|
|
|
(char)ddl_log_entry->action_type;
|
2006-03-25 00:19:13 +01:00
|
|
|
global_ddl_log.file_entry_buf[DDL_LOG_PHASE_POS]= 0;
|
|
|
|
int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NEXT_ENTRY_POS],
|
|
|
|
ddl_log_entry->next_entry);
|
|
|
|
DBUG_ASSERT(strlen(ddl_log_entry->name) < FN_LEN);
|
2006-04-21 14:35:25 +02:00
|
|
|
strmake(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS],
|
|
|
|
ddl_log_entry->name, FN_LEN - 1);
|
2006-03-25 00:19:13 +01:00
|
|
|
if (ddl_log_entry->action_type == DDL_LOG_RENAME_ACTION ||
|
|
|
|
ddl_log_entry->action_type == DDL_LOG_REPLACE_ACTION)
|
2006-02-10 05:57:54 +01:00
|
|
|
{
|
2006-03-25 00:19:13 +01:00
|
|
|
DBUG_ASSERT(strlen(ddl_log_entry->from_name) < FN_LEN);
|
2006-04-21 14:35:25 +02:00
|
|
|
strmake(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + FN_LEN],
|
|
|
|
ddl_log_entry->from_name, FN_LEN - 1);
|
2006-02-10 05:57:54 +01:00
|
|
|
}
|
2006-02-09 20:13:22 +01:00
|
|
|
else
|
2006-03-25 00:19:13 +01:00
|
|
|
global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + FN_LEN]= 0;
|
|
|
|
DBUG_ASSERT(strlen(ddl_log_entry->handler_name) < FN_LEN);
|
2006-04-21 14:35:25 +02:00
|
|
|
strmake(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + (2*FN_LEN)],
|
|
|
|
ddl_log_entry->handler_name, FN_LEN - 1);
|
2006-03-25 00:19:13 +01:00
|
|
|
if (get_free_ddl_log_entry(active_entry, &write_header))
|
2006-02-08 15:14:15 +01:00
|
|
|
{
|
|
|
|
DBUG_RETURN(TRUE);
|
|
|
|
}
|
|
|
|
error= FALSE;
|
2006-03-25 00:19:13 +01:00
|
|
|
if (write_ddl_log_file_entry((*active_entry)->entry_pos))
|
2006-04-01 16:37:36 +02:00
|
|
|
{
|
2006-02-08 15:14:15 +01:00
|
|
|
error= TRUE;
|
2006-04-01 16:37:36 +02:00
|
|
|
sql_print_error("Failed to write entry_no = %u",
|
|
|
|
(*active_entry)->entry_pos);
|
|
|
|
}
|
2006-02-09 00:04:58 +01:00
|
|
|
if (write_header && !error)
|
|
|
|
{
|
2006-03-25 00:19:13 +01:00
|
|
|
VOID(sync_ddl_log());
|
|
|
|
if (write_ddl_log_header())
|
2006-02-09 00:04:58 +01:00
|
|
|
error= TRUE;
|
|
|
|
}
|
2006-02-08 15:14:15 +01:00
|
|
|
if (error)
|
2006-03-25 00:19:13 +01:00
|
|
|
release_ddl_log_memory_entry(*active_entry);
|
2006-02-08 15:14:15 +01:00
|
|
|
DBUG_RETURN(error);
|
2006-02-07 10:45:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2006-03-25 00:19:13 +01:00
|
|
|
Write final entry in the ddl log
|
2006-02-07 10:45:07 +01:00
|
|
|
SYNOPSIS
|
2006-03-25 00:19:13 +01:00
|
|
|
write_execute_ddl_log_entry()
|
2006-02-08 15:14:15 +01:00
|
|
|
first_entry First entry in linked list of entries
|
|
|
|
to execute, if 0 = NULL it means that
|
|
|
|
the entry is removed and the entries
|
|
|
|
are put into the free list.
|
2006-04-16 03:49:13 +02:00
|
|
|
complete Flag indicating we are simply writing
|
|
|
|
info about that entry has been completed
|
|
|
|
in:out:active_entry Entry to execute, 0 = NULL if the entry
|
2006-02-08 15:14:15 +01:00
|
|
|
is written first time and needs to be
|
|
|
|
returned. In this case the entry written
|
|
|
|
is returned in this parameter
|
2006-02-07 10:45:07 +01:00
|
|
|
RETURN VALUES
|
2006-02-08 15:14:15 +01:00
|
|
|
TRUE Error
|
|
|
|
FALSE Success
|
|
|
|
|
|
|
|
DESCRIPTION
|
2006-03-25 00:19:13 +01:00
|
|
|
This is the last write in the ddl log. The previous log entries have
|
2006-02-08 15:14:15 +01:00
|
|
|
already been written but not yet synched to disk.
|
2006-04-01 16:37:36 +02:00
|
|
|
We write a couple of log entries that describes action to perform.
|
|
|
|
This entries are set-up in a linked list, however only when a first
|
|
|
|
execute entry is put as the first entry these will be executed.
|
|
|
|
This routine writes this first
|
2006-02-08 15:14:15 +01:00
|
|
|
*/
|
2006-02-07 10:45:07 +01:00
|
|
|
|
2006-03-25 00:19:13 +01:00
|
|
|
bool write_execute_ddl_log_entry(uint first_entry,
|
|
|
|
bool complete,
|
|
|
|
DDL_LOG_MEMORY_ENTRY **active_entry)
|
2006-02-07 10:45:07 +01:00
|
|
|
{
|
2006-02-10 12:30:51 +01:00
|
|
|
bool write_header= FALSE;
|
2006-03-25 00:19:13 +01:00
|
|
|
char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
|
|
|
|
DBUG_ENTER("write_execute_ddl_log_entry");
|
2006-02-08 15:14:15 +01:00
|
|
|
|
2006-03-25 00:19:13 +01:00
|
|
|
if (init_ddl_log())
|
|
|
|
{
|
|
|
|
DBUG_RETURN(TRUE);
|
|
|
|
}
|
2006-02-09 20:20:21 +01:00
|
|
|
if (!complete)
|
|
|
|
{
|
2006-04-01 16:37:36 +02:00
|
|
|
/*
|
|
|
|
We haven't synched the log entries yet, we synch them now before
|
|
|
|
writing the execute entry. If complete is true we haven't written
|
|
|
|
any log entries before, we are only here to write the execute
|
|
|
|
entry to indicate it is done.
|
|
|
|
*/
|
2006-03-25 00:19:13 +01:00
|
|
|
VOID(sync_ddl_log());
|
2006-04-20 02:04:00 +02:00
|
|
|
file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= (char)DDL_LOG_EXECUTE_CODE;
|
2006-02-09 20:20:21 +01:00
|
|
|
}
|
|
|
|
else
|
2006-04-20 02:04:00 +02:00
|
|
|
file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= (char)DDL_IGNORE_LOG_ENTRY_CODE;
|
2006-03-25 00:19:13 +01:00
|
|
|
file_entry_buf[DDL_LOG_ACTION_TYPE_POS]= 0; /* Ignored for execute entries */
|
|
|
|
file_entry_buf[DDL_LOG_PHASE_POS]= 0;
|
|
|
|
int4store(&file_entry_buf[DDL_LOG_NEXT_ENTRY_POS], first_entry);
|
|
|
|
file_entry_buf[DDL_LOG_NAME_POS]= 0;
|
|
|
|
file_entry_buf[DDL_LOG_NAME_POS + FN_LEN]= 0;
|
|
|
|
file_entry_buf[DDL_LOG_NAME_POS + 2*FN_LEN]= 0;
|
2006-02-09 20:13:22 +01:00
|
|
|
if (!(*active_entry))
|
2006-02-08 15:14:15 +01:00
|
|
|
{
|
2006-03-25 00:19:13 +01:00
|
|
|
if (get_free_ddl_log_entry(active_entry, &write_header))
|
2006-02-09 20:13:22 +01:00
|
|
|
{
|
|
|
|
DBUG_RETURN(TRUE);
|
|
|
|
}
|
2006-02-08 15:14:15 +01:00
|
|
|
}
|
2006-03-25 00:19:13 +01:00
|
|
|
if (write_ddl_log_file_entry((*active_entry)->entry_pos))
|
2006-02-08 15:14:15 +01:00
|
|
|
{
|
2006-04-01 16:37:36 +02:00
|
|
|
sql_print_error("Error writing execute entry in ddl log");
|
2006-03-25 00:19:13 +01:00
|
|
|
release_ddl_log_memory_entry(*active_entry);
|
2006-02-08 15:14:15 +01:00
|
|
|
DBUG_RETURN(TRUE);
|
|
|
|
}
|
2006-03-25 00:19:13 +01:00
|
|
|
VOID(sync_ddl_log());
|
2006-02-09 00:04:58 +01:00
|
|
|
if (write_header)
|
|
|
|
{
|
2006-03-25 00:19:13 +01:00
|
|
|
if (write_ddl_log_header())
|
2006-02-09 00:04:58 +01:00
|
|
|
{
|
2006-03-25 00:19:13 +01:00
|
|
|
release_ddl_log_memory_entry(*active_entry);
|
2006-02-09 00:04:58 +01:00
|
|
|
DBUG_RETURN(TRUE);
|
|
|
|
}
|
|
|
|
}
|
2006-02-07 10:45:07 +01:00
|
|
|
DBUG_RETURN(FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-02-10 22:36:01 +01:00
|
|
|
/*
|
2006-03-25 00:19:13 +01:00
|
|
|
For complex rename operations we need to deactivate individual entries.
|
2006-02-10 22:36:01 +01:00
|
|
|
SYNOPSIS
|
2006-03-25 00:19:13 +01:00
|
|
|
deactivate_ddl_log_entry()
|
2006-02-10 22:36:01 +01:00
|
|
|
entry_no Entry position of record to change
|
|
|
|
RETURN VALUES
|
|
|
|
TRUE Error
|
|
|
|
FALSE Success
|
|
|
|
DESCRIPTION
|
|
|
|
During replace operations where we start with an existing table called
|
|
|
|
t1 and a replacement table called t1#temp or something else and where
|
|
|
|
we want to delete t1 and rename t1#temp to t1 this is not possible to
|
2006-03-25 00:19:13 +01:00
|
|
|
do in a safe manner unless the ddl log is informed of the phases in
|
2006-02-10 22:36:01 +01:00
|
|
|
the change.
|
|
|
|
|
|
|
|
Delete actions are 1-phase actions that can be ignored immediately after
|
|
|
|
being executed.
|
|
|
|
Rename actions from x to y is also a 1-phase action since there is no
|
|
|
|
interaction with any other handlers named x and y.
|
|
|
|
Replace action where drop y and x -> y happens needs to be a two-phase
|
|
|
|
action. Thus the first phase will drop y and the second phase will
|
|
|
|
rename x -> y.
|
|
|
|
*/
|
|
|
|
|
2006-03-25 00:19:13 +01:00
|
|
|
bool deactivate_ddl_log_entry(uint entry_no)
|
2006-02-10 22:36:01 +01:00
|
|
|
{
|
2006-03-25 00:19:13 +01:00
|
|
|
char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
|
|
|
|
DBUG_ENTER("deactivate_ddl_log_entry");
|
2006-02-10 22:36:01 +01:00
|
|
|
|
2006-03-25 00:19:13 +01:00
|
|
|
if (!read_ddl_log_file_entry(entry_no))
|
2006-02-10 22:36:01 +01:00
|
|
|
{
|
2006-03-25 00:19:13 +01:00
|
|
|
if (file_entry_buf[DDL_LOG_ENTRY_TYPE_POS] == DDL_LOG_ENTRY_CODE)
|
2006-02-10 22:36:01 +01:00
|
|
|
{
|
2006-03-25 00:19:13 +01:00
|
|
|
if (file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_DELETE_ACTION ||
|
|
|
|
file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_RENAME_ACTION ||
|
|
|
|
(file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_REPLACE_ACTION &&
|
|
|
|
file_entry_buf[DDL_LOG_PHASE_POS] == 1))
|
|
|
|
file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= DDL_IGNORE_LOG_ENTRY_CODE;
|
|
|
|
else if (file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_REPLACE_ACTION)
|
2006-02-10 22:36:01 +01:00
|
|
|
{
|
2006-03-25 00:19:13 +01:00
|
|
|
DBUG_ASSERT(file_entry_buf[DDL_LOG_PHASE_POS] == 0);
|
|
|
|
file_entry_buf[DDL_LOG_PHASE_POS]= 1;
|
2006-02-10 22:36:01 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DBUG_ASSERT(0);
|
|
|
|
}
|
2006-04-01 16:37:36 +02:00
|
|
|
if (write_ddl_log_file_entry(entry_no))
|
|
|
|
{
|
|
|
|
sql_print_error("Error in deactivating log entry. Position = %u",
|
|
|
|
entry_no);
|
|
|
|
DBUG_RETURN(TRUE);
|
|
|
|
}
|
2006-02-10 22:36:01 +01:00
|
|
|
}
|
|
|
|
}
|
2006-04-01 16:37:36 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
sql_print_error("Failed in reading entry before deactivating it");
|
|
|
|
DBUG_RETURN(TRUE);
|
|
|
|
}
|
|
|
|
DBUG_RETURN(FALSE);
|
2006-02-10 22:36:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2006-03-25 00:19:13 +01:00
|
|
|
Sync ddl log file
|
2006-02-10 22:36:01 +01:00
|
|
|
SYNOPSIS
|
2006-03-25 00:19:13 +01:00
|
|
|
sync_ddl_log()
|
2006-02-10 22:36:01 +01:00
|
|
|
RETURN VALUES
|
|
|
|
TRUE Error
|
|
|
|
FALSE Success
|
|
|
|
*/
|
|
|
|
|
2006-03-25 00:19:13 +01:00
|
|
|
bool sync_ddl_log()
|
2006-02-10 22:36:01 +01:00
|
|
|
{
|
|
|
|
bool error= FALSE;
|
2006-03-25 00:19:13 +01:00
|
|
|
DBUG_ENTER("sync_ddl_log");
|
2006-02-10 22:36:01 +01:00
|
|
|
|
2006-04-01 16:37:36 +02:00
|
|
|
if ((!global_ddl_log.recovery_phase) &&
|
|
|
|
init_ddl_log())
|
2006-03-25 00:19:13 +01:00
|
|
|
{
|
|
|
|
DBUG_RETURN(TRUE);
|
|
|
|
}
|
|
|
|
if (my_sync(global_ddl_log.file_id, MYF(0)))
|
2006-02-10 22:36:01 +01:00
|
|
|
{
|
|
|
|
/* Write to error log */
|
2006-04-01 16:37:36 +02:00
|
|
|
sql_print_error("Failed to sync ddl log");
|
2006-02-10 22:36:01 +01:00
|
|
|
error= TRUE;
|
|
|
|
}
|
|
|
|
DBUG_RETURN(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-02-08 15:14:15 +01:00
|
|
|
/*
|
|
|
|
Release a log memory entry
|
|
|
|
SYNOPSIS
|
2006-03-25 00:19:13 +01:00
|
|
|
release_ddl_log_memory_entry()
|
2006-02-08 15:14:15 +01:00
|
|
|
log_memory_entry Log memory entry to release
|
|
|
|
RETURN VALUES
|
|
|
|
NONE
|
|
|
|
*/
|
|
|
|
|
2006-03-25 00:19:13 +01:00
|
|
|
void release_ddl_log_memory_entry(DDL_LOG_MEMORY_ENTRY *log_entry)
|
2006-02-08 15:14:15 +01:00
|
|
|
{
|
2006-03-25 00:19:13 +01:00
|
|
|
DDL_LOG_MEMORY_ENTRY *first_free= global_ddl_log.first_free;
|
|
|
|
DDL_LOG_MEMORY_ENTRY *next_log_entry= log_entry->next_log_entry;
|
|
|
|
DDL_LOG_MEMORY_ENTRY *prev_log_entry= log_entry->prev_log_entry;
|
|
|
|
DBUG_ENTER("release_ddl_log_memory_entry");
|
2006-02-08 15:14:15 +01:00
|
|
|
|
2006-03-25 00:19:13 +01:00
|
|
|
global_ddl_log.first_free= log_entry;
|
2006-02-08 15:14:15 +01:00
|
|
|
log_entry->next_log_entry= first_free;
|
|
|
|
|
|
|
|
if (prev_log_entry)
|
|
|
|
prev_log_entry->next_log_entry= next_log_entry;
|
|
|
|
else
|
2006-03-25 00:19:13 +01:00
|
|
|
global_ddl_log.first_used= next_log_entry;
|
2006-02-08 15:14:15 +01:00
|
|
|
if (next_log_entry)
|
|
|
|
next_log_entry->prev_log_entry= prev_log_entry;
|
2006-02-09 00:04:58 +01:00
|
|
|
DBUG_VOID_RETURN;
|
2006-02-08 15:14:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-02-07 10:45:07 +01:00
|
|
|
/*
|
2006-03-25 00:19:13 +01:00
|
|
|
Execute one entry in the ddl log. Executing an entry means executing
|
2006-02-07 10:45:07 +01:00
|
|
|
a linked list of actions.
|
|
|
|
SYNOPSIS
|
2006-03-25 00:19:13 +01:00
|
|
|
execute_ddl_log_entry()
|
2006-02-07 10:45:07 +01:00
|
|
|
first_entry Reference to first action in entry
|
|
|
|
RETURN VALUES
|
|
|
|
TRUE Error
|
|
|
|
FALSE Success
|
|
|
|
*/
|
|
|
|
|
2006-04-01 16:37:36 +02:00
|
|
|
bool execute_ddl_log_entry(THD *thd, uint first_entry)
|
2006-02-07 10:45:07 +01:00
|
|
|
{
|
2006-03-25 00:19:13 +01:00
|
|
|
DDL_LOG_ENTRY ddl_log_entry;
|
2006-02-07 10:45:07 +01:00
|
|
|
uint read_entry= first_entry;
|
2006-03-25 00:19:13 +01:00
|
|
|
DBUG_ENTER("execute_ddl_log_entry");
|
2006-02-07 10:45:07 +01:00
|
|
|
|
2006-04-16 03:49:13 +02:00
|
|
|
pthread_mutex_lock(&LOCK_gdl);
|
2006-02-07 10:45:07 +01:00
|
|
|
do
|
|
|
|
{
|
2006-03-25 00:19:13 +01:00
|
|
|
if (read_ddl_log_entry(read_entry, &ddl_log_entry))
|
2006-02-08 15:14:15 +01:00
|
|
|
{
|
|
|
|
/* Write to error log and continue with next log entry */
|
2006-04-01 16:37:36 +02:00
|
|
|
sql_print_error("Failed to read entry = %u from ddl log",
|
|
|
|
read_entry);
|
2006-02-08 15:14:15 +01:00
|
|
|
break;
|
|
|
|
}
|
2006-03-25 00:19:13 +01:00
|
|
|
DBUG_ASSERT(ddl_log_entry.entry_type == DDL_LOG_ENTRY_CODE ||
|
|
|
|
ddl_log_entry.entry_type == DDL_IGNORE_LOG_ENTRY_CODE);
|
2006-02-14 11:08:58 +01:00
|
|
|
|
2006-04-01 16:37:36 +02:00
|
|
|
if (execute_ddl_log_action(thd, &ddl_log_entry))
|
2006-02-07 10:45:07 +01:00
|
|
|
{
|
2006-02-08 15:14:15 +01:00
|
|
|
/* Write to error log and continue with next log entry */
|
2006-04-01 16:37:36 +02:00
|
|
|
sql_print_error("Failed to execute action for entry = %u from ddl log",
|
|
|
|
read_entry);
|
2006-02-08 15:14:15 +01:00
|
|
|
break;
|
2006-02-07 10:45:07 +01:00
|
|
|
}
|
2006-03-25 00:19:13 +01:00
|
|
|
read_entry= ddl_log_entry.next_entry;
|
2006-02-07 10:45:07 +01:00
|
|
|
} while (read_entry);
|
2006-04-16 03:49:13 +02:00
|
|
|
pthread_mutex_unlock(&LOCK_gdl);
|
2006-02-07 10:45:07 +01:00
|
|
|
DBUG_RETURN(FALSE);
|
|
|
|
}
|
|
|
|
|
2006-02-14 11:08:58 +01:00
|
|
|
|
2006-02-07 10:45:07 +01:00
|
|
|
/*
|
2006-03-25 00:19:13 +01:00
|
|
|
Execute the ddl log at recovery of MySQL Server
|
2006-02-07 10:45:07 +01:00
|
|
|
SYNOPSIS
|
2006-03-25 00:19:13 +01:00
|
|
|
execute_ddl_log_recovery()
|
2006-02-07 10:45:07 +01:00
|
|
|
RETURN VALUES
|
|
|
|
NONE
|
|
|
|
*/
|
|
|
|
|
2006-03-25 00:19:13 +01:00
|
|
|
void execute_ddl_log_recovery()
|
2006-02-07 10:45:07 +01:00
|
|
|
{
|
2006-03-25 00:19:13 +01:00
|
|
|
uint num_entries, i;
|
2006-04-03 18:26:35 +02:00
|
|
|
THD *thd;
|
2006-03-25 00:19:13 +01:00
|
|
|
DDL_LOG_ENTRY ddl_log_entry;
|
2006-04-03 18:26:35 +02:00
|
|
|
char file_name[FN_REFLEN];
|
2006-03-25 00:19:13 +01:00
|
|
|
DBUG_ENTER("execute_ddl_log_recovery");
|
2006-02-07 10:45:07 +01:00
|
|
|
|
2006-04-25 23:33:54 +02:00
|
|
|
/*
|
|
|
|
Initialise global_ddl_log struct
|
|
|
|
*/
|
|
|
|
bzero(global_ddl_log.file_entry_buf, sizeof(global_ddl_log.file_entry_buf));
|
|
|
|
global_ddl_log.inited= FALSE;
|
|
|
|
global_ddl_log.recovery_phase= TRUE;
|
|
|
|
global_ddl_log.io_size= IO_SIZE;
|
2006-05-04 05:28:24 +02:00
|
|
|
global_ddl_log.file_id= (File) -1;
|
2006-04-25 23:33:54 +02:00
|
|
|
|
2006-04-01 16:37:36 +02:00
|
|
|
/*
|
|
|
|
To be able to run this from boot, we allocate a temporary THD
|
|
|
|
*/
|
|
|
|
if (!(thd=new THD))
|
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
thd->thread_stack= (char*) &thd;
|
|
|
|
thd->store_globals();
|
|
|
|
|
2006-03-25 00:19:13 +01:00
|
|
|
num_entries= read_ddl_log_header();
|
2006-04-20 02:04:00 +02:00
|
|
|
for (i= 1; i < num_entries + 1; i++)
|
2006-02-07 10:45:07 +01:00
|
|
|
{
|
2006-03-25 00:19:13 +01:00
|
|
|
if (read_ddl_log_entry(i, &ddl_log_entry))
|
2006-02-08 15:14:15 +01:00
|
|
|
{
|
2006-04-01 16:37:36 +02:00
|
|
|
sql_print_error("Failed to read entry no = %u from ddl log",
|
|
|
|
i);
|
|
|
|
continue;
|
2006-02-08 15:14:15 +01:00
|
|
|
}
|
2006-03-25 00:19:13 +01:00
|
|
|
if (ddl_log_entry.entry_type == DDL_LOG_EXECUTE_CODE)
|
2006-02-07 10:45:07 +01:00
|
|
|
{
|
2006-04-03 18:26:35 +02:00
|
|
|
if (execute_ddl_log_entry(thd, ddl_log_entry.next_entry))
|
2006-02-07 10:45:07 +01:00
|
|
|
{
|
2006-04-01 16:37:36 +02:00
|
|
|
/* Real unpleasant scenario but we continue anyways. */
|
|
|
|
continue;
|
2006-02-07 10:45:07 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2006-04-01 16:37:36 +02:00
|
|
|
create_ddl_log_file_name(file_name);
|
|
|
|
VOID(my_delete(file_name, MYF(0)));
|
|
|
|
global_ddl_log.recovery_phase= FALSE;
|
|
|
|
delete thd;
|
|
|
|
/* Remember that we don't have a THD */
|
|
|
|
my_pthread_setspecific_ptr(THR_THD, 0);
|
2006-02-09 00:04:58 +01:00
|
|
|
DBUG_VOID_RETURN;
|
2006-02-08 15:14:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2006-03-25 00:19:13 +01:00
|
|
|
Release all memory allocated to the ddl log
|
2006-02-08 15:14:15 +01:00
|
|
|
SYNOPSIS
|
2006-03-25 00:19:13 +01:00
|
|
|
release_ddl_log()
|
2006-02-08 15:14:15 +01:00
|
|
|
RETURN VALUES
|
|
|
|
NONE
|
|
|
|
*/
|
|
|
|
|
2006-03-25 00:19:13 +01:00
|
|
|
void release_ddl_log()
|
2006-02-08 15:14:15 +01:00
|
|
|
{
|
2006-03-25 00:19:13 +01:00
|
|
|
DDL_LOG_MEMORY_ENTRY *free_list= global_ddl_log.first_free;
|
|
|
|
DDL_LOG_MEMORY_ENTRY *used_list= global_ddl_log.first_used;
|
|
|
|
DBUG_ENTER("release_ddl_log");
|
2006-02-08 15:14:15 +01:00
|
|
|
|
2006-04-16 03:49:13 +02:00
|
|
|
pthread_mutex_lock(&LOCK_gdl);
|
2006-02-08 15:14:15 +01:00
|
|
|
while (used_list)
|
|
|
|
{
|
2006-03-25 00:19:13 +01:00
|
|
|
DDL_LOG_MEMORY_ENTRY *tmp= used_list->next_log_entry;
|
2006-02-09 00:04:58 +01:00
|
|
|
my_free((char*)used_list, MYF(0));
|
2006-02-10 19:33:08 +01:00
|
|
|
used_list= tmp;
|
2006-02-08 15:14:15 +01:00
|
|
|
}
|
|
|
|
while (free_list)
|
|
|
|
{
|
2006-03-25 00:19:13 +01:00
|
|
|
DDL_LOG_MEMORY_ENTRY *tmp= free_list->next_log_entry;
|
2006-02-09 00:04:58 +01:00
|
|
|
my_free((char*)free_list, MYF(0));
|
2006-02-10 19:33:08 +01:00
|
|
|
free_list= tmp;
|
2006-02-08 15:14:15 +01:00
|
|
|
}
|
2006-05-04 05:28:24 +02:00
|
|
|
if (global_ddl_log.file_id >= 0)
|
2006-05-03 18:40:52 +02:00
|
|
|
{
|
|
|
|
VOID(my_close(global_ddl_log.file_id, MYF(MY_WME)));
|
2006-05-04 05:28:24 +02:00
|
|
|
global_ddl_log.file_id= (File) -1;
|
2006-05-03 18:40:52 +02:00
|
|
|
}
|
2006-05-04 05:28:24 +02:00
|
|
|
global_ddl_log.inited= 0;
|
2006-04-16 03:49:13 +02:00
|
|
|
pthread_mutex_unlock(&LOCK_gdl);
|
2006-03-25 00:19:13 +01:00
|
|
|
VOID(pthread_mutex_destroy(&LOCK_gdl));
|
2006-02-09 00:04:58 +01:00
|
|
|
DBUG_VOID_RETURN;
|
2006-02-08 15:14:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-02-07 10:45:07 +01:00
|
|
|
/*
|
|
|
|
---------------------------------------------------------------------------
|
|
|
|
|
2006-03-25 00:19:13 +01:00
|
|
|
END MODULE DDL log
|
2006-02-07 10:45:07 +01:00
|
|
|
--------------------
|
|
|
|
|
|
|
|
---------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
2006-01-17 08:40:00 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
SYNOPSIS
|
|
|
|
mysql_write_frm()
|
|
|
|
lpt Struct carrying many parameters needed for this
|
|
|
|
method
|
|
|
|
flags Flags as defined below
|
|
|
|
WFRM_INITIAL_WRITE If set we need to prepare table before
|
|
|
|
creating the frm file
|
|
|
|
WFRM_CREATE_HANDLER_FILES If set we need to create the handler file as
|
|
|
|
part of the creation of the frm file
|
|
|
|
WFRM_PACK_FRM If set we should pack the frm file and delete
|
|
|
|
the frm file
|
|
|
|
|
|
|
|
RETURN VALUES
|
|
|
|
TRUE Error
|
|
|
|
FALSE Success
|
|
|
|
|
|
|
|
DESCRIPTION
|
|
|
|
A support method that creates a new frm file and in this process it
|
|
|
|
regenerates the partition data. It works fine also for non-partitioned
|
|
|
|
tables since it only handles partitioned data if it exists.
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
Prepare table to prepare for writing a new frm file where the
|
|
|
|
partitions in add/drop state have temporarily changed their state
|
|
|
|
We set tmp_table to avoid get errors on naming of primary key index.
|
|
|
|
*/
|
|
|
|
int error= 0;
|
|
|
|
char path[FN_REFLEN+1];
|
2006-02-01 10:06:07 +01:00
|
|
|
char shadow_path[FN_REFLEN+1];
|
|
|
|
char shadow_frm_name[FN_REFLEN+1];
|
2006-01-17 08:40:00 +01:00
|
|
|
char frm_name[FN_REFLEN+1];
|
|
|
|
DBUG_ENTER("mysql_write_frm");
|
|
|
|
|
2006-02-01 10:06:07 +01:00
|
|
|
/*
|
|
|
|
Build shadow frm file name
|
|
|
|
*/
|
2006-02-03 18:05:29 +01:00
|
|
|
build_table_filename(shadow_path, sizeof(shadow_path), lpt->db,
|
2006-02-01 10:06:07 +01:00
|
|
|
lpt->table_name, "#");
|
2006-02-03 18:05:29 +01:00
|
|
|
strxmov(shadow_frm_name, shadow_path, reg_ext, NullS);
|
2006-02-01 10:06:07 +01:00
|
|
|
if (flags & WFRM_WRITE_SHADOW)
|
2006-01-17 08:40:00 +01:00
|
|
|
{
|
2006-02-01 10:06:07 +01:00
|
|
|
if (mysql_copy_create_list(lpt->create_list,
|
|
|
|
&lpt->new_create_list) ||
|
|
|
|
mysql_copy_key_list(lpt->key_list,
|
|
|
|
&lpt->new_key_list) ||
|
|
|
|
mysql_prepare_table(lpt->thd, lpt->create_info,
|
|
|
|
&lpt->new_create_list,
|
|
|
|
&lpt->new_key_list,
|
|
|
|
/*tmp_table*/ 1,
|
|
|
|
&lpt->db_options,
|
|
|
|
lpt->table->file,
|
|
|
|
&lpt->key_info_buffer,
|
|
|
|
&lpt->key_count,
|
2006-02-03 18:05:29 +01:00
|
|
|
/*select_field_count*/ 0))
|
2006-01-17 08:40:00 +01:00
|
|
|
{
|
|
|
|
DBUG_RETURN(TRUE);
|
|
|
|
}
|
|
|
|
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
|
|
|
{
|
2006-02-01 10:06:07 +01:00
|
|
|
partition_info *part_info= lpt->table->part_info;
|
|
|
|
char *part_syntax_buf;
|
|
|
|
uint syntax_len;
|
|
|
|
|
|
|
|
if (part_info)
|
2006-01-17 08:40:00 +01:00
|
|
|
{
|
2006-02-01 10:06:07 +01:00
|
|
|
if (!(part_syntax_buf= generate_partition_syntax(part_info,
|
|
|
|
&syntax_len,
|
2006-06-12 20:42:07 +02:00
|
|
|
TRUE, TRUE)))
|
2006-02-01 10:06:07 +01:00
|
|
|
{
|
|
|
|
DBUG_RETURN(TRUE);
|
|
|
|
}
|
|
|
|
part_info->part_info_string= part_syntax_buf;
|
|
|
|
part_info->part_info_len= syntax_len;
|
2006-01-17 08:40:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
2006-02-01 10:06:07 +01:00
|
|
|
/* Write shadow frm file */
|
|
|
|
lpt->create_info->table_options= lpt->db_options;
|
|
|
|
if ((mysql_create_frm(lpt->thd, shadow_frm_name, lpt->db,
|
|
|
|
lpt->table_name, lpt->create_info,
|
|
|
|
lpt->new_create_list, lpt->key_count,
|
|
|
|
lpt->key_info_buffer, lpt->table->file)) ||
|
2006-04-20 03:43:30 +02:00
|
|
|
lpt->table->file->create_handler_files(shadow_path, NULL,
|
|
|
|
CHF_CREATE_FLAG,
|
2006-04-20 03:22:35 +02:00
|
|
|
lpt->create_info))
|
2006-02-01 10:06:07 +01:00
|
|
|
{
|
|
|
|
my_delete(shadow_frm_name, MYF(0));
|
|
|
|
error= 1;
|
|
|
|
goto end;
|
|
|
|
}
|
2006-01-17 08:40:00 +01:00
|
|
|
}
|
|
|
|
if (flags & WFRM_PACK_FRM)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
We need to pack the frm file and after packing it we delete the
|
|
|
|
frm file to ensure it doesn't get used. This is only used for
|
|
|
|
handlers that have the main version of the frm file stored in the
|
|
|
|
handler.
|
|
|
|
*/
|
|
|
|
const void *data= 0;
|
|
|
|
uint length= 0;
|
2006-02-01 10:06:07 +01:00
|
|
|
if (readfrm(shadow_path, &data, &length) ||
|
2006-01-17 08:40:00 +01:00
|
|
|
packfrm(data, length, &lpt->pack_frm_data, &lpt->pack_frm_len))
|
|
|
|
{
|
|
|
|
my_free((char*)data, MYF(MY_ALLOW_ZERO_PTR));
|
|
|
|
my_free((char*)lpt->pack_frm_data, MYF(MY_ALLOW_ZERO_PTR));
|
|
|
|
mem_alloc_error(length);
|
|
|
|
error= 1;
|
|
|
|
goto end;
|
|
|
|
}
|
2006-02-01 10:06:07 +01:00
|
|
|
error= my_delete(shadow_frm_name, MYF(MY_WME));
|
2006-01-17 08:40:00 +01:00
|
|
|
}
|
2006-02-01 10:06:07 +01:00
|
|
|
if (flags & WFRM_INSTALL_SHADOW)
|
|
|
|
{
|
2006-02-20 21:07:03 +01:00
|
|
|
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
|
|
|
partition_info *part_info= lpt->part_info;
|
|
|
|
#endif
|
2006-02-01 10:06:07 +01:00
|
|
|
/*
|
|
|
|
Build frm file name
|
|
|
|
*/
|
|
|
|
build_table_filename(path, sizeof(path), lpt->db,
|
|
|
|
lpt->table_name, "");
|
|
|
|
strxmov(frm_name, path, reg_ext, NullS);
|
|
|
|
/*
|
|
|
|
When we are changing to use new frm file we need to ensure that we
|
|
|
|
don't collide with another thread in process to open the frm file.
|
2006-04-18 16:58:12 +02:00
|
|
|
We start by deleting the .frm file and possible .par file. Then we
|
|
|
|
write to the DDL log that we have completed the delete phase by
|
|
|
|
increasing the phase of the log entry. Next step is to rename the
|
|
|
|
new .frm file and the new .par file to the real name. After
|
|
|
|
completing this we write a new phase to the log entry that will
|
|
|
|
deactivate it.
|
2006-02-01 10:06:07 +01:00
|
|
|
*/
|
|
|
|
VOID(pthread_mutex_lock(&LOCK_open));
|
|
|
|
if (my_delete(frm_name, MYF(MY_WME)) ||
|
2006-02-20 21:07:03 +01:00
|
|
|
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
2006-04-16 03:49:13 +02:00
|
|
|
lpt->table->file->create_handler_files(path, shadow_path,
|
2006-04-20 03:43:30 +02:00
|
|
|
CHF_DELETE_FLAG, NULL) ||
|
2006-03-25 00:19:13 +01:00
|
|
|
deactivate_ddl_log_entry(part_info->frm_log_entry->entry_pos) ||
|
|
|
|
(sync_ddl_log(), FALSE) ||
|
2006-02-20 21:07:03 +01:00
|
|
|
#endif
|
2006-04-18 16:58:12 +02:00
|
|
|
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
2006-02-01 10:06:07 +01:00
|
|
|
my_rename(shadow_frm_name, frm_name, MYF(MY_WME)) ||
|
2006-04-16 03:49:13 +02:00
|
|
|
lpt->table->file->create_handler_files(path, shadow_path,
|
2006-04-20 03:43:30 +02:00
|
|
|
CHF_RENAME_FLAG, NULL))
|
2006-04-18 16:58:12 +02:00
|
|
|
#else
|
|
|
|
my_rename(shadow_frm_name, frm_name, MYF(MY_WME)))
|
|
|
|
#endif
|
2006-02-01 10:06:07 +01:00
|
|
|
{
|
|
|
|
error= 1;
|
|
|
|
}
|
|
|
|
VOID(pthread_mutex_unlock(&LOCK_open));
|
2006-02-20 21:07:03 +01:00
|
|
|
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
2006-03-25 00:19:13 +01:00
|
|
|
deactivate_ddl_log_entry(part_info->frm_log_entry->entry_pos);
|
2006-02-13 13:52:23 +01:00
|
|
|
part_info->frm_log_entry= NULL;
|
2006-03-25 00:19:13 +01:00
|
|
|
VOID(sync_ddl_log());
|
2006-02-20 21:07:03 +01:00
|
|
|
#endif
|
2006-01-17 08:40:00 +01:00
|
|
|
}
|
2006-02-01 10:06:07 +01:00
|
|
|
|
2006-01-17 08:40:00 +01:00
|
|
|
end:
|
|
|
|
DBUG_RETURN(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
SYNOPSIS
|
|
|
|
write_bin_log()
|
|
|
|
thd Thread object
|
|
|
|
clear_error is clear_error to be called
|
|
|
|
query Query to log
|
|
|
|
query_length Length of query
|
|
|
|
|
|
|
|
RETURN VALUES
|
|
|
|
NONE
|
|
|
|
|
|
|
|
DESCRIPTION
|
|
|
|
Write the binlog if open, routine used in multiple places in this
|
|
|
|
file
|
|
|
|
*/
|
|
|
|
|
|
|
|
void write_bin_log(THD *thd, bool clear_error,
|
|
|
|
char const *query, ulong query_length)
|
|
|
|
{
|
|
|
|
if (mysql_bin_log.is_open())
|
|
|
|
{
|
|
|
|
if (clear_error)
|
|
|
|
thd->clear_error();
|
|
|
|
thd->binlog_query(THD::STMT_QUERY_TYPE,
|
|
|
|
query, query_length, FALSE, FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-05-26 05:26:40 +02:00
|
|
|
|
2003-01-29 18:10:33 +01:00
|
|
|
/*
|
|
|
|
delete (drop) tables.
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
mysql_rm_table()
|
|
|
|
thd Thread handle
|
|
|
|
tables List of tables to delete
|
|
|
|
if_exists If 1, don't give error if one table doesn't exists
|
|
|
|
|
|
|
|
NOTES
|
|
|
|
Will delete all tables that can be deleted and give a compact error
|
|
|
|
messages for tables that could not be deleted.
|
|
|
|
If a table is in use, we will wait for all users to free the table
|
|
|
|
before dropping it
|
|
|
|
|
|
|
|
Wait if global_read_lock (FLUSH TABLES WITH READ LOCK) is set.
|
|
|
|
|
|
|
|
RETURN
|
2004-10-20 03:04:37 +02:00
|
|
|
FALSE OK. In this case ok packet is sent to user
|
|
|
|
TRUE Error
|
2003-01-29 18:10:33 +01:00
|
|
|
|
|
|
|
*/
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2004-10-20 03:04:37 +02:00
|
|
|
bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
|
|
|
|
my_bool drop_temporary)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2005-03-16 08:40:19 +01:00
|
|
|
bool error= FALSE, need_start_waiters= FALSE;
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_ENTER("mysql_rm_table");
|
|
|
|
|
|
|
|
/* mark for close and remove all cached entries */
|
|
|
|
|
2005-03-16 08:40:19 +01:00
|
|
|
if (!drop_temporary)
|
2000-11-18 01:15:06 +01:00
|
|
|
{
|
2005-03-16 08:40:19 +01:00
|
|
|
if ((error= wait_if_global_read_lock(thd, 0, 1)))
|
2000-11-04 21:58:07 +01:00
|
|
|
{
|
2005-01-06 12:00:13 +01:00
|
|
|
my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0), tables->table_name);
|
2005-11-15 21:57:02 +01:00
|
|
|
DBUG_RETURN(TRUE);
|
2000-11-18 01:15:06 +01:00
|
|
|
}
|
2005-03-16 08:40:19 +01:00
|
|
|
else
|
|
|
|
need_start_waiters= TRUE;
|
2000-11-18 01:15:06 +01:00
|
|
|
}
|
2005-11-15 21:57:02 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
Acquire LOCK_open after wait_if_global_read_lock(). If we would hold
|
|
|
|
LOCK_open during wait_if_global_read_lock(), other threads could not
|
|
|
|
close their tables. This would make a pretty deadlock.
|
|
|
|
*/
|
|
|
|
thd->mysys_var->current_mutex= &LOCK_open;
|
|
|
|
thd->mysys_var->current_cond= &COND_refresh;
|
|
|
|
VOID(pthread_mutex_lock(&LOCK_open));
|
|
|
|
|
2004-07-16 00:15:55 +02:00
|
|
|
error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 0, 0);
|
2001-09-02 12:47:00 +02:00
|
|
|
|
|
|
|
pthread_mutex_unlock(&LOCK_open);
|
|
|
|
|
|
|
|
pthread_mutex_lock(&thd->mysys_var->mutex);
|
|
|
|
thd->mysys_var->current_mutex= 0;
|
|
|
|
thd->mysys_var->current_cond= 0;
|
|
|
|
pthread_mutex_unlock(&thd->mysys_var->mutex);
|
|
|
|
|
2005-03-16 08:40:19 +01:00
|
|
|
if (need_start_waiters)
|
|
|
|
start_waiting_global_read_lock(thd);
|
|
|
|
|
2001-09-02 12:47:00 +02:00
|
|
|
if (error)
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
2002-10-02 12:33:08 +02:00
|
|
|
send_ok(thd);
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(FALSE);
|
2001-09-02 12:47:00 +02:00
|
|
|
}
|
|
|
|
|
2003-01-29 18:10:33 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
delete (drop) tables.
|
|
|
|
|
|
|
|
SYNOPSIS
|
2005-02-21 19:41:48 +01:00
|
|
|
mysql_rm_table_part2_with_lock()
|
|
|
|
thd Thread handle
|
|
|
|
tables List of tables to delete
|
|
|
|
if_exists If 1, don't give error if one table doesn't exists
|
|
|
|
dont_log_query Don't write query to log files. This will also not
|
2005-03-16 08:40:19 +01:00
|
|
|
generate warnings if the handler files doesn't exists
|
2003-01-29 18:10:33 +01:00
|
|
|
|
|
|
|
NOTES
|
|
|
|
Works like documented in mysql_rm_table(), but don't check
|
|
|
|
global_read_lock and don't send_ok packet to server.
|
|
|
|
|
|
|
|
RETURN
|
|
|
|
0 ok
|
|
|
|
1 error
|
|
|
|
*/
|
|
|
|
|
2002-07-23 17:31:22 +02:00
|
|
|
int mysql_rm_table_part2_with_lock(THD *thd,
|
|
|
|
TABLE_LIST *tables, bool if_exists,
|
2003-01-04 14:37:20 +01:00
|
|
|
bool drop_temporary, bool dont_log_query)
|
2002-07-23 17:31:22 +02:00
|
|
|
{
|
|
|
|
int error;
|
|
|
|
thd->mysys_var->current_mutex= &LOCK_open;
|
|
|
|
thd->mysys_var->current_cond= &COND_refresh;
|
|
|
|
VOID(pthread_mutex_lock(&LOCK_open));
|
|
|
|
|
2004-07-16 00:15:55 +02:00
|
|
|
error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 1,
|
|
|
|
dont_log_query);
|
2002-07-23 17:31:22 +02:00
|
|
|
|
|
|
|
pthread_mutex_unlock(&LOCK_open);
|
|
|
|
|
|
|
|
pthread_mutex_lock(&thd->mysys_var->mutex);
|
|
|
|
thd->mysys_var->current_mutex= 0;
|
|
|
|
thd->mysys_var->current_cond= 0;
|
|
|
|
pthread_mutex_unlock(&thd->mysys_var->mutex);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2003-01-29 18:10:33 +01:00
|
|
|
|
2002-11-07 03:02:37 +01:00
|
|
|
/*
|
2003-01-04 14:37:20 +01:00
|
|
|
Execute the drop of a normal or temporary table
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
mysql_rm_table_part2()
|
|
|
|
thd Thread handler
|
|
|
|
tables Tables to drop
|
|
|
|
if_exists If set, don't give an error if table doesn't exists.
|
|
|
|
In this case we give an warning of level 'NOTE'
|
|
|
|
drop_temporary Only drop temporary tables
|
2004-07-16 00:15:55 +02:00
|
|
|
drop_view Allow to delete VIEW .frm
|
2005-02-21 19:41:48 +01:00
|
|
|
dont_log_query Don't write query to log files. This will also not
|
|
|
|
generate warnings if the handler files doesn't exists
|
2003-01-04 14:37:20 +01:00
|
|
|
|
2002-11-07 03:02:37 +01:00
|
|
|
TODO:
|
|
|
|
When logging to the binary log, we should log
|
|
|
|
tmp_tables and transactional tables as separate statements if we
|
|
|
|
are in a transaction; This is needed to get these tables into the
|
|
|
|
cached binary log that is only written on COMMIT.
|
|
|
|
|
|
|
|
The current code only writes DROP statements that only uses temporary
|
|
|
|
tables to the cache binary log. This should be ok on most cases, but
|
|
|
|
not all.
|
2003-01-04 14:37:20 +01:00
|
|
|
|
|
|
|
RETURN
|
|
|
|
0 ok
|
|
|
|
1 Error
|
|
|
|
-1 Thread was killed
|
2002-11-07 03:02:37 +01:00
|
|
|
*/
|
2001-09-02 12:47:00 +02:00
|
|
|
|
|
|
|
int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
|
2004-07-16 00:15:55 +02:00
|
|
|
bool drop_temporary, bool drop_view,
|
|
|
|
bool dont_log_query)
|
2001-09-02 12:47:00 +02:00
|
|
|
{
|
|
|
|
TABLE_LIST *table;
|
2005-12-31 06:01:26 +01:00
|
|
|
char path[FN_REFLEN], *alias;
|
|
|
|
uint path_length;
|
2001-09-02 12:47:00 +02:00
|
|
|
String wrong_tables;
|
|
|
|
int error;
|
2005-12-22 06:39:02 +01:00
|
|
|
int non_temp_tables_count= 0;
|
2004-02-01 23:10:45 +01:00
|
|
|
bool some_tables_deleted=0, tmp_table_deleted=0, foreign_key_error=0;
|
2005-12-22 06:39:02 +01:00
|
|
|
String built_query;
|
2001-09-02 12:47:00 +02:00
|
|
|
DBUG_ENTER("mysql_rm_table_part2");
|
|
|
|
|
2006-03-29 13:27:36 +02:00
|
|
|
LINT_INIT(alias);
|
|
|
|
LINT_INIT(path_length);
|
|
|
|
safe_mutex_assert_owner(&LOCK_open);
|
|
|
|
|
WL#2977 and WL#2712 global and session-level variable to set the binlog format (row/statement),
and new binlog format called "mixed" (which is statement-based except if only row-based is correct,
in this cset it means if UDF or UUID is used; more cases could be added in later 5.1 release):
SET GLOBAL|SESSION BINLOG_FORMAT=row|statement|mixed|default;
the global default is statement unless cluster is enabled (then it's row) as in 5.1-alpha.
It's not possible to use SET on this variable if a session is currently in row-based mode and has open temporary tables (because CREATE
TEMPORARY TABLE was not binlogged so temp table is not known on slave), or if NDB is enabled (because
NDB does not support such change on-the-fly, though it will later), of if in a stored function (see below).
The added tests test the possibility or impossibility to SET, their effects, and the mixed mode,
including in prepared statements and in stored procedures and functions.
Caveats:
a) The mixed mode will not work for stored functions: in mixed mode, a stored function will
always be binlogged as one call and in a statement-based way (e.g. INSERT VALUES(myfunc()) or SELECT myfunc()).
b) for the same reason, changing the thread's binlog format inside a stored function is
refused with an error message.
c) the same problems apply to triggers; implementing b) for triggers will be done later (will ask
Dmitri).
Additionally, as the binlog format is now changeable by each user for his session, I remove the implication
which was done at startup, where row-based automatically set log-bin-trust-routine-creators to 1
(not possible anymore as a user can now switch to stmt-based and do nasty things again), and automatically
set --innodb-locks-unsafe-for-binlog to 1 (was anyway theoretically incorrect as it disabled
phantom protection).
Plus fixes for compiler warnings.
2006-02-25 22:21:03 +01:00
|
|
|
if (thd->current_stmt_binlog_row_based && !dont_log_query)
|
2005-12-22 06:39:02 +01:00
|
|
|
{
|
|
|
|
built_query.set_charset(system_charset_info);
|
|
|
|
if (if_exists)
|
|
|
|
built_query.append("DROP TABLE IF EXISTS ");
|
|
|
|
else
|
|
|
|
built_query.append("DROP TABLE ");
|
|
|
|
}
|
2005-11-23 21:45:02 +01:00
|
|
|
/*
|
|
|
|
If we have the table in the definition cache, we don't have to check the
|
|
|
|
.frm file to find if the table is a normal table (not view) and what
|
|
|
|
engine to use.
|
|
|
|
*/
|
|
|
|
|
|
|
|
for (table= tables; table; table= table->next_local)
|
|
|
|
{
|
|
|
|
TABLE_SHARE *share;
|
2005-12-21 19:18:40 +01:00
|
|
|
table->db_type= NULL;
|
2005-11-23 21:45:02 +01:00
|
|
|
if ((share= get_cached_table_share(table->db, table->table_name)))
|
|
|
|
table->db_type= share->db_type;
|
|
|
|
}
|
|
|
|
|
2003-03-03 19:42:49 +01:00
|
|
|
if (lock_table_names(thd, tables))
|
2003-03-07 00:20:56 +01:00
|
|
|
DBUG_RETURN(1);
|
2003-03-03 19:42:49 +01:00
|
|
|
|
2005-02-24 22:33:42 +01:00
|
|
|
/* Don't give warnings for not found errors, as we already generate notes */
|
|
|
|
thd->no_warnings_for_error= 1;
|
|
|
|
|
2004-07-16 00:15:55 +02:00
|
|
|
for (table= tables; table; table= table->next_local)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2003-03-04 12:36:59 +01:00
|
|
|
char *db=table->db;
|
2005-12-21 19:18:40 +01:00
|
|
|
handlerton *table_type;
|
|
|
|
enum legacy_db_type frm_db_type;
|
2005-11-03 15:10:11 +01:00
|
|
|
|
2005-11-15 21:57:02 +01:00
|
|
|
mysql_ha_flush(thd, table, MYSQL_HA_CLOSE_FINAL, TRUE);
|
2005-11-23 21:45:02 +01:00
|
|
|
if (!close_temporary_table(thd, table))
|
2000-11-18 22:13:48 +01:00
|
|
|
{
|
2002-11-07 03:02:37 +01:00
|
|
|
tmp_table_deleted=1;
|
2000-07-31 21:29:14 +02:00
|
|
|
continue; // removed temporary table
|
2000-11-18 22:13:48 +01:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2005-12-22 06:39:02 +01:00
|
|
|
/*
|
|
|
|
If row-based replication is used and the table is not a
|
|
|
|
temporary table, we add the table name to the drop statement
|
|
|
|
being built. The string always end in a comma and the comma
|
|
|
|
will be chopped off before being written to the binary log.
|
|
|
|
*/
|
WL#2977 and WL#2712 global and session-level variable to set the binlog format (row/statement),
and new binlog format called "mixed" (which is statement-based except if only row-based is correct,
in this cset it means if UDF or UUID is used; more cases could be added in later 5.1 release):
SET GLOBAL|SESSION BINLOG_FORMAT=row|statement|mixed|default;
the global default is statement unless cluster is enabled (then it's row) as in 5.1-alpha.
It's not possible to use SET on this variable if a session is currently in row-based mode and has open temporary tables (because CREATE
TEMPORARY TABLE was not binlogged so temp table is not known on slave), or if NDB is enabled (because
NDB does not support such change on-the-fly, though it will later), of if in a stored function (see below).
The added tests test the possibility or impossibility to SET, their effects, and the mixed mode,
including in prepared statements and in stored procedures and functions.
Caveats:
a) The mixed mode will not work for stored functions: in mixed mode, a stored function will
always be binlogged as one call and in a statement-based way (e.g. INSERT VALUES(myfunc()) or SELECT myfunc()).
b) for the same reason, changing the thread's binlog format inside a stored function is
refused with an error message.
c) the same problems apply to triggers; implementing b) for triggers will be done later (will ask
Dmitri).
Additionally, as the binlog format is now changeable by each user for his session, I remove the implication
which was done at startup, where row-based automatically set log-bin-trust-routine-creators to 1
(not possible anymore as a user can now switch to stmt-based and do nasty things again), and automatically
set --innodb-locks-unsafe-for-binlog to 1 (was anyway theoretically incorrect as it disabled
phantom protection).
Plus fixes for compiler warnings.
2006-02-25 22:21:03 +01:00
|
|
|
if (thd->current_stmt_binlog_row_based && !dont_log_query)
|
2005-12-22 06:39:02 +01:00
|
|
|
{
|
2006-03-29 13:27:36 +02:00
|
|
|
non_temp_tables_count++;
|
2005-12-22 06:39:02 +01:00
|
|
|
/*
|
|
|
|
Don't write the database name if it is the current one (or if
|
|
|
|
thd->db is NULL).
|
|
|
|
*/
|
|
|
|
built_query.append("`");
|
|
|
|
if (thd->db == NULL || strcmp(db,thd->db) != 0)
|
|
|
|
{
|
|
|
|
built_query.append(db);
|
|
|
|
built_query.append("`.`");
|
|
|
|
}
|
|
|
|
|
|
|
|
built_query.append(table->table_name);
|
|
|
|
built_query.append("`,");
|
|
|
|
}
|
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
error=0;
|
2005-11-23 21:45:02 +01:00
|
|
|
table_type= table->db_type;
|
2003-01-04 14:37:20 +01:00
|
|
|
if (!drop_temporary)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2006-03-29 13:27:36 +02:00
|
|
|
TABLE *locked_table;
|
2005-07-28 16:09:54 +02:00
|
|
|
abort_locked_tables(thd, db, table->table_name);
|
|
|
|
remove_table_from_cache(thd, db, table->table_name,
|
|
|
|
RTFC_WAIT_OTHER_THREAD_FLAG |
|
|
|
|
RTFC_CHECK_KILLED_FLAG);
|
2006-03-29 13:27:36 +02:00
|
|
|
/*
|
|
|
|
If the table was used in lock tables, remember it so that
|
|
|
|
unlock_table_names can free it
|
|
|
|
*/
|
|
|
|
if ((locked_table= drop_locked_tables(thd, db, table->table_name)))
|
|
|
|
table->table= locked_table;
|
|
|
|
|
2003-01-04 14:37:20 +01:00
|
|
|
if (thd->killed)
|
2005-02-24 22:33:42 +01:00
|
|
|
{
|
|
|
|
thd->no_warnings_for_error= 0;
|
2003-01-04 14:37:20 +01:00
|
|
|
DBUG_RETURN(-1);
|
2005-02-24 22:33:42 +01:00
|
|
|
}
|
2005-01-06 12:00:13 +01:00
|
|
|
alias= (lower_case_table_names == 2) ? table->alias : table->table_name;
|
2005-11-23 21:45:02 +01:00
|
|
|
/* remove .frm file and engine files */
|
2005-12-31 06:01:26 +01:00
|
|
|
path_length= build_table_filename(path, sizeof(path),
|
|
|
|
db, alias, reg_ext);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2006-03-29 13:27:36 +02:00
|
|
|
if (drop_temporary ||
|
|
|
|
(table_type == NULL &&
|
2005-11-23 21:45:02 +01:00
|
|
|
(access(path, F_OK) &&
|
|
|
|
ha_create_table_from_engine(thd, db, alias)) ||
|
|
|
|
(!drop_view &&
|
2005-12-21 19:18:40 +01:00
|
|
|
mysql_frm_type(thd, path, &frm_db_type) != FRMTYPE_TABLE)))
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2005-06-08 13:31:59 +02:00
|
|
|
// Table was not found on disk and table can't be created from engine
|
2002-12-04 12:19:08 +01:00
|
|
|
if (if_exists)
|
2003-01-06 00:48:59 +01:00
|
|
|
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
|
|
|
|
ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR),
|
2005-01-06 12:00:13 +01:00
|
|
|
table->table_name);
|
2002-12-04 12:19:08 +01:00
|
|
|
else
|
2005-06-08 13:31:59 +02:00
|
|
|
error= 1;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2000-12-08 16:04:57 +01:00
|
|
|
char *end;
|
2005-12-21 19:18:40 +01:00
|
|
|
if (table_type == NULL)
|
|
|
|
{
|
|
|
|
mysql_frm_type(thd, path, &frm_db_type);
|
|
|
|
table_type= ha_resolve_by_legacy_type(thd, frm_db_type);
|
|
|
|
}
|
2005-12-31 06:01:26 +01:00
|
|
|
// Remove extension for delete
|
|
|
|
*(end= path + path_length - reg_ext_length)= '\0';
|
2005-11-23 21:45:02 +01:00
|
|
|
error= ha_delete_table(thd, table_type, path, db, table->table_name,
|
2005-02-21 19:41:48 +01:00
|
|
|
!dont_log_query);
|
2005-10-24 15:05:06 +02:00
|
|
|
if ((error == ENOENT || error == HA_ERR_NO_SUCH_TABLE) &&
|
2005-12-21 19:18:40 +01:00
|
|
|
(if_exists || table_type == NULL))
|
2005-02-21 13:47:57 +01:00
|
|
|
error= 0;
|
2004-02-01 23:10:45 +01:00
|
|
|
if (error == HA_ERR_ROW_IS_REFERENCED)
|
2004-02-11 00:06:46 +01:00
|
|
|
{
|
|
|
|
/* the table is referenced by a foreign key constraint */
|
2004-04-08 16:56:45 +02:00
|
|
|
foreign_key_error=1;
|
2004-02-11 00:06:46 +01:00
|
|
|
}
|
2005-02-21 13:47:57 +01:00
|
|
|
if (!error || error == ENOENT || error == HA_ERR_NO_SUCH_TABLE)
|
2000-12-08 16:04:57 +01:00
|
|
|
{
|
2005-02-21 13:47:57 +01:00
|
|
|
int new_error;
|
2000-12-08 16:04:57 +01:00
|
|
|
/* Delete the table definition file */
|
|
|
|
strmov(end,reg_ext);
|
2005-02-21 13:47:57 +01:00
|
|
|
if (!(new_error=my_delete(path,MYF(MY_WME))))
|
2005-03-27 14:15:21 +02:00
|
|
|
{
|
2000-12-08 16:04:57 +01:00
|
|
|
some_tables_deleted=1;
|
2005-07-19 18:06:49 +02:00
|
|
|
new_error= Table_triggers_list::drop_all_triggers(thd, db,
|
|
|
|
table->table_name);
|
2005-03-27 14:15:21 +02:00
|
|
|
}
|
2005-02-21 13:47:57 +01:00
|
|
|
error|= new_error;
|
2005-02-17 14:22:44 +01:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
if (error)
|
|
|
|
{
|
|
|
|
if (wrong_tables.length())
|
|
|
|
wrong_tables.append(',');
|
2005-01-06 12:00:13 +01:00
|
|
|
wrong_tables.append(String(table->table_name,system_charset_info));
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
}
|
2003-02-10 16:59:16 +01:00
|
|
|
thd->tmp_table_used= tmp_table_deleted;
|
2004-06-09 16:07:01 +02:00
|
|
|
error= 0;
|
|
|
|
if (wrong_tables.length())
|
|
|
|
{
|
|
|
|
if (!foreign_key_error)
|
2005-04-02 20:13:19 +02:00
|
|
|
my_printf_error(ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR), MYF(0),
|
2005-04-03 08:36:18 +02:00
|
|
|
wrong_tables.c_ptr());
|
2004-06-09 16:07:01 +02:00
|
|
|
else
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_ROW_IS_REFERENCED, ER(ER_ROW_IS_REFERENCED), MYF(0));
|
2004-06-09 16:07:01 +02:00
|
|
|
error= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (some_tables_deleted || tmp_table_deleted || !error)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2002-03-22 21:55:08 +01:00
|
|
|
query_cache_invalidate3(thd, tables, 0);
|
2005-12-22 06:39:02 +01:00
|
|
|
if (!dont_log_query)
|
2005-08-20 22:56:54 +02:00
|
|
|
{
|
WL#2977 and WL#2712 global and session-level variable to set the binlog format (row/statement),
and new binlog format called "mixed" (which is statement-based except if only row-based is correct,
in this cset it means if UDF or UUID is used; more cases could be added in later 5.1 release):
SET GLOBAL|SESSION BINLOG_FORMAT=row|statement|mixed|default;
the global default is statement unless cluster is enabled (then it's row) as in 5.1-alpha.
It's not possible to use SET on this variable if a session is currently in row-based mode and has open temporary tables (because CREATE
TEMPORARY TABLE was not binlogged so temp table is not known on slave), or if NDB is enabled (because
NDB does not support such change on-the-fly, though it will later), of if in a stored function (see below).
The added tests test the possibility or impossibility to SET, their effects, and the mixed mode,
including in prepared statements and in stored procedures and functions.
Caveats:
a) The mixed mode will not work for stored functions: in mixed mode, a stored function will
always be binlogged as one call and in a statement-based way (e.g. INSERT VALUES(myfunc()) or SELECT myfunc()).
b) for the same reason, changing the thread's binlog format inside a stored function is
refused with an error message.
c) the same problems apply to triggers; implementing b) for triggers will be done later (will ask
Dmitri).
Additionally, as the binlog format is now changeable by each user for his session, I remove the implication
which was done at startup, where row-based automatically set log-bin-trust-routine-creators to 1
(not possible anymore as a user can now switch to stmt-based and do nasty things again), and automatically
set --innodb-locks-unsafe-for-binlog to 1 (was anyway theoretically incorrect as it disabled
phantom protection).
Plus fixes for compiler warnings.
2006-02-25 22:21:03 +01:00
|
|
|
if (!thd->current_stmt_binlog_row_based ||
|
2005-12-22 06:39:02 +01:00
|
|
|
non_temp_tables_count > 0 && !tmp_table_deleted)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
In this case, we are either using statement-based
|
|
|
|
replication or using row-based replication but have only
|
|
|
|
deleted one or more non-temporary tables (and no temporary
|
|
|
|
tables). In this case, we can write the original query into
|
|
|
|
the binary log.
|
|
|
|
*/
|
|
|
|
write_bin_log(thd, !error, thd->query, thd->query_length);
|
|
|
|
}
|
WL#2977 and WL#2712 global and session-level variable to set the binlog format (row/statement),
and new binlog format called "mixed" (which is statement-based except if only row-based is correct,
in this cset it means if UDF or UUID is used; more cases could be added in later 5.1 release):
SET GLOBAL|SESSION BINLOG_FORMAT=row|statement|mixed|default;
the global default is statement unless cluster is enabled (then it's row) as in 5.1-alpha.
It's not possible to use SET on this variable if a session is currently in row-based mode and has open temporary tables (because CREATE
TEMPORARY TABLE was not binlogged so temp table is not known on slave), or if NDB is enabled (because
NDB does not support such change on-the-fly, though it will later), of if in a stored function (see below).
The added tests test the possibility or impossibility to SET, their effects, and the mixed mode,
including in prepared statements and in stored procedures and functions.
Caveats:
a) The mixed mode will not work for stored functions: in mixed mode, a stored function will
always be binlogged as one call and in a statement-based way (e.g. INSERT VALUES(myfunc()) or SELECT myfunc()).
b) for the same reason, changing the thread's binlog format inside a stored function is
refused with an error message.
c) the same problems apply to triggers; implementing b) for triggers will be done later (will ask
Dmitri).
Additionally, as the binlog format is now changeable by each user for his session, I remove the implication
which was done at startup, where row-based automatically set log-bin-trust-routine-creators to 1
(not possible anymore as a user can now switch to stmt-based and do nasty things again), and automatically
set --innodb-locks-unsafe-for-binlog to 1 (was anyway theoretically incorrect as it disabled
phantom protection).
Plus fixes for compiler warnings.
2006-02-25 22:21:03 +01:00
|
|
|
else if (thd->current_stmt_binlog_row_based &&
|
2005-12-22 06:39:02 +01:00
|
|
|
non_temp_tables_count > 0 &&
|
|
|
|
tmp_table_deleted)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
In this case we have deleted both temporary and
|
|
|
|
non-temporary tables, so:
|
|
|
|
- since we have deleted a non-temporary table we have to
|
|
|
|
binlog the statement, but
|
|
|
|
- since we have deleted a temporary table we cannot binlog
|
|
|
|
the statement (since the table has not been created on the
|
|
|
|
slave, this might cause the slave to stop).
|
|
|
|
|
|
|
|
Instead, we write a built statement, only containing the
|
|
|
|
non-temporary tables, to the binary log
|
|
|
|
*/
|
|
|
|
built_query.chop(); // Chop of the last comma
|
|
|
|
built_query.append(" /* generated by server */");
|
|
|
|
write_bin_log(thd, !error, built_query.ptr(), built_query.length());
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
The remaining cases are:
|
|
|
|
- no tables where deleted and
|
|
|
|
- only temporary tables where deleted and row-based
|
|
|
|
replication is used.
|
|
|
|
In both these cases, nothing should be written to the binary
|
|
|
|
log.
|
|
|
|
*/
|
2005-08-20 22:56:54 +02:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2001-12-06 13:10:51 +01:00
|
|
|
|
2005-03-16 15:11:01 +01:00
|
|
|
unlock_table_names(thd, tables, (TABLE_LIST*) 0);
|
2005-02-24 22:33:42 +01:00
|
|
|
thd->no_warnings_for_error= 0;
|
2001-09-02 12:47:00 +02:00
|
|
|
DBUG_RETURN(error);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-12-21 19:18:40 +01:00
|
|
|
bool quick_rm_table(handlerton *base,const char *db,
|
2000-07-31 21:29:14 +02:00
|
|
|
const char *table_name)
|
|
|
|
{
|
|
|
|
char path[FN_REFLEN];
|
2005-11-23 21:45:02 +01:00
|
|
|
bool error= 0;
|
|
|
|
DBUG_ENTER("quick_rm_table");
|
|
|
|
|
2005-12-31 06:01:26 +01:00
|
|
|
uint path_length= build_table_filename(path, sizeof(path),
|
|
|
|
db, table_name, reg_ext);
|
2000-07-31 21:29:14 +02:00
|
|
|
if (my_delete(path,MYF(0)))
|
2005-11-23 21:45:02 +01:00
|
|
|
error= 1; /* purecov: inspected */
|
2005-12-31 06:01:26 +01:00
|
|
|
path[path_length - reg_ext_length]= '\0'; // Remove reg_ext
|
2005-11-23 21:45:02 +01:00
|
|
|
DBUG_RETURN(ha_delete_table(current_thd, base, path, db, table_name, 0) ||
|
|
|
|
error);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
2002-01-02 20:29:41 +01:00
|
|
|
/*
|
|
|
|
Sort keys in the following order:
|
|
|
|
- PRIMARY KEY
|
|
|
|
- UNIQUE keyws where all column are NOT NULL
|
|
|
|
- Other UNIQUE keys
|
|
|
|
- Normal keys
|
|
|
|
- Fulltext keys
|
|
|
|
|
|
|
|
This will make checking for duplicated keys faster and ensure that
|
|
|
|
PRIMARY keys are prioritized.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int sort_keys(KEY *a, KEY *b)
|
|
|
|
{
|
|
|
|
if (a->flags & HA_NOSAME)
|
|
|
|
{
|
|
|
|
if (!(b->flags & HA_NOSAME))
|
|
|
|
return -1;
|
2003-12-12 21:26:58 +01:00
|
|
|
if ((a->flags ^ b->flags) & (HA_NULL_PART_KEY | HA_END_SPACE_KEY))
|
2002-01-02 20:29:41 +01:00
|
|
|
{
|
|
|
|
/* Sort NOT NULL keys before other keys */
|
2003-12-12 21:26:58 +01:00
|
|
|
return (a->flags & (HA_NULL_PART_KEY | HA_END_SPACE_KEY)) ? 1 : -1;
|
2002-01-02 20:29:41 +01:00
|
|
|
}
|
|
|
|
if (a->name == primary_key_name)
|
|
|
|
return -1;
|
|
|
|
if (b->name == primary_key_name)
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
else if (b->flags & HA_NOSAME)
|
|
|
|
return 1; // Prefer b
|
|
|
|
|
|
|
|
if ((a->flags ^ b->flags) & HA_FULLTEXT)
|
|
|
|
{
|
|
|
|
return (a->flags & HA_FULLTEXT) ? 1 : -1;
|
|
|
|
}
|
2002-01-05 21:51:42 +01:00
|
|
|
/*
|
2004-04-08 16:56:45 +02:00
|
|
|
Prefer original key order. usable_key_parts contains here
|
2002-01-05 21:51:42 +01:00
|
|
|
the original key position.
|
|
|
|
*/
|
|
|
|
return ((a->usable_key_parts < b->usable_key_parts) ? -1 :
|
|
|
|
(a->usable_key_parts > b->usable_key_parts) ? 1 :
|
|
|
|
0);
|
2002-01-02 20:29:41 +01:00
|
|
|
}
|
|
|
|
|
2004-03-13 20:13:31 +01:00
|
|
|
/*
|
|
|
|
Check TYPELIB (set or enum) for duplicates
|
2004-04-08 16:56:45 +02:00
|
|
|
|
2004-03-13 20:13:31 +01:00
|
|
|
SYNOPSIS
|
|
|
|
check_duplicates_in_interval()
|
|
|
|
set_or_name "SET" or "ENUM" string for warning message
|
2004-04-08 16:56:45 +02:00
|
|
|
name name of the checked column
|
|
|
|
typelib list of values for the column
|
2004-03-13 20:13:31 +01:00
|
|
|
|
|
|
|
DESCRIPTION
|
2004-04-08 16:56:45 +02:00
|
|
|
This function prints an warning for each value in list
|
2004-03-13 20:13:31 +01:00
|
|
|
which has some duplicates on its right
|
|
|
|
|
|
|
|
RETURN VALUES
|
|
|
|
void
|
|
|
|
*/
|
|
|
|
|
|
|
|
void check_duplicates_in_interval(const char *set_or_name,
|
2004-10-26 07:41:14 +02:00
|
|
|
const char *name, TYPELIB *typelib,
|
|
|
|
CHARSET_INFO *cs)
|
2004-03-13 20:13:31 +01:00
|
|
|
{
|
2004-10-26 07:41:14 +02:00
|
|
|
TYPELIB tmp= *typelib;
|
2004-03-13 20:13:31 +01:00
|
|
|
const char **cur_value= typelib->type_names;
|
2004-10-26 07:41:14 +02:00
|
|
|
unsigned int *cur_length= typelib->type_lengths;
|
|
|
|
|
|
|
|
for ( ; tmp.count > 1; cur_value++, cur_length++)
|
2004-03-13 20:13:31 +01:00
|
|
|
{
|
2004-10-26 07:41:14 +02:00
|
|
|
tmp.type_names++;
|
|
|
|
tmp.type_lengths++;
|
|
|
|
tmp.count--;
|
|
|
|
if (find_type2(&tmp, (const char*)*cur_value, *cur_length, cs))
|
2004-03-13 20:13:31 +01:00
|
|
|
{
|
2004-03-15 11:53:27 +01:00
|
|
|
push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_NOTE,
|
2004-03-13 20:13:31 +01:00
|
|
|
ER_DUPLICATED_VALUE_IN_TYPE,
|
|
|
|
ER(ER_DUPLICATED_VALUE_IN_TYPE),
|
|
|
|
name,*cur_value,set_or_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2002-01-02 20:29:41 +01:00
|
|
|
|
2004-12-02 09:48:43 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
Check TYPELIB (set or enum) max and total lengths
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
calculate_interval_lengths()
|
|
|
|
cs charset+collation pair of the interval
|
|
|
|
typelib list of values for the column
|
|
|
|
max_length length of the longest item
|
|
|
|
tot_length sum of the item lengths
|
|
|
|
|
|
|
|
DESCRIPTION
|
|
|
|
After this function call:
|
|
|
|
- ENUM uses max_length
|
|
|
|
- SET uses tot_length.
|
|
|
|
|
|
|
|
RETURN VALUES
|
|
|
|
void
|
|
|
|
*/
|
|
|
|
void calculate_interval_lengths(CHARSET_INFO *cs, TYPELIB *interval,
|
|
|
|
uint32 *max_length, uint32 *tot_length)
|
|
|
|
{
|
|
|
|
const char **pos;
|
|
|
|
uint *len;
|
|
|
|
*max_length= *tot_length= 0;
|
|
|
|
for (pos= interval->type_names, len= interval->type_lengths;
|
|
|
|
*pos ; pos++, len++)
|
|
|
|
{
|
|
|
|
uint length= cs->cset->numchars(cs, *pos, *pos + *len);
|
|
|
|
*tot_length+= length;
|
|
|
|
set_if_bigger(*max_length, (uint32)length);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-03-04 22:14:35 +01:00
|
|
|
/*
|
|
|
|
Prepare a create_table instance for packing
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
prepare_create_field()
|
|
|
|
sql_field field to prepare for packing
|
|
|
|
blob_columns count for BLOBs
|
|
|
|
timestamps count for timestamps
|
|
|
|
table_flags table flags
|
|
|
|
|
|
|
|
DESCRIPTION
|
|
|
|
This function prepares a create_field instance.
|
|
|
|
Fields such as pack_flag are valid after this call.
|
|
|
|
|
|
|
|
RETURN VALUES
|
|
|
|
0 ok
|
|
|
|
1 Error
|
|
|
|
*/
|
|
|
|
|
|
|
|
int prepare_create_field(create_field *sql_field,
|
2005-03-16 15:11:01 +01:00
|
|
|
uint *blob_columns,
|
|
|
|
int *timestamps, int *timestamps_with_niladic,
|
2005-03-04 22:14:35 +01:00
|
|
|
uint table_flags)
|
|
|
|
{
|
|
|
|
DBUG_ENTER("prepare_field");
|
2005-03-16 15:11:01 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
This code came from mysql_prepare_table.
|
|
|
|
Indent preserved to make patching easier
|
|
|
|
*/
|
|
|
|
DBUG_ASSERT(sql_field->charset);
|
|
|
|
|
|
|
|
switch (sql_field->sql_type) {
|
|
|
|
case FIELD_TYPE_BLOB:
|
|
|
|
case FIELD_TYPE_MEDIUM_BLOB:
|
|
|
|
case FIELD_TYPE_TINY_BLOB:
|
|
|
|
case FIELD_TYPE_LONG_BLOB:
|
|
|
|
sql_field->pack_flag=FIELDFLAG_BLOB |
|
|
|
|
pack_length_to_packflag(sql_field->pack_length -
|
|
|
|
portable_sizeof_char_ptr);
|
|
|
|
if (sql_field->charset->state & MY_CS_BINSORT)
|
|
|
|
sql_field->pack_flag|=FIELDFLAG_BINARY;
|
|
|
|
sql_field->length=8; // Unireg field length
|
|
|
|
sql_field->unireg_check=Field::BLOB_FIELD;
|
2005-03-17 00:22:12 +01:00
|
|
|
(*blob_columns)++;
|
2005-03-16 15:11:01 +01:00
|
|
|
break;
|
|
|
|
case FIELD_TYPE_GEOMETRY:
|
2005-03-04 22:14:35 +01:00
|
|
|
#ifdef HAVE_SPATIAL
|
2005-03-16 15:11:01 +01:00
|
|
|
if (!(table_flags & HA_CAN_GEOMETRY))
|
|
|
|
{
|
|
|
|
my_printf_error(ER_CHECK_NOT_IMPLEMENTED, ER(ER_CHECK_NOT_IMPLEMENTED),
|
|
|
|
MYF(0), "GEOMETRY");
|
2005-03-04 22:14:35 +01:00
|
|
|
DBUG_RETURN(1);
|
2005-03-16 15:11:01 +01:00
|
|
|
}
|
|
|
|
sql_field->pack_flag=FIELDFLAG_GEOM |
|
|
|
|
pack_length_to_packflag(sql_field->pack_length -
|
|
|
|
portable_sizeof_char_ptr);
|
|
|
|
if (sql_field->charset->state & MY_CS_BINSORT)
|
|
|
|
sql_field->pack_flag|=FIELDFLAG_BINARY;
|
|
|
|
sql_field->length=8; // Unireg field length
|
|
|
|
sql_field->unireg_check=Field::BLOB_FIELD;
|
2005-03-17 00:22:12 +01:00
|
|
|
(*blob_columns)++;
|
2005-03-16 15:11:01 +01:00
|
|
|
break;
|
|
|
|
#else
|
|
|
|
my_printf_error(ER_FEATURE_DISABLED,ER(ER_FEATURE_DISABLED), MYF(0),
|
|
|
|
sym_group_geom.name, sym_group_geom.needed_define);
|
|
|
|
DBUG_RETURN(1);
|
2005-03-04 22:14:35 +01:00
|
|
|
#endif /*HAVE_SPATIAL*/
|
2005-03-16 15:11:01 +01:00
|
|
|
case MYSQL_TYPE_VARCHAR:
|
2005-03-04 22:14:35 +01:00
|
|
|
#ifndef QQ_ALL_HANDLERS_SUPPORT_VARCHAR
|
2005-03-16 15:11:01 +01:00
|
|
|
if (table_flags & HA_NO_VARCHAR)
|
|
|
|
{
|
|
|
|
/* convert VARCHAR to CHAR because handler is not yet up to date */
|
|
|
|
sql_field->sql_type= MYSQL_TYPE_VAR_STRING;
|
|
|
|
sql_field->pack_length= calc_pack_length(sql_field->sql_type,
|
|
|
|
(uint) sql_field->length);
|
|
|
|
if ((sql_field->length / sql_field->charset->mbmaxlen) >
|
|
|
|
MAX_FIELD_CHARLENGTH)
|
2005-03-04 22:14:35 +01:00
|
|
|
{
|
2005-03-16 15:11:01 +01:00
|
|
|
my_printf_error(ER_TOO_BIG_FIELDLENGTH, ER(ER_TOO_BIG_FIELDLENGTH),
|
|
|
|
MYF(0), sql_field->field_name, MAX_FIELD_CHARLENGTH);
|
2005-03-04 22:14:35 +01:00
|
|
|
DBUG_RETURN(1);
|
|
|
|
}
|
2005-03-16 15:11:01 +01:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
/* fall through */
|
|
|
|
case FIELD_TYPE_STRING:
|
|
|
|
sql_field->pack_flag=0;
|
|
|
|
if (sql_field->charset->state & MY_CS_BINSORT)
|
|
|
|
sql_field->pack_flag|=FIELDFLAG_BINARY;
|
|
|
|
break;
|
|
|
|
case FIELD_TYPE_ENUM:
|
|
|
|
sql_field->pack_flag=pack_length_to_packflag(sql_field->pack_length) |
|
|
|
|
FIELDFLAG_INTERVAL;
|
|
|
|
if (sql_field->charset->state & MY_CS_BINSORT)
|
|
|
|
sql_field->pack_flag|=FIELDFLAG_BINARY;
|
|
|
|
sql_field->unireg_check=Field::INTERVAL_FIELD;
|
|
|
|
check_duplicates_in_interval("ENUM",sql_field->field_name,
|
|
|
|
sql_field->interval,
|
|
|
|
sql_field->charset);
|
|
|
|
break;
|
|
|
|
case FIELD_TYPE_SET:
|
|
|
|
sql_field->pack_flag=pack_length_to_packflag(sql_field->pack_length) |
|
|
|
|
FIELDFLAG_BITFIELD;
|
|
|
|
if (sql_field->charset->state & MY_CS_BINSORT)
|
|
|
|
sql_field->pack_flag|=FIELDFLAG_BINARY;
|
|
|
|
sql_field->unireg_check=Field::BIT_FIELD;
|
|
|
|
check_duplicates_in_interval("SET",sql_field->field_name,
|
|
|
|
sql_field->interval,
|
|
|
|
sql_field->charset);
|
|
|
|
break;
|
|
|
|
case FIELD_TYPE_DATE: // Rest of string types
|
|
|
|
case FIELD_TYPE_NEWDATE:
|
|
|
|
case FIELD_TYPE_TIME:
|
|
|
|
case FIELD_TYPE_DATETIME:
|
|
|
|
case FIELD_TYPE_NULL:
|
|
|
|
sql_field->pack_flag=f_settype((uint) sql_field->sql_type);
|
|
|
|
break;
|
|
|
|
case FIELD_TYPE_BIT:
|
2005-04-12 20:12:00 +02:00
|
|
|
/*
|
|
|
|
We have sql_field->pack_flag already set here, see mysql_prepare_table().
|
|
|
|
*/
|
2005-03-16 15:11:01 +01:00
|
|
|
break;
|
|
|
|
case FIELD_TYPE_NEWDECIMAL:
|
|
|
|
sql_field->pack_flag=(FIELDFLAG_NUMBER |
|
|
|
|
(sql_field->flags & UNSIGNED_FLAG ? 0 :
|
|
|
|
FIELDFLAG_DECIMAL) |
|
|
|
|
(sql_field->flags & ZEROFILL_FLAG ?
|
|
|
|
FIELDFLAG_ZEROFILL : 0) |
|
|
|
|
(sql_field->decimals << FIELDFLAG_DEC_SHIFT));
|
|
|
|
break;
|
|
|
|
case FIELD_TYPE_TIMESTAMP:
|
|
|
|
/* We should replace old TIMESTAMP fields with their newer analogs */
|
|
|
|
if (sql_field->unireg_check == Field::TIMESTAMP_OLD_FIELD)
|
|
|
|
{
|
2005-03-17 00:22:12 +01:00
|
|
|
if (!*timestamps)
|
2005-03-04 22:14:35 +01:00
|
|
|
{
|
2005-03-16 15:11:01 +01:00
|
|
|
sql_field->unireg_check= Field::TIMESTAMP_DNUN_FIELD;
|
2005-03-17 00:22:12 +01:00
|
|
|
(*timestamps_with_niladic)++;
|
2005-03-04 22:14:35 +01:00
|
|
|
}
|
2005-03-16 15:11:01 +01:00
|
|
|
else
|
|
|
|
sql_field->unireg_check= Field::NONE;
|
|
|
|
}
|
|
|
|
else if (sql_field->unireg_check != Field::NONE)
|
2005-03-17 00:22:12 +01:00
|
|
|
(*timestamps_with_niladic)++;
|
2005-03-16 15:11:01 +01:00
|
|
|
|
2005-03-17 00:22:12 +01:00
|
|
|
(*timestamps)++;
|
2005-03-16 15:11:01 +01:00
|
|
|
/* fall-through */
|
|
|
|
default:
|
|
|
|
sql_field->pack_flag=(FIELDFLAG_NUMBER |
|
|
|
|
(sql_field->flags & UNSIGNED_FLAG ? 0 :
|
|
|
|
FIELDFLAG_DECIMAL) |
|
|
|
|
(sql_field->flags & ZEROFILL_FLAG ?
|
|
|
|
FIELDFLAG_ZEROFILL : 0) |
|
|
|
|
f_settype((uint) sql_field->sql_type) |
|
|
|
|
(sql_field->decimals << FIELDFLAG_DEC_SHIFT));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!(sql_field->flags & NOT_NULL_FLAG))
|
|
|
|
sql_field->pack_flag|= FIELDFLAG_MAYBE_NULL;
|
|
|
|
if (sql_field->flags & NO_DEFAULT_VALUE_FLAG)
|
|
|
|
sql_field->pack_flag|= FIELDFLAG_NO_DEFAULT;
|
2005-03-04 22:14:35 +01:00
|
|
|
DBUG_RETURN(0);
|
|
|
|
}
|
|
|
|
|
2002-09-03 14:44:25 +02:00
|
|
|
/*
|
2004-03-30 19:22:14 +02:00
|
|
|
Preparation for table creation
|
2002-09-03 14:44:25 +02:00
|
|
|
|
|
|
|
SYNOPSIS
|
2004-03-30 19:22:14 +02:00
|
|
|
mysql_prepare_table()
|
2006-01-12 10:05:07 +01:00
|
|
|
thd Thread object.
|
|
|
|
create_info Create information (like MAX_ROWS).
|
|
|
|
fields List of fields to create.
|
|
|
|
keys List of keys to create.
|
|
|
|
tmp_table If a temporary table is to be created.
|
|
|
|
db_options INOUT Table options (like HA_OPTION_PACK_RECORD).
|
|
|
|
file The handler for the new table.
|
|
|
|
key_info_buffer OUT An array of KEY structs for the indexes.
|
|
|
|
key_count OUT The number of elements in the array.
|
|
|
|
select_field_count The number of fields coming from a select table.
|
2002-09-03 14:44:25 +02:00
|
|
|
|
2003-09-03 11:34:32 +02:00
|
|
|
DESCRIPTION
|
2004-03-30 19:22:14 +02:00
|
|
|
Prepares the table and key structures for table creation.
|
2002-09-03 14:44:25 +02:00
|
|
|
|
2004-12-06 01:00:37 +01:00
|
|
|
NOTES
|
2005-03-22 14:48:06 +01:00
|
|
|
sets create_info->varchar if the table has a varchar
|
2004-12-06 01:00:37 +01:00
|
|
|
|
2002-09-03 14:44:25 +02:00
|
|
|
RETURN VALUES
|
|
|
|
0 ok
|
|
|
|
-1 error
|
|
|
|
*/
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2005-04-01 14:04:50 +02:00
|
|
|
static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
|
|
|
|
List<create_field> *fields,
|
|
|
|
List<Key> *keys, bool tmp_table,
|
|
|
|
uint *db_options,
|
|
|
|
handler *file, KEY **key_info_buffer,
|
|
|
|
uint *key_count, int select_field_count)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-04-08 16:56:45 +02:00
|
|
|
const char *key_name;
|
2000-07-31 21:29:14 +02:00
|
|
|
create_field *sql_field,*dup_field;
|
2005-05-16 14:21:35 +02:00
|
|
|
uint field,null_fields,blob_columns,max_key_length;
|
2005-05-13 23:01:40 +02:00
|
|
|
ulong record_offset= 0;
|
2004-04-08 16:56:45 +02:00
|
|
|
KEY *key_info;
|
2000-07-31 21:29:14 +02:00
|
|
|
KEY_PART_INFO *key_part_info;
|
2004-04-08 16:56:45 +02:00
|
|
|
int timestamps= 0, timestamps_with_niladic= 0;
|
|
|
|
int field_no,dup_no;
|
|
|
|
int select_field_pos,auto_increment=0;
|
2005-04-01 14:04:50 +02:00
|
|
|
List_iterator<create_field> it(*fields),it2(*fields);
|
2004-12-17 15:06:05 +01:00
|
|
|
uint total_uneven_bit_length= 0;
|
2004-03-30 19:22:14 +02:00
|
|
|
DBUG_ENTER("mysql_prepare_table");
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2005-04-01 14:04:50 +02:00
|
|
|
select_field_pos= fields->elements - select_field_count;
|
2000-07-31 21:29:14 +02:00
|
|
|
null_fields=blob_columns=0;
|
2004-12-06 01:00:37 +01:00
|
|
|
create_info->varchar= 0;
|
2005-05-16 14:21:35 +02:00
|
|
|
max_key_length= file->max_key_length();
|
2001-06-06 21:45:07 +02:00
|
|
|
|
2002-10-02 12:33:08 +02:00
|
|
|
for (field_no=0; (sql_field=it++) ; field_no++)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-12-06 01:00:37 +01:00
|
|
|
CHARSET_INFO *save_cs;
|
|
|
|
|
2006-02-21 17:52:20 +01:00
|
|
|
/*
|
|
|
|
Initialize length from its original value (number of characters),
|
|
|
|
which was set in the parser. This is necessary if we're
|
|
|
|
executing a prepared statement for the second time.
|
|
|
|
*/
|
|
|
|
sql_field->length= sql_field->char_length;
|
2003-09-10 14:25:26 +02:00
|
|
|
if (!sql_field->charset)
|
2003-11-18 12:47:27 +01:00
|
|
|
sql_field->charset= create_info->default_table_charset;
|
|
|
|
/*
|
|
|
|
table_charset is set in ALTER TABLE if we want change character set
|
2004-03-19 08:37:49 +01:00
|
|
|
for all varchar/char columns.
|
|
|
|
But the table charset must not affect the BLOB fields, so don't
|
|
|
|
allow to change my_charset_bin to somethig else.
|
2003-11-18 12:47:27 +01:00
|
|
|
*/
|
2004-03-19 08:37:49 +01:00
|
|
|
if (create_info->table_charset && sql_field->charset != &my_charset_bin)
|
2003-09-10 14:25:26 +02:00
|
|
|
sql_field->charset= create_info->table_charset;
|
2004-03-26 13:11:46 +01:00
|
|
|
|
2004-12-06 01:00:37 +01:00
|
|
|
save_cs= sql_field->charset;
|
2004-03-26 13:11:46 +01:00
|
|
|
if ((sql_field->flags & BINCMP_FLAG) &&
|
|
|
|
!(sql_field->charset= get_charset_by_csname(sql_field->charset->csname,
|
|
|
|
MY_CS_BINSORT,MYF(0))))
|
|
|
|
{
|
|
|
|
char tmp[64];
|
2005-11-20 19:47:07 +01:00
|
|
|
strmake(strmake(tmp, save_cs->csname, sizeof(tmp)-4),
|
|
|
|
STRING_WITH_LEN("_bin"));
|
2004-03-26 13:11:46 +01:00
|
|
|
my_error(ER_UNKNOWN_COLLATION, MYF(0), tmp);
|
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
2004-04-08 16:56:45 +02:00
|
|
|
|
2004-12-07 12:08:56 +01:00
|
|
|
if (sql_field->sql_type == FIELD_TYPE_SET ||
|
|
|
|
sql_field->sql_type == FIELD_TYPE_ENUM)
|
2004-12-02 09:48:43 +01:00
|
|
|
{
|
|
|
|
uint32 dummy;
|
|
|
|
CHARSET_INFO *cs= sql_field->charset;
|
2004-12-07 12:08:56 +01:00
|
|
|
TYPELIB *interval= sql_field->interval;
|
2004-12-02 09:48:43 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
Create typelib from interval_list, and if necessary
|
|
|
|
convert strings from client character set to the
|
|
|
|
column character set.
|
|
|
|
*/
|
2004-12-07 12:08:56 +01:00
|
|
|
if (!interval)
|
2004-12-02 09:48:43 +01:00
|
|
|
{
|
2005-11-25 11:25:31 +01:00
|
|
|
/*
|
|
|
|
Create the typelib in prepared statement memory if we're
|
|
|
|
executing one.
|
|
|
|
*/
|
2005-11-25 16:53:55 +01:00
|
|
|
MEM_ROOT *stmt_root= thd->stmt_arena->mem_root;
|
2005-11-25 11:25:31 +01:00
|
|
|
|
|
|
|
interval= sql_field->interval= typelib(stmt_root,
|
|
|
|
sql_field->interval_list);
|
2004-12-07 12:08:56 +01:00
|
|
|
List_iterator<String> it(sql_field->interval_list);
|
|
|
|
String conv, *tmp;
|
2006-03-29 16:52:26 +02:00
|
|
|
char comma_buf[2];
|
|
|
|
int comma_length= cs->cset->wc_mb(cs, ',', (uchar*) comma_buf,
|
|
|
|
(uchar*) comma_buf +
|
|
|
|
sizeof(comma_buf));
|
|
|
|
DBUG_ASSERT(comma_length > 0);
|
2004-12-07 12:08:56 +01:00
|
|
|
for (uint i= 0; (tmp= it++); i++)
|
2004-12-02 09:48:43 +01:00
|
|
|
{
|
2005-05-06 10:39:30 +02:00
|
|
|
uint lengthsp;
|
2004-12-07 12:08:56 +01:00
|
|
|
if (String::needs_conversion(tmp->length(), tmp->charset(),
|
|
|
|
cs, &dummy))
|
|
|
|
{
|
|
|
|
uint cnv_errs;
|
|
|
|
conv.copy(tmp->ptr(), tmp->length(), tmp->charset(), cs, &cnv_errs);
|
2005-11-25 11:57:13 +01:00
|
|
|
interval->type_names[i]= strmake_root(stmt_root, conv.ptr(),
|
2005-05-06 10:39:30 +02:00
|
|
|
conv.length());
|
2004-12-07 12:08:56 +01:00
|
|
|
interval->type_lengths[i]= conv.length();
|
|
|
|
}
|
2004-12-02 09:48:43 +01:00
|
|
|
|
2004-12-07 12:08:56 +01:00
|
|
|
// Strip trailing spaces.
|
2005-05-06 10:39:30 +02:00
|
|
|
lengthsp= cs->cset->lengthsp(cs, interval->type_names[i],
|
|
|
|
interval->type_lengths[i]);
|
2004-12-07 12:08:56 +01:00
|
|
|
interval->type_lengths[i]= lengthsp;
|
|
|
|
((uchar *)interval->type_names[i])[lengthsp]= '\0';
|
2006-03-29 16:52:26 +02:00
|
|
|
if (sql_field->sql_type == FIELD_TYPE_SET)
|
|
|
|
{
|
|
|
|
if (cs->coll->instr(cs, interval->type_names[i],
|
|
|
|
interval->type_lengths[i],
|
|
|
|
comma_buf, comma_length, NULL, 0))
|
|
|
|
{
|
2006-03-30 06:13:25 +02:00
|
|
|
my_error(ER_ILLEGAL_VALUE_FOR_TYPE, MYF(0), "set", tmp->ptr());
|
2006-03-29 16:52:26 +02:00
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
|
|
|
}
|
2004-12-02 09:48:43 +01:00
|
|
|
}
|
2004-12-07 12:08:56 +01:00
|
|
|
sql_field->interval_list.empty(); // Don't need interval_list anymore
|
2004-12-02 09:48:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Convert the default value from client character
|
|
|
|
set into the column character set if necessary.
|
|
|
|
*/
|
2005-07-13 10:00:17 +02:00
|
|
|
if (sql_field->def && cs != sql_field->def->collation.collation)
|
2004-12-02 09:48:43 +01:00
|
|
|
{
|
2005-11-25 16:53:55 +01:00
|
|
|
Query_arena backup_arena;
|
|
|
|
bool need_to_change_arena= !thd->stmt_arena->is_conventional();
|
2005-11-25 11:25:31 +01:00
|
|
|
if (need_to_change_arena)
|
|
|
|
{
|
|
|
|
/* Asser that we don't do that at every PS execute */
|
2005-11-25 16:53:55 +01:00
|
|
|
DBUG_ASSERT(thd->stmt_arena->is_first_stmt_execute() ||
|
|
|
|
thd->stmt_arena->is_first_sp_execute());
|
|
|
|
thd->set_n_backup_active_arena(thd->stmt_arena, &backup_arena);
|
2005-11-25 11:25:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
sql_field->def= sql_field->def->safe_charset_converter(cs);
|
|
|
|
|
|
|
|
if (need_to_change_arena)
|
2005-11-25 16:53:55 +01:00
|
|
|
thd->restore_active_arena(thd->stmt_arena, &backup_arena);
|
2005-11-25 11:25:31 +01:00
|
|
|
|
2006-04-28 18:15:29 +02:00
|
|
|
if (sql_field->def == NULL)
|
2005-07-13 10:00:17 +02:00
|
|
|
{
|
|
|
|
/* Could not convert */
|
|
|
|
my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
|
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
2004-12-02 09:48:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (sql_field->sql_type == FIELD_TYPE_SET)
|
|
|
|
{
|
2004-12-07 14:47:00 +01:00
|
|
|
uint32 field_length;
|
2006-04-28 18:15:29 +02:00
|
|
|
if (sql_field->def != NULL)
|
2004-12-02 09:48:43 +01:00
|
|
|
{
|
|
|
|
char *not_used;
|
|
|
|
uint not_used2;
|
|
|
|
bool not_found= 0;
|
|
|
|
String str, *def= sql_field->def->val_str(&str);
|
2006-04-28 18:15:29 +02:00
|
|
|
if (def == NULL) /* SQL "NULL" maps to NULL */
|
|
|
|
{
|
|
|
|
if ((sql_field->flags & NOT_NULL_FLAG) != 0)
|
|
|
|
{
|
|
|
|
my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
|
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* else, NULL is an allowed value */
|
|
|
|
(void) find_set(interval, NULL, 0,
|
|
|
|
cs, ¬_used, ¬_used2, ¬_found);
|
|
|
|
}
|
|
|
|
else /* not NULL */
|
|
|
|
{
|
|
|
|
(void) find_set(interval, def->ptr(), def->length(),
|
|
|
|
cs, ¬_used, ¬_used2, ¬_found);
|
|
|
|
}
|
|
|
|
|
2004-12-02 09:48:43 +01:00
|
|
|
if (not_found)
|
|
|
|
{
|
|
|
|
my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
|
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
|
|
|
}
|
2004-12-07 14:47:00 +01:00
|
|
|
calculate_interval_lengths(cs, interval, &dummy, &field_length);
|
|
|
|
sql_field->length= field_length + (interval->count - 1);
|
2004-12-02 09:48:43 +01:00
|
|
|
}
|
|
|
|
else /* FIELD_TYPE_ENUM */
|
|
|
|
{
|
2004-12-07 14:47:00 +01:00
|
|
|
uint32 field_length;
|
2006-04-28 18:15:29 +02:00
|
|
|
DBUG_ASSERT(sql_field->sql_type == FIELD_TYPE_ENUM);
|
|
|
|
if (sql_field->def != NULL)
|
2004-12-02 09:48:43 +01:00
|
|
|
{
|
|
|
|
String str, *def= sql_field->def->val_str(&str);
|
2006-04-28 18:15:29 +02:00
|
|
|
if (def == NULL) /* SQL "NULL" maps to NULL */
|
2004-12-02 09:48:43 +01:00
|
|
|
{
|
2006-04-28 18:15:29 +02:00
|
|
|
if ((sql_field->flags & NOT_NULL_FLAG) != 0)
|
|
|
|
{
|
|
|
|
my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
|
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* else, the defaults yield the correct length for NULLs. */
|
|
|
|
}
|
|
|
|
else /* not NULL */
|
|
|
|
{
|
|
|
|
def->length(cs->cset->lengthsp(cs, def->ptr(), def->length()));
|
|
|
|
if (find_type2(interval, def->ptr(), def->length(), cs) == 0) /* not found */
|
|
|
|
{
|
|
|
|
my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
|
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
2004-12-02 09:48:43 +01:00
|
|
|
}
|
|
|
|
}
|
2004-12-07 14:47:00 +01:00
|
|
|
calculate_interval_lengths(cs, interval, &field_length, &dummy);
|
|
|
|
sql_field->length= field_length;
|
2004-12-02 09:48:43 +01:00
|
|
|
}
|
|
|
|
set_if_smaller(sql_field->length, MAX_FIELD_WIDTH-1);
|
|
|
|
}
|
|
|
|
|
2005-04-12 09:27:43 +02:00
|
|
|
if (sql_field->sql_type == FIELD_TYPE_BIT)
|
|
|
|
{
|
2005-04-12 20:12:00 +02:00
|
|
|
sql_field->pack_flag= FIELDFLAG_NUMBER;
|
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes
Changes that requires code changes in other code of other storage engines.
(Note that all changes are very straightforward and one should find all issues
by compiling a --debug build and fixing all compiler errors and all
asserts in field.cc while running the test suite),
- New optional handler function introduced: reset()
This is called after every DML statement to make it easy for a handler to
statement specific cleanups.
(The only case it's not called is if force the file to be closed)
- handler::extra(HA_EXTRA_RESET) is removed. Code that was there before
should be moved to handler::reset()
- table->read_set contains a bitmap over all columns that are needed
in the query. read_row() and similar functions only needs to read these
columns
- table->write_set contains a bitmap over all columns that will be updated
in the query. write_row() and update_row() only needs to update these
columns.
The above bitmaps should now be up to date in all context
(including ALTER TABLE, filesort()).
The handler is informed of any changes to the bitmap after
fix_fields() by calling the virtual function
handler::column_bitmaps_signal(). If the handler does caching of
these bitmaps (instead of using table->read_set, table->write_set),
it should redo the caching in this code. as the signal() may be sent
several times, it's probably best to set a variable in the signal
and redo the caching on read_row() / write_row() if the variable was
set.
- Removed the read_set and write_set bitmap objects from the handler class
- Removed all column bit handling functions from the handler class.
(Now one instead uses the normal bitmap functions in my_bitmap.c instead
of handler dedicated bitmap functions)
- field->query_id is removed. One should instead instead check
table->read_set and table->write_set if a field is used in the query.
- handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and
handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now
instead use table->read_set to check for which columns to retrieve.
- If a handler needs to call Field->val() or Field->store() on columns
that are not used in the query, one should install a temporary
all-columns-used map while doing so. For this, we provide the following
functions:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
field->val();
dbug_tmp_restore_column_map(table->read_set, old_map);
and similar for the write map:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
field->val();
dbug_tmp_restore_column_map(table->write_set, old_map);
If this is not done, you will sooner or later hit a DBUG_ASSERT
in the field store() / val() functions.
(For not DBUG binaries, the dbug_tmp_restore_column_map() and
dbug_tmp_restore_column_map() are inline dummy functions and should
be optimized away be the compiler).
- If one needs to temporary set the column map for all binaries (and not
just to avoid the DBUG_ASSERT() in the Field::store() / Field::val()
methods) one should use the functions tmp_use_all_columns() and
tmp_restore_column_map() instead of the above dbug_ variants.
- All 'status' fields in the handler base class (like records,
data_file_length etc) are now stored in a 'stats' struct. This makes
it easier to know what status variables are provided by the base
handler. This requires some trivial variable names in the extra()
function.
- New virtual function handler::records(). This is called to optimize
COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true.
(stats.records is not supposed to be an exact value. It's only has to
be 'reasonable enough' for the optimizer to be able to choose a good
optimization path).
- Non virtual handler::init() function added for caching of virtual
constants from engine.
- Removed has_transactions() virtual method. Now one should instead return
HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support
transactions.
- The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument
that is to be used with 'new handler_name()' to allocate the handler
in the right area. The xxxx_create_handler() function is also
responsible for any initialization of the object before returning.
For example, one should change:
static handler *myisam_create_handler(TABLE_SHARE *table)
{
return new ha_myisam(table);
}
->
static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root)
{
return new (mem_root) ha_myisam(table);
}
- New optional virtual function: use_hidden_primary_key().
This is called in case of an update/delete when
(table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
but we don't have a primary key. This allows the handler to take precisions
in remembering any hidden primary key to able to update/delete any
found row. The default handler marks all columns to be read.
- handler::table_flags() now returns a ulonglong (to allow for more flags).
- New/changed table_flags()
- HA_HAS_RECORDS Set if ::records() is supported
- HA_NO_TRANSACTIONS Set if engine doesn't support transactions
- HA_PRIMARY_KEY_REQUIRED_FOR_DELETE
Set if we should mark all primary key columns for
read when reading rows as part of a DELETE
statement. If there is no primary key,
all columns are marked for read.
- HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some
cases (based on table->read_set)
- HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS
Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION.
- HA_DUPP_POS Renamed to HA_DUPLICATE_POS
- HA_REQUIRES_KEY_COLUMNS_FOR_DELETE
Set this if we should mark ALL key columns for
read when when reading rows as part of a DELETE
statement. In case of an update we will mark
all keys for read for which key part changed
value.
- HA_STATS_RECORDS_IS_EXACT
Set this if stats.records is exact.
(This saves us some extra records() calls
when optimizing COUNT(*))
- Removed table_flags()
- HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if
handler::records() gives an exact count() and
HA_STATS_RECORDS_IS_EXACT if stats.records is exact.
- HA_READ_RND_SAME Removed (no one supported this one)
- Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk()
- Renamed handler::dupp_pos to handler::dup_pos
- Removed not used variable handler::sortkey
Upper level handler changes:
- ha_reset() now does some overall checks and calls ::reset()
- ha_table_flags() added. This is a cached version of table_flags(). The
cache is updated on engine creation time and updated on open.
MySQL level changes (not obvious from the above):
- DBUG_ASSERT() added to check that column usage matches what is set
in the column usage bit maps. (This found a LOT of bugs in current
column marking code).
- In 5.1 before, all used columns was marked in read_set and only updated
columns was marked in write_set. Now we only mark columns for which we
need a value in read_set.
- Column bitmaps are created in open_binary_frm() and open_table_from_share().
(Before this was in table.cc)
- handler::table_flags() calls are replaced with handler::ha_table_flags()
- For calling field->val() you must have the corresponding bit set in
table->read_set. For calling field->store() you must have the
corresponding bit set in table->write_set. (There are asserts in
all store()/val() functions to catch wrong usage)
- thd->set_query_id is renamed to thd->mark_used_columns and instead
of setting this to an integer value, this has now the values:
MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE
Changed also all variables named 'set_query_id' to mark_used_columns.
- In filesort() we now inform the handler of exactly which columns are needed
doing the sort and choosing the rows.
- The TABLE_SHARE object has a 'all_set' column bitmap one can use
when one needs a column bitmap with all columns set.
(This is used for table->use_all_columns() and other places)
- The TABLE object has 3 column bitmaps:
- def_read_set Default bitmap for columns to be read
- def_write_set Default bitmap for columns to be written
- tmp_set Can be used as a temporary bitmap when needed.
The table object has also two pointer to bitmaps read_set and write_set
that the handler should use to find out which columns are used in which way.
- count() optimization now calls handler::records() instead of using
handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true).
- Added extra argument to Item::walk() to indicate if we should also
traverse sub queries.
- Added TABLE parameter to cp_buffer_from_ref()
- Don't close tables created with CREATE ... SELECT but keep them in
the table cache. (Faster usage of newly created tables).
New interfaces:
- table->clear_column_bitmaps() to initialize the bitmaps for tables
at start of new statements.
- table->column_bitmaps_set() to set up new column bitmaps and signal
the handler about this.
- table->column_bitmaps_set_no_signal() for some few cases where we need
to setup new column bitmaps but don't signal the handler (as the handler
has already been signaled about these before). Used for the momement
only in opt_range.cc when doing ROR scans.
- table->use_all_columns() to install a bitmap where all columns are marked
as use in the read and the write set.
- table->default_column_bitmaps() to install the normal read and write
column bitmaps, but not signaling the handler about this.
This is mainly used when creating TABLE instances.
- table->mark_columns_needed_for_delete(),
table->mark_columns_needed_for_delete() and
table->mark_columns_needed_for_insert() to allow us to put additional
columns in column usage maps if handler so requires.
(The handler indicates what it neads in handler->table_flags())
- table->prepare_for_position() to allow us to tell handler that it
needs to read primary key parts to be able to store them in
future table->position() calls.
(This replaces the table->file->ha_retrieve_all_pk function)
- table->mark_auto_increment_column() to tell handler are going to update
columns part of any auto_increment key.
- table->mark_columns_used_by_index() to mark all columns that is part of
an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow
it to quickly know that it only needs to read colums that are part
of the key. (The handler can also use the column map for detecting this,
but simpler/faster handler can just monitor the extra() call).
- table->mark_columns_used_by_index_no_reset() to in addition to other columns,
also mark all columns that is used by the given key.
- table->restore_column_maps_after_mark_index() to restore to default
column maps after a call to table->mark_columns_used_by_index().
- New item function register_field_in_read_map(), for marking used columns
in table->read_map. Used by filesort() to mark all used columns
- Maintain in TABLE->merge_keys set of all keys that are used in query.
(Simplices some optimization loops)
- Maintain Field->part_of_key_not_clustered which is like Field->part_of_key
but the field in the clustered key is not assumed to be part of all index.
(used in opt_range.cc for faster loops)
- dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map()
tmp_use_all_columns() and tmp_restore_column_map() functions to temporally
mark all columns as usable. The 'dbug_' version is primarily intended
inside a handler when it wants to just call Field:store() & Field::val()
functions, but don't need the column maps set for any other usage.
(ie:: bitmap_is_set() is never called)
- We can't use compare_records() to skip updates for handlers that returns
a partial column set and the read_set doesn't cover all columns in the
write set. The reason for this is that if we have a column marked only for
write we can't in the MySQL level know if the value changed or not.
The reason this worked before was that MySQL marked all to be written
columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden
bug'.
- open_table_from_share() does not anymore setup temporary MEM_ROOT
object as a thread specific variable for the handler. Instead we
send the to-be-used MEMROOT to get_new_handler().
(Simpler, faster code)
Bugs fixed:
- Column marking was not done correctly in a lot of cases.
(ALTER TABLE, when using triggers, auto_increment fields etc)
(Could potentially result in wrong values inserted in table handlers
relying on that the old column maps or field->set_query_id was correct)
Especially when it comes to triggers, there may be cases where the
old code would cause lost/wrong values for NDB and/or InnoDB tables.
- Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags:
OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG.
This allowed me to remove some wrong warnings about:
"Some non-transactional changed tables couldn't be rolled back"
- Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset
(thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose
some warnings about
"Some non-transactional changed tables couldn't be rolled back")
- Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table()
which could cause delete_table to report random failures.
- Fixed core dumps for some tests when running with --debug
- Added missing FN_LIBCHAR in mysql_rm_tmp_tables()
(This has probably caused us to not properly remove temporary files after
crash)
- slow_logs was not properly initialized, which could maybe cause
extra/lost entries in slow log.
- If we get an duplicate row on insert, change column map to read and
write all columns while retrying the operation. This is required by
the definition of REPLACE and also ensures that fields that are only
part of UPDATE are properly handled. This fixed a bug in NDB and
REPLACE where REPLACE wrongly copied some column values from the replaced
row.
- For table handler that doesn't support NULL in keys, we would give an error
when creating a primary key with NULL fields, even after the fields has been
automaticly converted to NOT NULL.
- Creating a primary key on a SPATIAL key, would fail if field was not
declared as NOT NULL.
Cleanups:
- Removed not used condition argument to setup_tables
- Removed not needed item function reset_query_id_processor().
- Field->add_index is removed. Now this is instead maintained in
(field->flags & FIELD_IN_ADD_INDEX)
- Field->fieldnr is removed (use field->field_index instead)
- New argument to filesort() to indicate that it should return a set of
row pointers (not used columns). This allowed me to remove some references
to sql_command in filesort and should also enable us to return column
results in some cases where we couldn't before.
- Changed column bitmap handling in opt_range.cc to be aligned with TABLE
bitmap, which allowed me to use bitmap functions instead of looping over
all fields to create some needed bitmaps. (Faster and smaller code)
- Broke up found too long lines
- Moved some variable declaration at start of function for better code
readability.
- Removed some not used arguments from functions.
(setup_fields(), mysql_prepare_insert_check_table())
- setup_fields() now takes an enum instead of an int for marking columns
usage.
- For internal temporary tables, use handler::write_row(),
handler::delete_row() and handler::update_row() instead of
handler::ha_xxxx() for faster execution.
- Changed some constants to enum's and define's.
- Using separate column read and write sets allows for easier checking
of timestamp field was set by statement.
- Remove calls to free_io_cache() as this is now done automaticly in ha_reset()
- Don't build table->normalized_path as this is now identical to table->path
(after bar's fixes to convert filenames)
- Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to
do comparision with the 'convert-dbug-for-diff' tool.
Things left to do in 5.1:
- We wrongly log failed CREATE TABLE ... SELECT in some cases when using
row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result)
Mats has promised to look into this.
- Test that my fix for CREATE TABLE ... SELECT is indeed correct.
(I added several test cases for this, but in this case it's better that
someone else also tests this throughly).
Lars has promosed to do this.
2006-06-04 17:52:22 +02:00
|
|
|
if (file->ha_table_flags() & HA_CAN_BIT_FIELD)
|
2005-04-12 09:27:43 +02:00
|
|
|
total_uneven_bit_length+= sql_field->length & 7;
|
|
|
|
else
|
|
|
|
sql_field->pack_flag|= FIELDFLAG_TREAT_BIT_AS_CHAR;
|
|
|
|
}
|
|
|
|
|
2003-09-10 14:25:26 +02:00
|
|
|
sql_field->create_length_to_internal_length();
|
2005-05-06 10:39:30 +02:00
|
|
|
if (prepare_blob_field(thd, sql_field))
|
|
|
|
DBUG_RETURN(-1);
|
2003-10-15 13:40:20 +02:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
if (!(sql_field->flags & NOT_NULL_FLAG))
|
|
|
|
null_fields++;
|
2004-12-17 15:06:05 +01:00
|
|
|
|
2003-03-14 16:08:42 +01:00
|
|
|
if (check_column_name(sql_field->field_name))
|
|
|
|
{
|
2003-11-18 16:28:00 +01:00
|
|
|
my_error(ER_WRONG_COLUMN_NAME, MYF(0), sql_field->field_name);
|
2003-03-14 16:08:42 +01:00
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
2003-03-16 18:17:54 +01:00
|
|
|
|
2002-10-02 12:33:08 +02:00
|
|
|
/* Check if we have used the same field name before */
|
|
|
|
for (dup_no=0; (dup_field=it2++) != sql_field; dup_no++)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2002-07-20 16:36:56 +02:00
|
|
|
if (my_strcasecmp(system_charset_info,
|
2004-04-08 16:56:45 +02:00
|
|
|
sql_field->field_name,
|
|
|
|
dup_field->field_name) == 0)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
/*
|
|
|
|
If this was a CREATE ... SELECT statement, accept a field
|
|
|
|
redefinition if we are changing a field in the SELECT part
|
|
|
|
*/
|
2004-04-08 16:56:45 +02:00
|
|
|
if (field_no < select_field_pos || dup_no >= select_field_pos)
|
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_DUP_FIELDNAME, MYF(0), sql_field->field_name);
|
2004-04-08 16:56:45 +02:00
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
/* Field redefined */
|
2005-11-09 07:34:46 +01:00
|
|
|
sql_field->def= dup_field->def;
|
2003-08-11 15:18:34 +02:00
|
|
|
sql_field->sql_type= dup_field->sql_type;
|
2003-11-18 12:47:27 +01:00
|
|
|
sql_field->charset= (dup_field->charset ?
|
|
|
|
dup_field->charset :
|
|
|
|
create_info->default_table_charset);
|
2006-02-21 17:52:20 +01:00
|
|
|
sql_field->length= dup_field->char_length;
|
2005-10-25 22:56:17 +02:00
|
|
|
sql_field->pack_length= dup_field->pack_length;
|
2004-12-07 14:47:00 +01:00
|
|
|
sql_field->key_length= dup_field->key_length;
|
2003-08-11 15:18:34 +02:00
|
|
|
sql_field->create_length_to_internal_length();
|
2002-10-02 12:33:08 +02:00
|
|
|
sql_field->decimals= dup_field->decimals;
|
|
|
|
sql_field->unireg_check= dup_field->unireg_check;
|
2005-11-07 07:23:43 +01:00
|
|
|
/*
|
|
|
|
We're making one field from two, the result field will have
|
|
|
|
dup_field->flags as flags. If we've incremented null_fields
|
|
|
|
because of sql_field->flags, decrement it back.
|
|
|
|
*/
|
|
|
|
if (!(sql_field->flags & NOT_NULL_FLAG))
|
|
|
|
null_fields--;
|
|
|
|
sql_field->flags= dup_field->flags;
|
2005-09-01 00:13:02 +02:00
|
|
|
sql_field->interval= dup_field->interval;
|
2002-10-02 12:33:08 +02:00
|
|
|
it2.remove(); // Remove first (create) definition
|
|
|
|
select_field_pos--;
|
|
|
|
break;
|
2004-04-08 16:56:45 +02:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
}
|
2004-12-07 14:47:00 +01:00
|
|
|
/* Don't pack rows in old tables if the user has requested this */
|
|
|
|
if ((sql_field->flags & BLOB_FLAG) ||
|
|
|
|
sql_field->sql_type == MYSQL_TYPE_VARCHAR &&
|
|
|
|
create_info->row_type != ROW_TYPE_FIXED)
|
2005-04-01 14:04:50 +02:00
|
|
|
(*db_options)|= HA_OPTION_PACK_RECORD;
|
2000-07-31 21:29:14 +02:00
|
|
|
it2.rewind();
|
|
|
|
}
|
2005-05-13 10:11:50 +02:00
|
|
|
|
|
|
|
/* record_offset will be increased with 'length-of-null-bits' later */
|
|
|
|
record_offset= 0;
|
2005-05-14 17:31:22 +02:00
|
|
|
null_fields+= total_uneven_bit_length;
|
2000-07-31 21:29:14 +02:00
|
|
|
|
|
|
|
it.rewind();
|
|
|
|
while ((sql_field=it++))
|
|
|
|
{
|
2005-02-25 15:53:22 +01:00
|
|
|
DBUG_ASSERT(sql_field->charset != 0);
|
2003-11-18 12:47:27 +01:00
|
|
|
|
2005-03-16 15:11:01 +01:00
|
|
|
if (prepare_create_field(sql_field, &blob_columns,
|
|
|
|
×tamps, ×tamps_with_niladic,
|
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes
Changes that requires code changes in other code of other storage engines.
(Note that all changes are very straightforward and one should find all issues
by compiling a --debug build and fixing all compiler errors and all
asserts in field.cc while running the test suite),
- New optional handler function introduced: reset()
This is called after every DML statement to make it easy for a handler to
statement specific cleanups.
(The only case it's not called is if force the file to be closed)
- handler::extra(HA_EXTRA_RESET) is removed. Code that was there before
should be moved to handler::reset()
- table->read_set contains a bitmap over all columns that are needed
in the query. read_row() and similar functions only needs to read these
columns
- table->write_set contains a bitmap over all columns that will be updated
in the query. write_row() and update_row() only needs to update these
columns.
The above bitmaps should now be up to date in all context
(including ALTER TABLE, filesort()).
The handler is informed of any changes to the bitmap after
fix_fields() by calling the virtual function
handler::column_bitmaps_signal(). If the handler does caching of
these bitmaps (instead of using table->read_set, table->write_set),
it should redo the caching in this code. as the signal() may be sent
several times, it's probably best to set a variable in the signal
and redo the caching on read_row() / write_row() if the variable was
set.
- Removed the read_set and write_set bitmap objects from the handler class
- Removed all column bit handling functions from the handler class.
(Now one instead uses the normal bitmap functions in my_bitmap.c instead
of handler dedicated bitmap functions)
- field->query_id is removed. One should instead instead check
table->read_set and table->write_set if a field is used in the query.
- handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and
handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now
instead use table->read_set to check for which columns to retrieve.
- If a handler needs to call Field->val() or Field->store() on columns
that are not used in the query, one should install a temporary
all-columns-used map while doing so. For this, we provide the following
functions:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
field->val();
dbug_tmp_restore_column_map(table->read_set, old_map);
and similar for the write map:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
field->val();
dbug_tmp_restore_column_map(table->write_set, old_map);
If this is not done, you will sooner or later hit a DBUG_ASSERT
in the field store() / val() functions.
(For not DBUG binaries, the dbug_tmp_restore_column_map() and
dbug_tmp_restore_column_map() are inline dummy functions and should
be optimized away be the compiler).
- If one needs to temporary set the column map for all binaries (and not
just to avoid the DBUG_ASSERT() in the Field::store() / Field::val()
methods) one should use the functions tmp_use_all_columns() and
tmp_restore_column_map() instead of the above dbug_ variants.
- All 'status' fields in the handler base class (like records,
data_file_length etc) are now stored in a 'stats' struct. This makes
it easier to know what status variables are provided by the base
handler. This requires some trivial variable names in the extra()
function.
- New virtual function handler::records(). This is called to optimize
COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true.
(stats.records is not supposed to be an exact value. It's only has to
be 'reasonable enough' for the optimizer to be able to choose a good
optimization path).
- Non virtual handler::init() function added for caching of virtual
constants from engine.
- Removed has_transactions() virtual method. Now one should instead return
HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support
transactions.
- The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument
that is to be used with 'new handler_name()' to allocate the handler
in the right area. The xxxx_create_handler() function is also
responsible for any initialization of the object before returning.
For example, one should change:
static handler *myisam_create_handler(TABLE_SHARE *table)
{
return new ha_myisam(table);
}
->
static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root)
{
return new (mem_root) ha_myisam(table);
}
- New optional virtual function: use_hidden_primary_key().
This is called in case of an update/delete when
(table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
but we don't have a primary key. This allows the handler to take precisions
in remembering any hidden primary key to able to update/delete any
found row. The default handler marks all columns to be read.
- handler::table_flags() now returns a ulonglong (to allow for more flags).
- New/changed table_flags()
- HA_HAS_RECORDS Set if ::records() is supported
- HA_NO_TRANSACTIONS Set if engine doesn't support transactions
- HA_PRIMARY_KEY_REQUIRED_FOR_DELETE
Set if we should mark all primary key columns for
read when reading rows as part of a DELETE
statement. If there is no primary key,
all columns are marked for read.
- HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some
cases (based on table->read_set)
- HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS
Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION.
- HA_DUPP_POS Renamed to HA_DUPLICATE_POS
- HA_REQUIRES_KEY_COLUMNS_FOR_DELETE
Set this if we should mark ALL key columns for
read when when reading rows as part of a DELETE
statement. In case of an update we will mark
all keys for read for which key part changed
value.
- HA_STATS_RECORDS_IS_EXACT
Set this if stats.records is exact.
(This saves us some extra records() calls
when optimizing COUNT(*))
- Removed table_flags()
- HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if
handler::records() gives an exact count() and
HA_STATS_RECORDS_IS_EXACT if stats.records is exact.
- HA_READ_RND_SAME Removed (no one supported this one)
- Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk()
- Renamed handler::dupp_pos to handler::dup_pos
- Removed not used variable handler::sortkey
Upper level handler changes:
- ha_reset() now does some overall checks and calls ::reset()
- ha_table_flags() added. This is a cached version of table_flags(). The
cache is updated on engine creation time and updated on open.
MySQL level changes (not obvious from the above):
- DBUG_ASSERT() added to check that column usage matches what is set
in the column usage bit maps. (This found a LOT of bugs in current
column marking code).
- In 5.1 before, all used columns was marked in read_set and only updated
columns was marked in write_set. Now we only mark columns for which we
need a value in read_set.
- Column bitmaps are created in open_binary_frm() and open_table_from_share().
(Before this was in table.cc)
- handler::table_flags() calls are replaced with handler::ha_table_flags()
- For calling field->val() you must have the corresponding bit set in
table->read_set. For calling field->store() you must have the
corresponding bit set in table->write_set. (There are asserts in
all store()/val() functions to catch wrong usage)
- thd->set_query_id is renamed to thd->mark_used_columns and instead
of setting this to an integer value, this has now the values:
MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE
Changed also all variables named 'set_query_id' to mark_used_columns.
- In filesort() we now inform the handler of exactly which columns are needed
doing the sort and choosing the rows.
- The TABLE_SHARE object has a 'all_set' column bitmap one can use
when one needs a column bitmap with all columns set.
(This is used for table->use_all_columns() and other places)
- The TABLE object has 3 column bitmaps:
- def_read_set Default bitmap for columns to be read
- def_write_set Default bitmap for columns to be written
- tmp_set Can be used as a temporary bitmap when needed.
The table object has also two pointer to bitmaps read_set and write_set
that the handler should use to find out which columns are used in which way.
- count() optimization now calls handler::records() instead of using
handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true).
- Added extra argument to Item::walk() to indicate if we should also
traverse sub queries.
- Added TABLE parameter to cp_buffer_from_ref()
- Don't close tables created with CREATE ... SELECT but keep them in
the table cache. (Faster usage of newly created tables).
New interfaces:
- table->clear_column_bitmaps() to initialize the bitmaps for tables
at start of new statements.
- table->column_bitmaps_set() to set up new column bitmaps and signal
the handler about this.
- table->column_bitmaps_set_no_signal() for some few cases where we need
to setup new column bitmaps but don't signal the handler (as the handler
has already been signaled about these before). Used for the momement
only in opt_range.cc when doing ROR scans.
- table->use_all_columns() to install a bitmap where all columns are marked
as use in the read and the write set.
- table->default_column_bitmaps() to install the normal read and write
column bitmaps, but not signaling the handler about this.
This is mainly used when creating TABLE instances.
- table->mark_columns_needed_for_delete(),
table->mark_columns_needed_for_delete() and
table->mark_columns_needed_for_insert() to allow us to put additional
columns in column usage maps if handler so requires.
(The handler indicates what it neads in handler->table_flags())
- table->prepare_for_position() to allow us to tell handler that it
needs to read primary key parts to be able to store them in
future table->position() calls.
(This replaces the table->file->ha_retrieve_all_pk function)
- table->mark_auto_increment_column() to tell handler are going to update
columns part of any auto_increment key.
- table->mark_columns_used_by_index() to mark all columns that is part of
an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow
it to quickly know that it only needs to read colums that are part
of the key. (The handler can also use the column map for detecting this,
but simpler/faster handler can just monitor the extra() call).
- table->mark_columns_used_by_index_no_reset() to in addition to other columns,
also mark all columns that is used by the given key.
- table->restore_column_maps_after_mark_index() to restore to default
column maps after a call to table->mark_columns_used_by_index().
- New item function register_field_in_read_map(), for marking used columns
in table->read_map. Used by filesort() to mark all used columns
- Maintain in TABLE->merge_keys set of all keys that are used in query.
(Simplices some optimization loops)
- Maintain Field->part_of_key_not_clustered which is like Field->part_of_key
but the field in the clustered key is not assumed to be part of all index.
(used in opt_range.cc for faster loops)
- dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map()
tmp_use_all_columns() and tmp_restore_column_map() functions to temporally
mark all columns as usable. The 'dbug_' version is primarily intended
inside a handler when it wants to just call Field:store() & Field::val()
functions, but don't need the column maps set for any other usage.
(ie:: bitmap_is_set() is never called)
- We can't use compare_records() to skip updates for handlers that returns
a partial column set and the read_set doesn't cover all columns in the
write set. The reason for this is that if we have a column marked only for
write we can't in the MySQL level know if the value changed or not.
The reason this worked before was that MySQL marked all to be written
columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden
bug'.
- open_table_from_share() does not anymore setup temporary MEM_ROOT
object as a thread specific variable for the handler. Instead we
send the to-be-used MEMROOT to get_new_handler().
(Simpler, faster code)
Bugs fixed:
- Column marking was not done correctly in a lot of cases.
(ALTER TABLE, when using triggers, auto_increment fields etc)
(Could potentially result in wrong values inserted in table handlers
relying on that the old column maps or field->set_query_id was correct)
Especially when it comes to triggers, there may be cases where the
old code would cause lost/wrong values for NDB and/or InnoDB tables.
- Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags:
OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG.
This allowed me to remove some wrong warnings about:
"Some non-transactional changed tables couldn't be rolled back"
- Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset
(thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose
some warnings about
"Some non-transactional changed tables couldn't be rolled back")
- Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table()
which could cause delete_table to report random failures.
- Fixed core dumps for some tests when running with --debug
- Added missing FN_LIBCHAR in mysql_rm_tmp_tables()
(This has probably caused us to not properly remove temporary files after
crash)
- slow_logs was not properly initialized, which could maybe cause
extra/lost entries in slow log.
- If we get an duplicate row on insert, change column map to read and
write all columns while retrying the operation. This is required by
the definition of REPLACE and also ensures that fields that are only
part of UPDATE are properly handled. This fixed a bug in NDB and
REPLACE where REPLACE wrongly copied some column values from the replaced
row.
- For table handler that doesn't support NULL in keys, we would give an error
when creating a primary key with NULL fields, even after the fields has been
automaticly converted to NOT NULL.
- Creating a primary key on a SPATIAL key, would fail if field was not
declared as NOT NULL.
Cleanups:
- Removed not used condition argument to setup_tables
- Removed not needed item function reset_query_id_processor().
- Field->add_index is removed. Now this is instead maintained in
(field->flags & FIELD_IN_ADD_INDEX)
- Field->fieldnr is removed (use field->field_index instead)
- New argument to filesort() to indicate that it should return a set of
row pointers (not used columns). This allowed me to remove some references
to sql_command in filesort and should also enable us to return column
results in some cases where we couldn't before.
- Changed column bitmap handling in opt_range.cc to be aligned with TABLE
bitmap, which allowed me to use bitmap functions instead of looping over
all fields to create some needed bitmaps. (Faster and smaller code)
- Broke up found too long lines
- Moved some variable declaration at start of function for better code
readability.
- Removed some not used arguments from functions.
(setup_fields(), mysql_prepare_insert_check_table())
- setup_fields() now takes an enum instead of an int for marking columns
usage.
- For internal temporary tables, use handler::write_row(),
handler::delete_row() and handler::update_row() instead of
handler::ha_xxxx() for faster execution.
- Changed some constants to enum's and define's.
- Using separate column read and write sets allows for easier checking
of timestamp field was set by statement.
- Remove calls to free_io_cache() as this is now done automaticly in ha_reset()
- Don't build table->normalized_path as this is now identical to table->path
(after bar's fixes to convert filenames)
- Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to
do comparision with the 'convert-dbug-for-diff' tool.
Things left to do in 5.1:
- We wrongly log failed CREATE TABLE ... SELECT in some cases when using
row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result)
Mats has promised to look into this.
- Test that my fix for CREATE TABLE ... SELECT is indeed correct.
(I added several test cases for this, but in this case it's better that
someone else also tests this throughly).
Lars has promosed to do this.
2006-06-04 17:52:22 +02:00
|
|
|
file->ha_table_flags()))
|
2004-01-15 18:06:22 +01:00
|
|
|
DBUG_RETURN(-1);
|
2005-03-22 14:48:06 +01:00
|
|
|
if (sql_field->sql_type == MYSQL_TYPE_VARCHAR)
|
2005-03-04 22:14:35 +01:00
|
|
|
create_info->varchar= 1;
|
2005-05-13 10:11:50 +02:00
|
|
|
sql_field->offset= record_offset;
|
2000-07-31 21:29:14 +02:00
|
|
|
if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
|
|
|
|
auto_increment++;
|
2005-05-13 10:11:50 +02:00
|
|
|
record_offset+= sql_field->pack_length;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2004-04-02 08:12:53 +02:00
|
|
|
if (timestamps_with_niladic > 1)
|
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_TOO_MUCH_AUTO_TIMESTAMP_COLS,
|
|
|
|
ER(ER_TOO_MUCH_AUTO_TIMESTAMP_COLS), MYF(0));
|
2004-04-02 08:12:53 +02:00
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
if (auto_increment > 1)
|
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_WRONG_AUTO_KEY, ER(ER_WRONG_AUTO_KEY), MYF(0));
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
|
|
|
if (auto_increment &&
|
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes
Changes that requires code changes in other code of other storage engines.
(Note that all changes are very straightforward and one should find all issues
by compiling a --debug build and fixing all compiler errors and all
asserts in field.cc while running the test suite),
- New optional handler function introduced: reset()
This is called after every DML statement to make it easy for a handler to
statement specific cleanups.
(The only case it's not called is if force the file to be closed)
- handler::extra(HA_EXTRA_RESET) is removed. Code that was there before
should be moved to handler::reset()
- table->read_set contains a bitmap over all columns that are needed
in the query. read_row() and similar functions only needs to read these
columns
- table->write_set contains a bitmap over all columns that will be updated
in the query. write_row() and update_row() only needs to update these
columns.
The above bitmaps should now be up to date in all context
(including ALTER TABLE, filesort()).
The handler is informed of any changes to the bitmap after
fix_fields() by calling the virtual function
handler::column_bitmaps_signal(). If the handler does caching of
these bitmaps (instead of using table->read_set, table->write_set),
it should redo the caching in this code. as the signal() may be sent
several times, it's probably best to set a variable in the signal
and redo the caching on read_row() / write_row() if the variable was
set.
- Removed the read_set and write_set bitmap objects from the handler class
- Removed all column bit handling functions from the handler class.
(Now one instead uses the normal bitmap functions in my_bitmap.c instead
of handler dedicated bitmap functions)
- field->query_id is removed. One should instead instead check
table->read_set and table->write_set if a field is used in the query.
- handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and
handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now
instead use table->read_set to check for which columns to retrieve.
- If a handler needs to call Field->val() or Field->store() on columns
that are not used in the query, one should install a temporary
all-columns-used map while doing so. For this, we provide the following
functions:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
field->val();
dbug_tmp_restore_column_map(table->read_set, old_map);
and similar for the write map:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
field->val();
dbug_tmp_restore_column_map(table->write_set, old_map);
If this is not done, you will sooner or later hit a DBUG_ASSERT
in the field store() / val() functions.
(For not DBUG binaries, the dbug_tmp_restore_column_map() and
dbug_tmp_restore_column_map() are inline dummy functions and should
be optimized away be the compiler).
- If one needs to temporary set the column map for all binaries (and not
just to avoid the DBUG_ASSERT() in the Field::store() / Field::val()
methods) one should use the functions tmp_use_all_columns() and
tmp_restore_column_map() instead of the above dbug_ variants.
- All 'status' fields in the handler base class (like records,
data_file_length etc) are now stored in a 'stats' struct. This makes
it easier to know what status variables are provided by the base
handler. This requires some trivial variable names in the extra()
function.
- New virtual function handler::records(). This is called to optimize
COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true.
(stats.records is not supposed to be an exact value. It's only has to
be 'reasonable enough' for the optimizer to be able to choose a good
optimization path).
- Non virtual handler::init() function added for caching of virtual
constants from engine.
- Removed has_transactions() virtual method. Now one should instead return
HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support
transactions.
- The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument
that is to be used with 'new handler_name()' to allocate the handler
in the right area. The xxxx_create_handler() function is also
responsible for any initialization of the object before returning.
For example, one should change:
static handler *myisam_create_handler(TABLE_SHARE *table)
{
return new ha_myisam(table);
}
->
static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root)
{
return new (mem_root) ha_myisam(table);
}
- New optional virtual function: use_hidden_primary_key().
This is called in case of an update/delete when
(table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
but we don't have a primary key. This allows the handler to take precisions
in remembering any hidden primary key to able to update/delete any
found row. The default handler marks all columns to be read.
- handler::table_flags() now returns a ulonglong (to allow for more flags).
- New/changed table_flags()
- HA_HAS_RECORDS Set if ::records() is supported
- HA_NO_TRANSACTIONS Set if engine doesn't support transactions
- HA_PRIMARY_KEY_REQUIRED_FOR_DELETE
Set if we should mark all primary key columns for
read when reading rows as part of a DELETE
statement. If there is no primary key,
all columns are marked for read.
- HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some
cases (based on table->read_set)
- HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS
Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION.
- HA_DUPP_POS Renamed to HA_DUPLICATE_POS
- HA_REQUIRES_KEY_COLUMNS_FOR_DELETE
Set this if we should mark ALL key columns for
read when when reading rows as part of a DELETE
statement. In case of an update we will mark
all keys for read for which key part changed
value.
- HA_STATS_RECORDS_IS_EXACT
Set this if stats.records is exact.
(This saves us some extra records() calls
when optimizing COUNT(*))
- Removed table_flags()
- HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if
handler::records() gives an exact count() and
HA_STATS_RECORDS_IS_EXACT if stats.records is exact.
- HA_READ_RND_SAME Removed (no one supported this one)
- Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk()
- Renamed handler::dupp_pos to handler::dup_pos
- Removed not used variable handler::sortkey
Upper level handler changes:
- ha_reset() now does some overall checks and calls ::reset()
- ha_table_flags() added. This is a cached version of table_flags(). The
cache is updated on engine creation time and updated on open.
MySQL level changes (not obvious from the above):
- DBUG_ASSERT() added to check that column usage matches what is set
in the column usage bit maps. (This found a LOT of bugs in current
column marking code).
- In 5.1 before, all used columns was marked in read_set and only updated
columns was marked in write_set. Now we only mark columns for which we
need a value in read_set.
- Column bitmaps are created in open_binary_frm() and open_table_from_share().
(Before this was in table.cc)
- handler::table_flags() calls are replaced with handler::ha_table_flags()
- For calling field->val() you must have the corresponding bit set in
table->read_set. For calling field->store() you must have the
corresponding bit set in table->write_set. (There are asserts in
all store()/val() functions to catch wrong usage)
- thd->set_query_id is renamed to thd->mark_used_columns and instead
of setting this to an integer value, this has now the values:
MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE
Changed also all variables named 'set_query_id' to mark_used_columns.
- In filesort() we now inform the handler of exactly which columns are needed
doing the sort and choosing the rows.
- The TABLE_SHARE object has a 'all_set' column bitmap one can use
when one needs a column bitmap with all columns set.
(This is used for table->use_all_columns() and other places)
- The TABLE object has 3 column bitmaps:
- def_read_set Default bitmap for columns to be read
- def_write_set Default bitmap for columns to be written
- tmp_set Can be used as a temporary bitmap when needed.
The table object has also two pointer to bitmaps read_set and write_set
that the handler should use to find out which columns are used in which way.
- count() optimization now calls handler::records() instead of using
handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true).
- Added extra argument to Item::walk() to indicate if we should also
traverse sub queries.
- Added TABLE parameter to cp_buffer_from_ref()
- Don't close tables created with CREATE ... SELECT but keep them in
the table cache. (Faster usage of newly created tables).
New interfaces:
- table->clear_column_bitmaps() to initialize the bitmaps for tables
at start of new statements.
- table->column_bitmaps_set() to set up new column bitmaps and signal
the handler about this.
- table->column_bitmaps_set_no_signal() for some few cases where we need
to setup new column bitmaps but don't signal the handler (as the handler
has already been signaled about these before). Used for the momement
only in opt_range.cc when doing ROR scans.
- table->use_all_columns() to install a bitmap where all columns are marked
as use in the read and the write set.
- table->default_column_bitmaps() to install the normal read and write
column bitmaps, but not signaling the handler about this.
This is mainly used when creating TABLE instances.
- table->mark_columns_needed_for_delete(),
table->mark_columns_needed_for_delete() and
table->mark_columns_needed_for_insert() to allow us to put additional
columns in column usage maps if handler so requires.
(The handler indicates what it neads in handler->table_flags())
- table->prepare_for_position() to allow us to tell handler that it
needs to read primary key parts to be able to store them in
future table->position() calls.
(This replaces the table->file->ha_retrieve_all_pk function)
- table->mark_auto_increment_column() to tell handler are going to update
columns part of any auto_increment key.
- table->mark_columns_used_by_index() to mark all columns that is part of
an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow
it to quickly know that it only needs to read colums that are part
of the key. (The handler can also use the column map for detecting this,
but simpler/faster handler can just monitor the extra() call).
- table->mark_columns_used_by_index_no_reset() to in addition to other columns,
also mark all columns that is used by the given key.
- table->restore_column_maps_after_mark_index() to restore to default
column maps after a call to table->mark_columns_used_by_index().
- New item function register_field_in_read_map(), for marking used columns
in table->read_map. Used by filesort() to mark all used columns
- Maintain in TABLE->merge_keys set of all keys that are used in query.
(Simplices some optimization loops)
- Maintain Field->part_of_key_not_clustered which is like Field->part_of_key
but the field in the clustered key is not assumed to be part of all index.
(used in opt_range.cc for faster loops)
- dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map()
tmp_use_all_columns() and tmp_restore_column_map() functions to temporally
mark all columns as usable. The 'dbug_' version is primarily intended
inside a handler when it wants to just call Field:store() & Field::val()
functions, but don't need the column maps set for any other usage.
(ie:: bitmap_is_set() is never called)
- We can't use compare_records() to skip updates for handlers that returns
a partial column set and the read_set doesn't cover all columns in the
write set. The reason for this is that if we have a column marked only for
write we can't in the MySQL level know if the value changed or not.
The reason this worked before was that MySQL marked all to be written
columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden
bug'.
- open_table_from_share() does not anymore setup temporary MEM_ROOT
object as a thread specific variable for the handler. Instead we
send the to-be-used MEMROOT to get_new_handler().
(Simpler, faster code)
Bugs fixed:
- Column marking was not done correctly in a lot of cases.
(ALTER TABLE, when using triggers, auto_increment fields etc)
(Could potentially result in wrong values inserted in table handlers
relying on that the old column maps or field->set_query_id was correct)
Especially when it comes to triggers, there may be cases where the
old code would cause lost/wrong values for NDB and/or InnoDB tables.
- Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags:
OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG.
This allowed me to remove some wrong warnings about:
"Some non-transactional changed tables couldn't be rolled back"
- Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset
(thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose
some warnings about
"Some non-transactional changed tables couldn't be rolled back")
- Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table()
which could cause delete_table to report random failures.
- Fixed core dumps for some tests when running with --debug
- Added missing FN_LIBCHAR in mysql_rm_tmp_tables()
(This has probably caused us to not properly remove temporary files after
crash)
- slow_logs was not properly initialized, which could maybe cause
extra/lost entries in slow log.
- If we get an duplicate row on insert, change column map to read and
write all columns while retrying the operation. This is required by
the definition of REPLACE and also ensures that fields that are only
part of UPDATE are properly handled. This fixed a bug in NDB and
REPLACE where REPLACE wrongly copied some column values from the replaced
row.
- For table handler that doesn't support NULL in keys, we would give an error
when creating a primary key with NULL fields, even after the fields has been
automaticly converted to NOT NULL.
- Creating a primary key on a SPATIAL key, would fail if field was not
declared as NOT NULL.
Cleanups:
- Removed not used condition argument to setup_tables
- Removed not needed item function reset_query_id_processor().
- Field->add_index is removed. Now this is instead maintained in
(field->flags & FIELD_IN_ADD_INDEX)
- Field->fieldnr is removed (use field->field_index instead)
- New argument to filesort() to indicate that it should return a set of
row pointers (not used columns). This allowed me to remove some references
to sql_command in filesort and should also enable us to return column
results in some cases where we couldn't before.
- Changed column bitmap handling in opt_range.cc to be aligned with TABLE
bitmap, which allowed me to use bitmap functions instead of looping over
all fields to create some needed bitmaps. (Faster and smaller code)
- Broke up found too long lines
- Moved some variable declaration at start of function for better code
readability.
- Removed some not used arguments from functions.
(setup_fields(), mysql_prepare_insert_check_table())
- setup_fields() now takes an enum instead of an int for marking columns
usage.
- For internal temporary tables, use handler::write_row(),
handler::delete_row() and handler::update_row() instead of
handler::ha_xxxx() for faster execution.
- Changed some constants to enum's and define's.
- Using separate column read and write sets allows for easier checking
of timestamp field was set by statement.
- Remove calls to free_io_cache() as this is now done automaticly in ha_reset()
- Don't build table->normalized_path as this is now identical to table->path
(after bar's fixes to convert filenames)
- Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to
do comparision with the 'convert-dbug-for-diff' tool.
Things left to do in 5.1:
- We wrongly log failed CREATE TABLE ... SELECT in some cases when using
row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result)
Mats has promised to look into this.
- Test that my fix for CREATE TABLE ... SELECT is indeed correct.
(I added several test cases for this, but in this case it's better that
someone else also tests this throughly).
Lars has promosed to do this.
2006-06-04 17:52:22 +02:00
|
|
|
(file->ha_table_flags() & HA_NO_AUTO_INCREMENT))
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT,
|
|
|
|
ER(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT), MYF(0));
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
|
|
|
|
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes
Changes that requires code changes in other code of other storage engines.
(Note that all changes are very straightforward and one should find all issues
by compiling a --debug build and fixing all compiler errors and all
asserts in field.cc while running the test suite),
- New optional handler function introduced: reset()
This is called after every DML statement to make it easy for a handler to
statement specific cleanups.
(The only case it's not called is if force the file to be closed)
- handler::extra(HA_EXTRA_RESET) is removed. Code that was there before
should be moved to handler::reset()
- table->read_set contains a bitmap over all columns that are needed
in the query. read_row() and similar functions only needs to read these
columns
- table->write_set contains a bitmap over all columns that will be updated
in the query. write_row() and update_row() only needs to update these
columns.
The above bitmaps should now be up to date in all context
(including ALTER TABLE, filesort()).
The handler is informed of any changes to the bitmap after
fix_fields() by calling the virtual function
handler::column_bitmaps_signal(). If the handler does caching of
these bitmaps (instead of using table->read_set, table->write_set),
it should redo the caching in this code. as the signal() may be sent
several times, it's probably best to set a variable in the signal
and redo the caching on read_row() / write_row() if the variable was
set.
- Removed the read_set and write_set bitmap objects from the handler class
- Removed all column bit handling functions from the handler class.
(Now one instead uses the normal bitmap functions in my_bitmap.c instead
of handler dedicated bitmap functions)
- field->query_id is removed. One should instead instead check
table->read_set and table->write_set if a field is used in the query.
- handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and
handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now
instead use table->read_set to check for which columns to retrieve.
- If a handler needs to call Field->val() or Field->store() on columns
that are not used in the query, one should install a temporary
all-columns-used map while doing so. For this, we provide the following
functions:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
field->val();
dbug_tmp_restore_column_map(table->read_set, old_map);
and similar for the write map:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
field->val();
dbug_tmp_restore_column_map(table->write_set, old_map);
If this is not done, you will sooner or later hit a DBUG_ASSERT
in the field store() / val() functions.
(For not DBUG binaries, the dbug_tmp_restore_column_map() and
dbug_tmp_restore_column_map() are inline dummy functions and should
be optimized away be the compiler).
- If one needs to temporary set the column map for all binaries (and not
just to avoid the DBUG_ASSERT() in the Field::store() / Field::val()
methods) one should use the functions tmp_use_all_columns() and
tmp_restore_column_map() instead of the above dbug_ variants.
- All 'status' fields in the handler base class (like records,
data_file_length etc) are now stored in a 'stats' struct. This makes
it easier to know what status variables are provided by the base
handler. This requires some trivial variable names in the extra()
function.
- New virtual function handler::records(). This is called to optimize
COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true.
(stats.records is not supposed to be an exact value. It's only has to
be 'reasonable enough' for the optimizer to be able to choose a good
optimization path).
- Non virtual handler::init() function added for caching of virtual
constants from engine.
- Removed has_transactions() virtual method. Now one should instead return
HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support
transactions.
- The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument
that is to be used with 'new handler_name()' to allocate the handler
in the right area. The xxxx_create_handler() function is also
responsible for any initialization of the object before returning.
For example, one should change:
static handler *myisam_create_handler(TABLE_SHARE *table)
{
return new ha_myisam(table);
}
->
static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root)
{
return new (mem_root) ha_myisam(table);
}
- New optional virtual function: use_hidden_primary_key().
This is called in case of an update/delete when
(table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
but we don't have a primary key. This allows the handler to take precisions
in remembering any hidden primary key to able to update/delete any
found row. The default handler marks all columns to be read.
- handler::table_flags() now returns a ulonglong (to allow for more flags).
- New/changed table_flags()
- HA_HAS_RECORDS Set if ::records() is supported
- HA_NO_TRANSACTIONS Set if engine doesn't support transactions
- HA_PRIMARY_KEY_REQUIRED_FOR_DELETE
Set if we should mark all primary key columns for
read when reading rows as part of a DELETE
statement. If there is no primary key,
all columns are marked for read.
- HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some
cases (based on table->read_set)
- HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS
Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION.
- HA_DUPP_POS Renamed to HA_DUPLICATE_POS
- HA_REQUIRES_KEY_COLUMNS_FOR_DELETE
Set this if we should mark ALL key columns for
read when when reading rows as part of a DELETE
statement. In case of an update we will mark
all keys for read for which key part changed
value.
- HA_STATS_RECORDS_IS_EXACT
Set this if stats.records is exact.
(This saves us some extra records() calls
when optimizing COUNT(*))
- Removed table_flags()
- HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if
handler::records() gives an exact count() and
HA_STATS_RECORDS_IS_EXACT if stats.records is exact.
- HA_READ_RND_SAME Removed (no one supported this one)
- Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk()
- Renamed handler::dupp_pos to handler::dup_pos
- Removed not used variable handler::sortkey
Upper level handler changes:
- ha_reset() now does some overall checks and calls ::reset()
- ha_table_flags() added. This is a cached version of table_flags(). The
cache is updated on engine creation time and updated on open.
MySQL level changes (not obvious from the above):
- DBUG_ASSERT() added to check that column usage matches what is set
in the column usage bit maps. (This found a LOT of bugs in current
column marking code).
- In 5.1 before, all used columns was marked in read_set and only updated
columns was marked in write_set. Now we only mark columns for which we
need a value in read_set.
- Column bitmaps are created in open_binary_frm() and open_table_from_share().
(Before this was in table.cc)
- handler::table_flags() calls are replaced with handler::ha_table_flags()
- For calling field->val() you must have the corresponding bit set in
table->read_set. For calling field->store() you must have the
corresponding bit set in table->write_set. (There are asserts in
all store()/val() functions to catch wrong usage)
- thd->set_query_id is renamed to thd->mark_used_columns and instead
of setting this to an integer value, this has now the values:
MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE
Changed also all variables named 'set_query_id' to mark_used_columns.
- In filesort() we now inform the handler of exactly which columns are needed
doing the sort and choosing the rows.
- The TABLE_SHARE object has a 'all_set' column bitmap one can use
when one needs a column bitmap with all columns set.
(This is used for table->use_all_columns() and other places)
- The TABLE object has 3 column bitmaps:
- def_read_set Default bitmap for columns to be read
- def_write_set Default bitmap for columns to be written
- tmp_set Can be used as a temporary bitmap when needed.
The table object has also two pointer to bitmaps read_set and write_set
that the handler should use to find out which columns are used in which way.
- count() optimization now calls handler::records() instead of using
handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true).
- Added extra argument to Item::walk() to indicate if we should also
traverse sub queries.
- Added TABLE parameter to cp_buffer_from_ref()
- Don't close tables created with CREATE ... SELECT but keep them in
the table cache. (Faster usage of newly created tables).
New interfaces:
- table->clear_column_bitmaps() to initialize the bitmaps for tables
at start of new statements.
- table->column_bitmaps_set() to set up new column bitmaps and signal
the handler about this.
- table->column_bitmaps_set_no_signal() for some few cases where we need
to setup new column bitmaps but don't signal the handler (as the handler
has already been signaled about these before). Used for the momement
only in opt_range.cc when doing ROR scans.
- table->use_all_columns() to install a bitmap where all columns are marked
as use in the read and the write set.
- table->default_column_bitmaps() to install the normal read and write
column bitmaps, but not signaling the handler about this.
This is mainly used when creating TABLE instances.
- table->mark_columns_needed_for_delete(),
table->mark_columns_needed_for_delete() and
table->mark_columns_needed_for_insert() to allow us to put additional
columns in column usage maps if handler so requires.
(The handler indicates what it neads in handler->table_flags())
- table->prepare_for_position() to allow us to tell handler that it
needs to read primary key parts to be able to store them in
future table->position() calls.
(This replaces the table->file->ha_retrieve_all_pk function)
- table->mark_auto_increment_column() to tell handler are going to update
columns part of any auto_increment key.
- table->mark_columns_used_by_index() to mark all columns that is part of
an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow
it to quickly know that it only needs to read colums that are part
of the key. (The handler can also use the column map for detecting this,
but simpler/faster handler can just monitor the extra() call).
- table->mark_columns_used_by_index_no_reset() to in addition to other columns,
also mark all columns that is used by the given key.
- table->restore_column_maps_after_mark_index() to restore to default
column maps after a call to table->mark_columns_used_by_index().
- New item function register_field_in_read_map(), for marking used columns
in table->read_map. Used by filesort() to mark all used columns
- Maintain in TABLE->merge_keys set of all keys that are used in query.
(Simplices some optimization loops)
- Maintain Field->part_of_key_not_clustered which is like Field->part_of_key
but the field in the clustered key is not assumed to be part of all index.
(used in opt_range.cc for faster loops)
- dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map()
tmp_use_all_columns() and tmp_restore_column_map() functions to temporally
mark all columns as usable. The 'dbug_' version is primarily intended
inside a handler when it wants to just call Field:store() & Field::val()
functions, but don't need the column maps set for any other usage.
(ie:: bitmap_is_set() is never called)
- We can't use compare_records() to skip updates for handlers that returns
a partial column set and the read_set doesn't cover all columns in the
write set. The reason for this is that if we have a column marked only for
write we can't in the MySQL level know if the value changed or not.
The reason this worked before was that MySQL marked all to be written
columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden
bug'.
- open_table_from_share() does not anymore setup temporary MEM_ROOT
object as a thread specific variable for the handler. Instead we
send the to-be-used MEMROOT to get_new_handler().
(Simpler, faster code)
Bugs fixed:
- Column marking was not done correctly in a lot of cases.
(ALTER TABLE, when using triggers, auto_increment fields etc)
(Could potentially result in wrong values inserted in table handlers
relying on that the old column maps or field->set_query_id was correct)
Especially when it comes to triggers, there may be cases where the
old code would cause lost/wrong values for NDB and/or InnoDB tables.
- Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags:
OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG.
This allowed me to remove some wrong warnings about:
"Some non-transactional changed tables couldn't be rolled back"
- Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset
(thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose
some warnings about
"Some non-transactional changed tables couldn't be rolled back")
- Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table()
which could cause delete_table to report random failures.
- Fixed core dumps for some tests when running with --debug
- Added missing FN_LIBCHAR in mysql_rm_tmp_tables()
(This has probably caused us to not properly remove temporary files after
crash)
- slow_logs was not properly initialized, which could maybe cause
extra/lost entries in slow log.
- If we get an duplicate row on insert, change column map to read and
write all columns while retrying the operation. This is required by
the definition of REPLACE and also ensures that fields that are only
part of UPDATE are properly handled. This fixed a bug in NDB and
REPLACE where REPLACE wrongly copied some column values from the replaced
row.
- For table handler that doesn't support NULL in keys, we would give an error
when creating a primary key with NULL fields, even after the fields has been
automaticly converted to NOT NULL.
- Creating a primary key on a SPATIAL key, would fail if field was not
declared as NOT NULL.
Cleanups:
- Removed not used condition argument to setup_tables
- Removed not needed item function reset_query_id_processor().
- Field->add_index is removed. Now this is instead maintained in
(field->flags & FIELD_IN_ADD_INDEX)
- Field->fieldnr is removed (use field->field_index instead)
- New argument to filesort() to indicate that it should return a set of
row pointers (not used columns). This allowed me to remove some references
to sql_command in filesort and should also enable us to return column
results in some cases where we couldn't before.
- Changed column bitmap handling in opt_range.cc to be aligned with TABLE
bitmap, which allowed me to use bitmap functions instead of looping over
all fields to create some needed bitmaps. (Faster and smaller code)
- Broke up found too long lines
- Moved some variable declaration at start of function for better code
readability.
- Removed some not used arguments from functions.
(setup_fields(), mysql_prepare_insert_check_table())
- setup_fields() now takes an enum instead of an int for marking columns
usage.
- For internal temporary tables, use handler::write_row(),
handler::delete_row() and handler::update_row() instead of
handler::ha_xxxx() for faster execution.
- Changed some constants to enum's and define's.
- Using separate column read and write sets allows for easier checking
of timestamp field was set by statement.
- Remove calls to free_io_cache() as this is now done automaticly in ha_reset()
- Don't build table->normalized_path as this is now identical to table->path
(after bar's fixes to convert filenames)
- Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to
do comparision with the 'convert-dbug-for-diff' tool.
Things left to do in 5.1:
- We wrongly log failed CREATE TABLE ... SELECT in some cases when using
row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result)
Mats has promised to look into this.
- Test that my fix for CREATE TABLE ... SELECT is indeed correct.
(I added several test cases for this, but in this case it's better that
someone else also tests this throughly).
Lars has promosed to do this.
2006-06-04 17:52:22 +02:00
|
|
|
if (blob_columns && (file->ha_table_flags() & HA_NO_BLOBS))
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_TABLE_CANT_HANDLE_BLOB, ER(ER_TABLE_CANT_HANDLE_BLOB),
|
|
|
|
MYF(0));
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create keys */
|
2000-12-10 20:10:03 +01:00
|
|
|
|
2005-04-01 14:04:50 +02:00
|
|
|
List_iterator<Key> key_iterator(*keys), key_iterator2(*keys);
|
2004-03-30 19:22:14 +02:00
|
|
|
uint key_parts=0, fk_key_count=0;
|
2002-01-02 20:29:41 +01:00
|
|
|
bool primary_key=0,unique_key=0;
|
2004-04-21 12:15:43 +02:00
|
|
|
Key *key, *key2;
|
2002-01-05 21:51:42 +01:00
|
|
|
uint tmp, key_number;
|
2004-04-21 12:15:43 +02:00
|
|
|
/* special marker for keys to be ignored */
|
|
|
|
static char ignore_key[1];
|
2000-12-10 20:10:03 +01:00
|
|
|
|
2002-01-02 20:29:41 +01:00
|
|
|
/* Calculate number of key segements */
|
2004-04-08 16:56:45 +02:00
|
|
|
*key_count= 0;
|
2000-12-10 20:10:03 +01:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
while ((key=key_iterator++))
|
|
|
|
{
|
2006-01-12 10:05:07 +01:00
|
|
|
DBUG_PRINT("info", ("key name: '%s' type: %d", key->name ? key->name :
|
|
|
|
"(none)" , key->type));
|
2002-06-02 20:22:20 +02:00
|
|
|
if (key->type == Key::FOREIGN_KEY)
|
|
|
|
{
|
|
|
|
fk_key_count++;
|
|
|
|
foreign_key *fk_key= (foreign_key*) key;
|
|
|
|
if (fk_key->ref_columns.elements &&
|
|
|
|
fk_key->ref_columns.elements != fk_key->columns.elements)
|
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_WRONG_FK_DEF, MYF(0),
|
|
|
|
(fk_key->name ? fk_key->name : "foreign key without name"),
|
|
|
|
ER(ER_KEY_REF_DO_NOT_MATCH_TABLE_REF));
|
2002-06-02 20:22:20 +02:00
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
2004-04-08 16:56:45 +02:00
|
|
|
(*key_count)++;
|
2004-06-23 12:29:05 +02:00
|
|
|
tmp=file->max_key_parts();
|
2000-07-31 21:29:14 +02:00
|
|
|
if (key->columns.elements > tmp)
|
|
|
|
{
|
|
|
|
my_error(ER_TOO_MANY_KEY_PARTS,MYF(0),tmp);
|
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
2002-06-02 20:22:20 +02:00
|
|
|
if (key->name && strlen(key->name) > NAME_LEN)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2002-06-02 20:22:20 +02:00
|
|
|
my_error(ER_TOO_LONG_IDENT, MYF(0), key->name);
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
2004-04-21 12:15:43 +02:00
|
|
|
key_iterator2.rewind ();
|
2004-05-11 23:29:52 +02:00
|
|
|
if (key->type != Key::FOREIGN_KEY)
|
2004-04-21 12:15:43 +02:00
|
|
|
{
|
2004-05-11 23:29:52 +02:00
|
|
|
while ((key2 = key_iterator2++) != key)
|
2004-04-21 12:15:43 +02:00
|
|
|
{
|
2004-05-14 08:02:06 +02:00
|
|
|
/*
|
2004-05-15 10:57:40 +02:00
|
|
|
foreign_key_prefix(key, key2) returns 0 if key or key2, or both, is
|
|
|
|
'generated', and a generated key is a prefix of the other key.
|
|
|
|
Then we do not need the generated shorter key.
|
2004-05-14 08:02:06 +02:00
|
|
|
*/
|
2004-05-15 10:57:40 +02:00
|
|
|
if ((key2->type != Key::FOREIGN_KEY &&
|
|
|
|
key2->name != ignore_key &&
|
|
|
|
!foreign_key_prefix(key, key2)))
|
2004-05-11 23:29:52 +02:00
|
|
|
{
|
2004-05-14 12:49:18 +02:00
|
|
|
/* TODO: issue warning message */
|
2004-05-11 23:29:52 +02:00
|
|
|
/* mark that the generated key should be ignored */
|
|
|
|
if (!key2->generated ||
|
|
|
|
(key->generated && key->columns.elements <
|
|
|
|
key2->columns.elements))
|
|
|
|
key->name= ignore_key;
|
|
|
|
else
|
|
|
|
{
|
2004-05-14 12:49:18 +02:00
|
|
|
key2->name= ignore_key;
|
|
|
|
key_parts-= key2->columns.elements;
|
|
|
|
(*key_count)--;
|
2004-05-11 23:29:52 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2004-04-21 12:15:43 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (key->name != ignore_key)
|
|
|
|
key_parts+=key->columns.elements;
|
|
|
|
else
|
|
|
|
(*key_count)--;
|
2006-01-12 10:05:07 +01:00
|
|
|
if (key->name && !tmp_table && (key->type != Key::PRIMARY) &&
|
2004-03-13 22:31:30 +01:00
|
|
|
!my_strcasecmp(system_charset_info,key->name,primary_key_name))
|
|
|
|
{
|
|
|
|
my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name);
|
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
2000-12-10 20:10:03 +01:00
|
|
|
}
|
2004-06-23 12:29:05 +02:00
|
|
|
tmp=file->max_keys();
|
2004-04-08 16:56:45 +02:00
|
|
|
if (*key_count > tmp)
|
2002-06-02 20:22:20 +02:00
|
|
|
{
|
|
|
|
my_error(ER_TOO_MANY_KEYS,MYF(0),tmp);
|
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
2000-12-10 20:10:03 +01:00
|
|
|
|
2006-01-12 10:05:07 +01:00
|
|
|
(*key_info_buffer)= key_info= (KEY*) sql_calloc(sizeof(KEY) * (*key_count));
|
2000-12-10 20:10:03 +01:00
|
|
|
key_part_info=(KEY_PART_INFO*) sql_calloc(sizeof(KEY_PART_INFO)*key_parts);
|
2005-04-01 14:04:50 +02:00
|
|
|
if (!*key_info_buffer || ! key_part_info)
|
2000-12-10 20:10:03 +01:00
|
|
|
DBUG_RETURN(-1); // Out of memory
|
|
|
|
|
2002-01-02 20:29:41 +01:00
|
|
|
key_iterator.rewind();
|
2002-01-05 21:51:42 +01:00
|
|
|
key_number=0;
|
2002-11-15 15:37:44 +01:00
|
|
|
for (; (key=key_iterator++) ; key_number++)
|
2000-12-10 20:10:03 +01:00
|
|
|
{
|
|
|
|
uint key_length=0;
|
|
|
|
key_part_spec *column;
|
|
|
|
|
2004-04-21 12:15:43 +02:00
|
|
|
if (key->name == ignore_key)
|
|
|
|
{
|
|
|
|
/* ignore redundant keys */
|
|
|
|
do
|
|
|
|
key=key_iterator++;
|
|
|
|
while (key && key->name == ignore_key);
|
|
|
|
if (!key)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2006-05-03 14:59:17 +02:00
|
|
|
switch (key->type) {
|
2002-11-15 15:37:44 +01:00
|
|
|
case Key::MULTIPLE:
|
2004-05-11 23:29:52 +02:00
|
|
|
key_info->flags= 0;
|
2004-04-08 16:56:45 +02:00
|
|
|
break;
|
2002-11-15 15:37:44 +01:00
|
|
|
case Key::FULLTEXT:
|
2004-05-11 23:29:52 +02:00
|
|
|
key_info->flags= HA_FULLTEXT;
|
2006-05-03 18:40:52 +02:00
|
|
|
if ((key_info->parser_name= &key->key_create_info.parser_name)->str)
|
2005-11-06 13:13:06 +01:00
|
|
|
key_info->flags|= HA_USES_PARSER;
|
2006-05-03 14:59:17 +02:00
|
|
|
else
|
|
|
|
key_info->parser_name= 0;
|
2004-04-08 16:56:45 +02:00
|
|
|
break;
|
2002-11-15 15:37:44 +01:00
|
|
|
case Key::SPATIAL:
|
2004-01-15 18:06:22 +01:00
|
|
|
#ifdef HAVE_SPATIAL
|
2004-05-11 23:29:52 +02:00
|
|
|
key_info->flags= HA_SPATIAL;
|
2004-04-08 16:56:45 +02:00
|
|
|
break;
|
2004-01-15 18:06:22 +01:00
|
|
|
#else
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_FEATURE_DISABLED, MYF(0),
|
|
|
|
sym_group_geom.name, sym_group_geom.needed_define);
|
2004-01-15 18:06:22 +01:00
|
|
|
DBUG_RETURN(-1);
|
|
|
|
#endif
|
2002-06-02 20:22:20 +02:00
|
|
|
case Key::FOREIGN_KEY:
|
|
|
|
key_number--; // Skip this key
|
|
|
|
continue;
|
|
|
|
default:
|
2004-05-11 23:29:52 +02:00
|
|
|
key_info->flags = HA_NOSAME;
|
|
|
|
break;
|
2002-02-22 12:24:42 +01:00
|
|
|
}
|
2004-05-11 23:29:52 +02:00
|
|
|
if (key->generated)
|
|
|
|
key_info->flags|= HA_GENERATED_KEY;
|
2002-02-22 12:24:42 +01:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
key_info->key_parts=(uint8) key->columns.elements;
|
|
|
|
key_info->key_part=key_part_info;
|
2002-01-05 21:51:42 +01:00
|
|
|
key_info->usable_key_parts= key_number;
|
2006-05-03 18:40:52 +02:00
|
|
|
key_info->algorithm= key->key_create_info.algorithm;
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2001-10-23 10:52:51 +02:00
|
|
|
if (key->type == Key::FULLTEXT)
|
|
|
|
{
|
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes
Changes that requires code changes in other code of other storage engines.
(Note that all changes are very straightforward and one should find all issues
by compiling a --debug build and fixing all compiler errors and all
asserts in field.cc while running the test suite),
- New optional handler function introduced: reset()
This is called after every DML statement to make it easy for a handler to
statement specific cleanups.
(The only case it's not called is if force the file to be closed)
- handler::extra(HA_EXTRA_RESET) is removed. Code that was there before
should be moved to handler::reset()
- table->read_set contains a bitmap over all columns that are needed
in the query. read_row() and similar functions only needs to read these
columns
- table->write_set contains a bitmap over all columns that will be updated
in the query. write_row() and update_row() only needs to update these
columns.
The above bitmaps should now be up to date in all context
(including ALTER TABLE, filesort()).
The handler is informed of any changes to the bitmap after
fix_fields() by calling the virtual function
handler::column_bitmaps_signal(). If the handler does caching of
these bitmaps (instead of using table->read_set, table->write_set),
it should redo the caching in this code. as the signal() may be sent
several times, it's probably best to set a variable in the signal
and redo the caching on read_row() / write_row() if the variable was
set.
- Removed the read_set and write_set bitmap objects from the handler class
- Removed all column bit handling functions from the handler class.
(Now one instead uses the normal bitmap functions in my_bitmap.c instead
of handler dedicated bitmap functions)
- field->query_id is removed. One should instead instead check
table->read_set and table->write_set if a field is used in the query.
- handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and
handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now
instead use table->read_set to check for which columns to retrieve.
- If a handler needs to call Field->val() or Field->store() on columns
that are not used in the query, one should install a temporary
all-columns-used map while doing so. For this, we provide the following
functions:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
field->val();
dbug_tmp_restore_column_map(table->read_set, old_map);
and similar for the write map:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
field->val();
dbug_tmp_restore_column_map(table->write_set, old_map);
If this is not done, you will sooner or later hit a DBUG_ASSERT
in the field store() / val() functions.
(For not DBUG binaries, the dbug_tmp_restore_column_map() and
dbug_tmp_restore_column_map() are inline dummy functions and should
be optimized away be the compiler).
- If one needs to temporary set the column map for all binaries (and not
just to avoid the DBUG_ASSERT() in the Field::store() / Field::val()
methods) one should use the functions tmp_use_all_columns() and
tmp_restore_column_map() instead of the above dbug_ variants.
- All 'status' fields in the handler base class (like records,
data_file_length etc) are now stored in a 'stats' struct. This makes
it easier to know what status variables are provided by the base
handler. This requires some trivial variable names in the extra()
function.
- New virtual function handler::records(). This is called to optimize
COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true.
(stats.records is not supposed to be an exact value. It's only has to
be 'reasonable enough' for the optimizer to be able to choose a good
optimization path).
- Non virtual handler::init() function added for caching of virtual
constants from engine.
- Removed has_transactions() virtual method. Now one should instead return
HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support
transactions.
- The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument
that is to be used with 'new handler_name()' to allocate the handler
in the right area. The xxxx_create_handler() function is also
responsible for any initialization of the object before returning.
For example, one should change:
static handler *myisam_create_handler(TABLE_SHARE *table)
{
return new ha_myisam(table);
}
->
static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root)
{
return new (mem_root) ha_myisam(table);
}
- New optional virtual function: use_hidden_primary_key().
This is called in case of an update/delete when
(table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
but we don't have a primary key. This allows the handler to take precisions
in remembering any hidden primary key to able to update/delete any
found row. The default handler marks all columns to be read.
- handler::table_flags() now returns a ulonglong (to allow for more flags).
- New/changed table_flags()
- HA_HAS_RECORDS Set if ::records() is supported
- HA_NO_TRANSACTIONS Set if engine doesn't support transactions
- HA_PRIMARY_KEY_REQUIRED_FOR_DELETE
Set if we should mark all primary key columns for
read when reading rows as part of a DELETE
statement. If there is no primary key,
all columns are marked for read.
- HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some
cases (based on table->read_set)
- HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS
Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION.
- HA_DUPP_POS Renamed to HA_DUPLICATE_POS
- HA_REQUIRES_KEY_COLUMNS_FOR_DELETE
Set this if we should mark ALL key columns for
read when when reading rows as part of a DELETE
statement. In case of an update we will mark
all keys for read for which key part changed
value.
- HA_STATS_RECORDS_IS_EXACT
Set this if stats.records is exact.
(This saves us some extra records() calls
when optimizing COUNT(*))
- Removed table_flags()
- HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if
handler::records() gives an exact count() and
HA_STATS_RECORDS_IS_EXACT if stats.records is exact.
- HA_READ_RND_SAME Removed (no one supported this one)
- Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk()
- Renamed handler::dupp_pos to handler::dup_pos
- Removed not used variable handler::sortkey
Upper level handler changes:
- ha_reset() now does some overall checks and calls ::reset()
- ha_table_flags() added. This is a cached version of table_flags(). The
cache is updated on engine creation time and updated on open.
MySQL level changes (not obvious from the above):
- DBUG_ASSERT() added to check that column usage matches what is set
in the column usage bit maps. (This found a LOT of bugs in current
column marking code).
- In 5.1 before, all used columns was marked in read_set and only updated
columns was marked in write_set. Now we only mark columns for which we
need a value in read_set.
- Column bitmaps are created in open_binary_frm() and open_table_from_share().
(Before this was in table.cc)
- handler::table_flags() calls are replaced with handler::ha_table_flags()
- For calling field->val() you must have the corresponding bit set in
table->read_set. For calling field->store() you must have the
corresponding bit set in table->write_set. (There are asserts in
all store()/val() functions to catch wrong usage)
- thd->set_query_id is renamed to thd->mark_used_columns and instead
of setting this to an integer value, this has now the values:
MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE
Changed also all variables named 'set_query_id' to mark_used_columns.
- In filesort() we now inform the handler of exactly which columns are needed
doing the sort and choosing the rows.
- The TABLE_SHARE object has a 'all_set' column bitmap one can use
when one needs a column bitmap with all columns set.
(This is used for table->use_all_columns() and other places)
- The TABLE object has 3 column bitmaps:
- def_read_set Default bitmap for columns to be read
- def_write_set Default bitmap for columns to be written
- tmp_set Can be used as a temporary bitmap when needed.
The table object has also two pointer to bitmaps read_set and write_set
that the handler should use to find out which columns are used in which way.
- count() optimization now calls handler::records() instead of using
handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true).
- Added extra argument to Item::walk() to indicate if we should also
traverse sub queries.
- Added TABLE parameter to cp_buffer_from_ref()
- Don't close tables created with CREATE ... SELECT but keep them in
the table cache. (Faster usage of newly created tables).
New interfaces:
- table->clear_column_bitmaps() to initialize the bitmaps for tables
at start of new statements.
- table->column_bitmaps_set() to set up new column bitmaps and signal
the handler about this.
- table->column_bitmaps_set_no_signal() for some few cases where we need
to setup new column bitmaps but don't signal the handler (as the handler
has already been signaled about these before). Used for the momement
only in opt_range.cc when doing ROR scans.
- table->use_all_columns() to install a bitmap where all columns are marked
as use in the read and the write set.
- table->default_column_bitmaps() to install the normal read and write
column bitmaps, but not signaling the handler about this.
This is mainly used when creating TABLE instances.
- table->mark_columns_needed_for_delete(),
table->mark_columns_needed_for_delete() and
table->mark_columns_needed_for_insert() to allow us to put additional
columns in column usage maps if handler so requires.
(The handler indicates what it neads in handler->table_flags())
- table->prepare_for_position() to allow us to tell handler that it
needs to read primary key parts to be able to store them in
future table->position() calls.
(This replaces the table->file->ha_retrieve_all_pk function)
- table->mark_auto_increment_column() to tell handler are going to update
columns part of any auto_increment key.
- table->mark_columns_used_by_index() to mark all columns that is part of
an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow
it to quickly know that it only needs to read colums that are part
of the key. (The handler can also use the column map for detecting this,
but simpler/faster handler can just monitor the extra() call).
- table->mark_columns_used_by_index_no_reset() to in addition to other columns,
also mark all columns that is used by the given key.
- table->restore_column_maps_after_mark_index() to restore to default
column maps after a call to table->mark_columns_used_by_index().
- New item function register_field_in_read_map(), for marking used columns
in table->read_map. Used by filesort() to mark all used columns
- Maintain in TABLE->merge_keys set of all keys that are used in query.
(Simplices some optimization loops)
- Maintain Field->part_of_key_not_clustered which is like Field->part_of_key
but the field in the clustered key is not assumed to be part of all index.
(used in opt_range.cc for faster loops)
- dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map()
tmp_use_all_columns() and tmp_restore_column_map() functions to temporally
mark all columns as usable. The 'dbug_' version is primarily intended
inside a handler when it wants to just call Field:store() & Field::val()
functions, but don't need the column maps set for any other usage.
(ie:: bitmap_is_set() is never called)
- We can't use compare_records() to skip updates for handlers that returns
a partial column set and the read_set doesn't cover all columns in the
write set. The reason for this is that if we have a column marked only for
write we can't in the MySQL level know if the value changed or not.
The reason this worked before was that MySQL marked all to be written
columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden
bug'.
- open_table_from_share() does not anymore setup temporary MEM_ROOT
object as a thread specific variable for the handler. Instead we
send the to-be-used MEMROOT to get_new_handler().
(Simpler, faster code)
Bugs fixed:
- Column marking was not done correctly in a lot of cases.
(ALTER TABLE, when using triggers, auto_increment fields etc)
(Could potentially result in wrong values inserted in table handlers
relying on that the old column maps or field->set_query_id was correct)
Especially when it comes to triggers, there may be cases where the
old code would cause lost/wrong values for NDB and/or InnoDB tables.
- Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags:
OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG.
This allowed me to remove some wrong warnings about:
"Some non-transactional changed tables couldn't be rolled back"
- Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset
(thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose
some warnings about
"Some non-transactional changed tables couldn't be rolled back")
- Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table()
which could cause delete_table to report random failures.
- Fixed core dumps for some tests when running with --debug
- Added missing FN_LIBCHAR in mysql_rm_tmp_tables()
(This has probably caused us to not properly remove temporary files after
crash)
- slow_logs was not properly initialized, which could maybe cause
extra/lost entries in slow log.
- If we get an duplicate row on insert, change column map to read and
write all columns while retrying the operation. This is required by
the definition of REPLACE and also ensures that fields that are only
part of UPDATE are properly handled. This fixed a bug in NDB and
REPLACE where REPLACE wrongly copied some column values from the replaced
row.
- For table handler that doesn't support NULL in keys, we would give an error
when creating a primary key with NULL fields, even after the fields has been
automaticly converted to NOT NULL.
- Creating a primary key on a SPATIAL key, would fail if field was not
declared as NOT NULL.
Cleanups:
- Removed not used condition argument to setup_tables
- Removed not needed item function reset_query_id_processor().
- Field->add_index is removed. Now this is instead maintained in
(field->flags & FIELD_IN_ADD_INDEX)
- Field->fieldnr is removed (use field->field_index instead)
- New argument to filesort() to indicate that it should return a set of
row pointers (not used columns). This allowed me to remove some references
to sql_command in filesort and should also enable us to return column
results in some cases where we couldn't before.
- Changed column bitmap handling in opt_range.cc to be aligned with TABLE
bitmap, which allowed me to use bitmap functions instead of looping over
all fields to create some needed bitmaps. (Faster and smaller code)
- Broke up found too long lines
- Moved some variable declaration at start of function for better code
readability.
- Removed some not used arguments from functions.
(setup_fields(), mysql_prepare_insert_check_table())
- setup_fields() now takes an enum instead of an int for marking columns
usage.
- For internal temporary tables, use handler::write_row(),
handler::delete_row() and handler::update_row() instead of
handler::ha_xxxx() for faster execution.
- Changed some constants to enum's and define's.
- Using separate column read and write sets allows for easier checking
of timestamp field was set by statement.
- Remove calls to free_io_cache() as this is now done automaticly in ha_reset()
- Don't build table->normalized_path as this is now identical to table->path
(after bar's fixes to convert filenames)
- Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to
do comparision with the 'convert-dbug-for-diff' tool.
Things left to do in 5.1:
- We wrongly log failed CREATE TABLE ... SELECT in some cases when using
row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result)
Mats has promised to look into this.
- Test that my fix for CREATE TABLE ... SELECT is indeed correct.
(I added several test cases for this, but in this case it's better that
someone else also tests this throughly).
Lars has promosed to do this.
2006-06-04 17:52:22 +02:00
|
|
|
if (!(file->ha_table_flags() & HA_CAN_FULLTEXT))
|
2001-10-23 10:52:51 +02:00
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_TABLE_CANT_HANDLE_FT, ER(ER_TABLE_CANT_HANDLE_FT),
|
|
|
|
MYF(0));
|
2004-04-08 16:56:45 +02:00
|
|
|
DBUG_RETURN(-1);
|
2001-10-23 10:52:51 +02:00
|
|
|
}
|
|
|
|
}
|
2002-02-22 12:24:42 +01:00
|
|
|
/*
|
|
|
|
Make SPATIAL to be RTREE by default
|
|
|
|
SPATIAL only on BLOB or at least BINARY, this
|
2003-09-03 11:34:32 +02:00
|
|
|
actually should be replaced by special GEOM type
|
2002-02-22 12:24:42 +01:00
|
|
|
in near future when new frm file is ready
|
|
|
|
checking for proper key parts number:
|
|
|
|
*/
|
2003-09-03 11:34:32 +02:00
|
|
|
|
2003-10-15 13:40:20 +02:00
|
|
|
/* TODO: Add proper checks if handler supports key_type and algorithm */
|
2004-05-11 23:29:52 +02:00
|
|
|
if (key_info->flags & HA_SPATIAL)
|
2002-05-22 17:51:21 +02:00
|
|
|
{
|
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes
Changes that requires code changes in other code of other storage engines.
(Note that all changes are very straightforward and one should find all issues
by compiling a --debug build and fixing all compiler errors and all
asserts in field.cc while running the test suite),
- New optional handler function introduced: reset()
This is called after every DML statement to make it easy for a handler to
statement specific cleanups.
(The only case it's not called is if force the file to be closed)
- handler::extra(HA_EXTRA_RESET) is removed. Code that was there before
should be moved to handler::reset()
- table->read_set contains a bitmap over all columns that are needed
in the query. read_row() and similar functions only needs to read these
columns
- table->write_set contains a bitmap over all columns that will be updated
in the query. write_row() and update_row() only needs to update these
columns.
The above bitmaps should now be up to date in all context
(including ALTER TABLE, filesort()).
The handler is informed of any changes to the bitmap after
fix_fields() by calling the virtual function
handler::column_bitmaps_signal(). If the handler does caching of
these bitmaps (instead of using table->read_set, table->write_set),
it should redo the caching in this code. as the signal() may be sent
several times, it's probably best to set a variable in the signal
and redo the caching on read_row() / write_row() if the variable was
set.
- Removed the read_set and write_set bitmap objects from the handler class
- Removed all column bit handling functions from the handler class.
(Now one instead uses the normal bitmap functions in my_bitmap.c instead
of handler dedicated bitmap functions)
- field->query_id is removed. One should instead instead check
table->read_set and table->write_set if a field is used in the query.
- handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and
handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now
instead use table->read_set to check for which columns to retrieve.
- If a handler needs to call Field->val() or Field->store() on columns
that are not used in the query, one should install a temporary
all-columns-used map while doing so. For this, we provide the following
functions:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
field->val();
dbug_tmp_restore_column_map(table->read_set, old_map);
and similar for the write map:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
field->val();
dbug_tmp_restore_column_map(table->write_set, old_map);
If this is not done, you will sooner or later hit a DBUG_ASSERT
in the field store() / val() functions.
(For not DBUG binaries, the dbug_tmp_restore_column_map() and
dbug_tmp_restore_column_map() are inline dummy functions and should
be optimized away be the compiler).
- If one needs to temporary set the column map for all binaries (and not
just to avoid the DBUG_ASSERT() in the Field::store() / Field::val()
methods) one should use the functions tmp_use_all_columns() and
tmp_restore_column_map() instead of the above dbug_ variants.
- All 'status' fields in the handler base class (like records,
data_file_length etc) are now stored in a 'stats' struct. This makes
it easier to know what status variables are provided by the base
handler. This requires some trivial variable names in the extra()
function.
- New virtual function handler::records(). This is called to optimize
COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true.
(stats.records is not supposed to be an exact value. It's only has to
be 'reasonable enough' for the optimizer to be able to choose a good
optimization path).
- Non virtual handler::init() function added for caching of virtual
constants from engine.
- Removed has_transactions() virtual method. Now one should instead return
HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support
transactions.
- The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument
that is to be used with 'new handler_name()' to allocate the handler
in the right area. The xxxx_create_handler() function is also
responsible for any initialization of the object before returning.
For example, one should change:
static handler *myisam_create_handler(TABLE_SHARE *table)
{
return new ha_myisam(table);
}
->
static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root)
{
return new (mem_root) ha_myisam(table);
}
- New optional virtual function: use_hidden_primary_key().
This is called in case of an update/delete when
(table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
but we don't have a primary key. This allows the handler to take precisions
in remembering any hidden primary key to able to update/delete any
found row. The default handler marks all columns to be read.
- handler::table_flags() now returns a ulonglong (to allow for more flags).
- New/changed table_flags()
- HA_HAS_RECORDS Set if ::records() is supported
- HA_NO_TRANSACTIONS Set if engine doesn't support transactions
- HA_PRIMARY_KEY_REQUIRED_FOR_DELETE
Set if we should mark all primary key columns for
read when reading rows as part of a DELETE
statement. If there is no primary key,
all columns are marked for read.
- HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some
cases (based on table->read_set)
- HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS
Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION.
- HA_DUPP_POS Renamed to HA_DUPLICATE_POS
- HA_REQUIRES_KEY_COLUMNS_FOR_DELETE
Set this if we should mark ALL key columns for
read when when reading rows as part of a DELETE
statement. In case of an update we will mark
all keys for read for which key part changed
value.
- HA_STATS_RECORDS_IS_EXACT
Set this if stats.records is exact.
(This saves us some extra records() calls
when optimizing COUNT(*))
- Removed table_flags()
- HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if
handler::records() gives an exact count() and
HA_STATS_RECORDS_IS_EXACT if stats.records is exact.
- HA_READ_RND_SAME Removed (no one supported this one)
- Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk()
- Renamed handler::dupp_pos to handler::dup_pos
- Removed not used variable handler::sortkey
Upper level handler changes:
- ha_reset() now does some overall checks and calls ::reset()
- ha_table_flags() added. This is a cached version of table_flags(). The
cache is updated on engine creation time and updated on open.
MySQL level changes (not obvious from the above):
- DBUG_ASSERT() added to check that column usage matches what is set
in the column usage bit maps. (This found a LOT of bugs in current
column marking code).
- In 5.1 before, all used columns was marked in read_set and only updated
columns was marked in write_set. Now we only mark columns for which we
need a value in read_set.
- Column bitmaps are created in open_binary_frm() and open_table_from_share().
(Before this was in table.cc)
- handler::table_flags() calls are replaced with handler::ha_table_flags()
- For calling field->val() you must have the corresponding bit set in
table->read_set. For calling field->store() you must have the
corresponding bit set in table->write_set. (There are asserts in
all store()/val() functions to catch wrong usage)
- thd->set_query_id is renamed to thd->mark_used_columns and instead
of setting this to an integer value, this has now the values:
MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE
Changed also all variables named 'set_query_id' to mark_used_columns.
- In filesort() we now inform the handler of exactly which columns are needed
doing the sort and choosing the rows.
- The TABLE_SHARE object has a 'all_set' column bitmap one can use
when one needs a column bitmap with all columns set.
(This is used for table->use_all_columns() and other places)
- The TABLE object has 3 column bitmaps:
- def_read_set Default bitmap for columns to be read
- def_write_set Default bitmap for columns to be written
- tmp_set Can be used as a temporary bitmap when needed.
The table object has also two pointer to bitmaps read_set and write_set
that the handler should use to find out which columns are used in which way.
- count() optimization now calls handler::records() instead of using
handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true).
- Added extra argument to Item::walk() to indicate if we should also
traverse sub queries.
- Added TABLE parameter to cp_buffer_from_ref()
- Don't close tables created with CREATE ... SELECT but keep them in
the table cache. (Faster usage of newly created tables).
New interfaces:
- table->clear_column_bitmaps() to initialize the bitmaps for tables
at start of new statements.
- table->column_bitmaps_set() to set up new column bitmaps and signal
the handler about this.
- table->column_bitmaps_set_no_signal() for some few cases where we need
to setup new column bitmaps but don't signal the handler (as the handler
has already been signaled about these before). Used for the momement
only in opt_range.cc when doing ROR scans.
- table->use_all_columns() to install a bitmap where all columns are marked
as use in the read and the write set.
- table->default_column_bitmaps() to install the normal read and write
column bitmaps, but not signaling the handler about this.
This is mainly used when creating TABLE instances.
- table->mark_columns_needed_for_delete(),
table->mark_columns_needed_for_delete() and
table->mark_columns_needed_for_insert() to allow us to put additional
columns in column usage maps if handler so requires.
(The handler indicates what it neads in handler->table_flags())
- table->prepare_for_position() to allow us to tell handler that it
needs to read primary key parts to be able to store them in
future table->position() calls.
(This replaces the table->file->ha_retrieve_all_pk function)
- table->mark_auto_increment_column() to tell handler are going to update
columns part of any auto_increment key.
- table->mark_columns_used_by_index() to mark all columns that is part of
an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow
it to quickly know that it only needs to read colums that are part
of the key. (The handler can also use the column map for detecting this,
but simpler/faster handler can just monitor the extra() call).
- table->mark_columns_used_by_index_no_reset() to in addition to other columns,
also mark all columns that is used by the given key.
- table->restore_column_maps_after_mark_index() to restore to default
column maps after a call to table->mark_columns_used_by_index().
- New item function register_field_in_read_map(), for marking used columns
in table->read_map. Used by filesort() to mark all used columns
- Maintain in TABLE->merge_keys set of all keys that are used in query.
(Simplices some optimization loops)
- Maintain Field->part_of_key_not_clustered which is like Field->part_of_key
but the field in the clustered key is not assumed to be part of all index.
(used in opt_range.cc for faster loops)
- dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map()
tmp_use_all_columns() and tmp_restore_column_map() functions to temporally
mark all columns as usable. The 'dbug_' version is primarily intended
inside a handler when it wants to just call Field:store() & Field::val()
functions, but don't need the column maps set for any other usage.
(ie:: bitmap_is_set() is never called)
- We can't use compare_records() to skip updates for handlers that returns
a partial column set and the read_set doesn't cover all columns in the
write set. The reason for this is that if we have a column marked only for
write we can't in the MySQL level know if the value changed or not.
The reason this worked before was that MySQL marked all to be written
columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden
bug'.
- open_table_from_share() does not anymore setup temporary MEM_ROOT
object as a thread specific variable for the handler. Instead we
send the to-be-used MEMROOT to get_new_handler().
(Simpler, faster code)
Bugs fixed:
- Column marking was not done correctly in a lot of cases.
(ALTER TABLE, when using triggers, auto_increment fields etc)
(Could potentially result in wrong values inserted in table handlers
relying on that the old column maps or field->set_query_id was correct)
Especially when it comes to triggers, there may be cases where the
old code would cause lost/wrong values for NDB and/or InnoDB tables.
- Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags:
OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG.
This allowed me to remove some wrong warnings about:
"Some non-transactional changed tables couldn't be rolled back"
- Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset
(thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose
some warnings about
"Some non-transactional changed tables couldn't be rolled back")
- Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table()
which could cause delete_table to report random failures.
- Fixed core dumps for some tests when running with --debug
- Added missing FN_LIBCHAR in mysql_rm_tmp_tables()
(This has probably caused us to not properly remove temporary files after
crash)
- slow_logs was not properly initialized, which could maybe cause
extra/lost entries in slow log.
- If we get an duplicate row on insert, change column map to read and
write all columns while retrying the operation. This is required by
the definition of REPLACE and also ensures that fields that are only
part of UPDATE are properly handled. This fixed a bug in NDB and
REPLACE where REPLACE wrongly copied some column values from the replaced
row.
- For table handler that doesn't support NULL in keys, we would give an error
when creating a primary key with NULL fields, even after the fields has been
automaticly converted to NOT NULL.
- Creating a primary key on a SPATIAL key, would fail if field was not
declared as NOT NULL.
Cleanups:
- Removed not used condition argument to setup_tables
- Removed not needed item function reset_query_id_processor().
- Field->add_index is removed. Now this is instead maintained in
(field->flags & FIELD_IN_ADD_INDEX)
- Field->fieldnr is removed (use field->field_index instead)
- New argument to filesort() to indicate that it should return a set of
row pointers (not used columns). This allowed me to remove some references
to sql_command in filesort and should also enable us to return column
results in some cases where we couldn't before.
- Changed column bitmap handling in opt_range.cc to be aligned with TABLE
bitmap, which allowed me to use bitmap functions instead of looping over
all fields to create some needed bitmaps. (Faster and smaller code)
- Broke up found too long lines
- Moved some variable declaration at start of function for better code
readability.
- Removed some not used arguments from functions.
(setup_fields(), mysql_prepare_insert_check_table())
- setup_fields() now takes an enum instead of an int for marking columns
usage.
- For internal temporary tables, use handler::write_row(),
handler::delete_row() and handler::update_row() instead of
handler::ha_xxxx() for faster execution.
- Changed some constants to enum's and define's.
- Using separate column read and write sets allows for easier checking
of timestamp field was set by statement.
- Remove calls to free_io_cache() as this is now done automaticly in ha_reset()
- Don't build table->normalized_path as this is now identical to table->path
(after bar's fixes to convert filenames)
- Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to
do comparision with the 'convert-dbug-for-diff' tool.
Things left to do in 5.1:
- We wrongly log failed CREATE TABLE ... SELECT in some cases when using
row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result)
Mats has promised to look into this.
- Test that my fix for CREATE TABLE ... SELECT is indeed correct.
(I added several test cases for this, but in this case it's better that
someone else also tests this throughly).
Lars has promosed to do this.
2006-06-04 17:52:22 +02:00
|
|
|
if (!(file->ha_table_flags() & HA_CAN_RTREEKEYS))
|
2006-04-12 19:05:23 +02:00
|
|
|
{
|
|
|
|
my_message(ER_TABLE_CANT_HANDLE_SPKEYS, ER(ER_TABLE_CANT_HANDLE_SPKEYS),
|
|
|
|
MYF(0));
|
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
2002-05-22 17:51:21 +02:00
|
|
|
if (key_info->key_parts != 1)
|
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_WRONG_ARGUMENTS, MYF(0), "SPATIAL INDEX");
|
2004-04-08 16:56:45 +02:00
|
|
|
DBUG_RETURN(-1);
|
2002-02-22 12:24:42 +01:00
|
|
|
}
|
2002-05-22 17:51:21 +02:00
|
|
|
}
|
2004-12-02 13:43:51 +01:00
|
|
|
else if (key_info->algorithm == HA_KEY_ALG_RTREE)
|
2002-02-22 12:24:42 +01:00
|
|
|
{
|
2004-01-15 18:06:22 +01:00
|
|
|
#ifdef HAVE_RTREE_KEYS
|
2002-05-22 17:51:21 +02:00
|
|
|
if ((key_info->key_parts & 1) == 1)
|
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_WRONG_ARGUMENTS, MYF(0), "RTREE INDEX");
|
2002-05-22 17:51:21 +02:00
|
|
|
DBUG_RETURN(-1);
|
2002-02-22 12:24:42 +01:00
|
|
|
}
|
2003-02-12 20:55:37 +01:00
|
|
|
/* TODO: To be deleted */
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_NOT_SUPPORTED_YET, MYF(0), "RTREE INDEX");
|
2003-02-12 20:55:37 +01:00
|
|
|
DBUG_RETURN(-1);
|
2004-01-15 18:06:22 +01:00
|
|
|
#else
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_FEATURE_DISABLED, MYF(0),
|
|
|
|
sym_group_rtree.name, sym_group_rtree.needed_define);
|
2004-01-15 18:06:22 +01:00
|
|
|
DBUG_RETURN(-1);
|
|
|
|
#endif
|
2002-02-22 12:24:42 +01:00
|
|
|
}
|
2003-09-03 11:34:32 +02:00
|
|
|
|
2006-05-03 14:59:17 +02:00
|
|
|
/* Take block size from key part or table part */
|
|
|
|
/*
|
|
|
|
TODO: Add warning if block size changes. We can't do it here, as
|
|
|
|
this may depend on the size of the key
|
|
|
|
*/
|
2006-05-03 18:40:52 +02:00
|
|
|
key_info->block_size= (key->key_create_info.block_size ?
|
|
|
|
key->key_create_info.block_size :
|
2006-05-03 14:59:17 +02:00
|
|
|
create_info->key_block_size);
|
|
|
|
|
|
|
|
if (key_info->block_size)
|
|
|
|
key_info->flags|= HA_USES_BLOCK_SIZE;
|
|
|
|
|
2004-11-22 19:07:04 +01:00
|
|
|
List_iterator<key_part_spec> cols(key->columns), cols2(key->columns);
|
2003-10-15 13:40:20 +02:00
|
|
|
CHARSET_INFO *ft_key_charset=0; // for FULLTEXT
|
2000-07-31 21:29:14 +02:00
|
|
|
for (uint column_nr=0 ; (column=cols++) ; column_nr++)
|
|
|
|
{
|
2004-12-06 01:00:37 +01:00
|
|
|
uint length;
|
2004-12-02 13:43:51 +01:00
|
|
|
key_part_spec *dup_column;
|
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
it.rewind();
|
|
|
|
field=0;
|
|
|
|
while ((sql_field=it++) &&
|
2002-03-12 18:37:58 +01:00
|
|
|
my_strcasecmp(system_charset_info,
|
2004-04-08 16:56:45 +02:00
|
|
|
column->field_name,
|
|
|
|
sql_field->field_name))
|
2000-07-31 21:29:14 +02:00
|
|
|
field++;
|
|
|
|
if (!sql_field)
|
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name);
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
2004-12-02 13:43:51 +01:00
|
|
|
while ((dup_column= cols2++) != column)
|
2004-11-22 19:07:04 +01:00
|
|
|
{
|
|
|
|
if (!my_strcasecmp(system_charset_info,
|
|
|
|
column->field_name, dup_column->field_name))
|
|
|
|
{
|
|
|
|
my_printf_error(ER_DUP_FIELDNAME,
|
|
|
|
ER(ER_DUP_FIELDNAME),MYF(0),
|
|
|
|
column->field_name);
|
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cols2.rewind();
|
2003-04-23 13:44:39 +02:00
|
|
|
if (key->type == Key::FULLTEXT)
|
2003-10-15 13:40:20 +02:00
|
|
|
{
|
2004-12-06 01:00:37 +01:00
|
|
|
if ((sql_field->sql_type != MYSQL_TYPE_STRING &&
|
|
|
|
sql_field->sql_type != MYSQL_TYPE_VARCHAR &&
|
2004-04-08 16:56:45 +02:00
|
|
|
!f_is_blob(sql_field->pack_flag)) ||
|
|
|
|
sql_field->charset == &my_charset_bin ||
|
2004-06-11 13:29:16 +02:00
|
|
|
sql_field->charset->mbminlen > 1 || // ucs2 doesn't work yet
|
2004-04-08 16:56:45 +02:00
|
|
|
(ft_key_charset && sql_field->charset != ft_key_charset))
|
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_BAD_FT_COLUMN, MYF(0), column->field_name);
|
2004-04-08 16:56:45 +02:00
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
|
|
|
ft_key_charset=sql_field->charset;
|
|
|
|
/*
|
|
|
|
for fulltext keys keyseg length is 1 for blobs (it's ignored in ft
|
|
|
|
code anyway, and 0 (set to column width later) for char's. it has
|
|
|
|
to be correct col width for char's, as char data are not prefixed
|
|
|
|
with length (unlike blobs, where ft code takes data length from a
|
|
|
|
data prefix, ignoring column->length).
|
|
|
|
*/
|
|
|
|
column->length=test(f_is_blob(sql_field->pack_flag));
|
2003-10-15 13:40:20 +02:00
|
|
|
}
|
2003-09-15 11:45:42 +02:00
|
|
|
else
|
2003-10-15 13:40:20 +02:00
|
|
|
{
|
2004-04-08 16:56:45 +02:00
|
|
|
column->length*= sql_field->charset->mbmaxlen;
|
|
|
|
|
2005-09-26 11:55:52 +02:00
|
|
|
if (f_is_blob(sql_field->pack_flag) ||
|
|
|
|
(f_is_geom(sql_field->pack_flag) && key->type != Key::SPATIAL))
|
2004-04-08 16:56:45 +02:00
|
|
|
{
|
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes
Changes that requires code changes in other code of other storage engines.
(Note that all changes are very straightforward and one should find all issues
by compiling a --debug build and fixing all compiler errors and all
asserts in field.cc while running the test suite),
- New optional handler function introduced: reset()
This is called after every DML statement to make it easy for a handler to
statement specific cleanups.
(The only case it's not called is if force the file to be closed)
- handler::extra(HA_EXTRA_RESET) is removed. Code that was there before
should be moved to handler::reset()
- table->read_set contains a bitmap over all columns that are needed
in the query. read_row() and similar functions only needs to read these
columns
- table->write_set contains a bitmap over all columns that will be updated
in the query. write_row() and update_row() only needs to update these
columns.
The above bitmaps should now be up to date in all context
(including ALTER TABLE, filesort()).
The handler is informed of any changes to the bitmap after
fix_fields() by calling the virtual function
handler::column_bitmaps_signal(). If the handler does caching of
these bitmaps (instead of using table->read_set, table->write_set),
it should redo the caching in this code. as the signal() may be sent
several times, it's probably best to set a variable in the signal
and redo the caching on read_row() / write_row() if the variable was
set.
- Removed the read_set and write_set bitmap objects from the handler class
- Removed all column bit handling functions from the handler class.
(Now one instead uses the normal bitmap functions in my_bitmap.c instead
of handler dedicated bitmap functions)
- field->query_id is removed. One should instead instead check
table->read_set and table->write_set if a field is used in the query.
- handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and
handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now
instead use table->read_set to check for which columns to retrieve.
- If a handler needs to call Field->val() or Field->store() on columns
that are not used in the query, one should install a temporary
all-columns-used map while doing so. For this, we provide the following
functions:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
field->val();
dbug_tmp_restore_column_map(table->read_set, old_map);
and similar for the write map:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
field->val();
dbug_tmp_restore_column_map(table->write_set, old_map);
If this is not done, you will sooner or later hit a DBUG_ASSERT
in the field store() / val() functions.
(For not DBUG binaries, the dbug_tmp_restore_column_map() and
dbug_tmp_restore_column_map() are inline dummy functions and should
be optimized away be the compiler).
- If one needs to temporary set the column map for all binaries (and not
just to avoid the DBUG_ASSERT() in the Field::store() / Field::val()
methods) one should use the functions tmp_use_all_columns() and
tmp_restore_column_map() instead of the above dbug_ variants.
- All 'status' fields in the handler base class (like records,
data_file_length etc) are now stored in a 'stats' struct. This makes
it easier to know what status variables are provided by the base
handler. This requires some trivial variable names in the extra()
function.
- New virtual function handler::records(). This is called to optimize
COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true.
(stats.records is not supposed to be an exact value. It's only has to
be 'reasonable enough' for the optimizer to be able to choose a good
optimization path).
- Non virtual handler::init() function added for caching of virtual
constants from engine.
- Removed has_transactions() virtual method. Now one should instead return
HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support
transactions.
- The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument
that is to be used with 'new handler_name()' to allocate the handler
in the right area. The xxxx_create_handler() function is also
responsible for any initialization of the object before returning.
For example, one should change:
static handler *myisam_create_handler(TABLE_SHARE *table)
{
return new ha_myisam(table);
}
->
static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root)
{
return new (mem_root) ha_myisam(table);
}
- New optional virtual function: use_hidden_primary_key().
This is called in case of an update/delete when
(table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
but we don't have a primary key. This allows the handler to take precisions
in remembering any hidden primary key to able to update/delete any
found row. The default handler marks all columns to be read.
- handler::table_flags() now returns a ulonglong (to allow for more flags).
- New/changed table_flags()
- HA_HAS_RECORDS Set if ::records() is supported
- HA_NO_TRANSACTIONS Set if engine doesn't support transactions
- HA_PRIMARY_KEY_REQUIRED_FOR_DELETE
Set if we should mark all primary key columns for
read when reading rows as part of a DELETE
statement. If there is no primary key,
all columns are marked for read.
- HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some
cases (based on table->read_set)
- HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS
Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION.
- HA_DUPP_POS Renamed to HA_DUPLICATE_POS
- HA_REQUIRES_KEY_COLUMNS_FOR_DELETE
Set this if we should mark ALL key columns for
read when when reading rows as part of a DELETE
statement. In case of an update we will mark
all keys for read for which key part changed
value.
- HA_STATS_RECORDS_IS_EXACT
Set this if stats.records is exact.
(This saves us some extra records() calls
when optimizing COUNT(*))
- Removed table_flags()
- HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if
handler::records() gives an exact count() and
HA_STATS_RECORDS_IS_EXACT if stats.records is exact.
- HA_READ_RND_SAME Removed (no one supported this one)
- Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk()
- Renamed handler::dupp_pos to handler::dup_pos
- Removed not used variable handler::sortkey
Upper level handler changes:
- ha_reset() now does some overall checks and calls ::reset()
- ha_table_flags() added. This is a cached version of table_flags(). The
cache is updated on engine creation time and updated on open.
MySQL level changes (not obvious from the above):
- DBUG_ASSERT() added to check that column usage matches what is set
in the column usage bit maps. (This found a LOT of bugs in current
column marking code).
- In 5.1 before, all used columns was marked in read_set and only updated
columns was marked in write_set. Now we only mark columns for which we
need a value in read_set.
- Column bitmaps are created in open_binary_frm() and open_table_from_share().
(Before this was in table.cc)
- handler::table_flags() calls are replaced with handler::ha_table_flags()
- For calling field->val() you must have the corresponding bit set in
table->read_set. For calling field->store() you must have the
corresponding bit set in table->write_set. (There are asserts in
all store()/val() functions to catch wrong usage)
- thd->set_query_id is renamed to thd->mark_used_columns and instead
of setting this to an integer value, this has now the values:
MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE
Changed also all variables named 'set_query_id' to mark_used_columns.
- In filesort() we now inform the handler of exactly which columns are needed
doing the sort and choosing the rows.
- The TABLE_SHARE object has a 'all_set' column bitmap one can use
when one needs a column bitmap with all columns set.
(This is used for table->use_all_columns() and other places)
- The TABLE object has 3 column bitmaps:
- def_read_set Default bitmap for columns to be read
- def_write_set Default bitmap for columns to be written
- tmp_set Can be used as a temporary bitmap when needed.
The table object has also two pointer to bitmaps read_set and write_set
that the handler should use to find out which columns are used in which way.
- count() optimization now calls handler::records() instead of using
handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true).
- Added extra argument to Item::walk() to indicate if we should also
traverse sub queries.
- Added TABLE parameter to cp_buffer_from_ref()
- Don't close tables created with CREATE ... SELECT but keep them in
the table cache. (Faster usage of newly created tables).
New interfaces:
- table->clear_column_bitmaps() to initialize the bitmaps for tables
at start of new statements.
- table->column_bitmaps_set() to set up new column bitmaps and signal
the handler about this.
- table->column_bitmaps_set_no_signal() for some few cases where we need
to setup new column bitmaps but don't signal the handler (as the handler
has already been signaled about these before). Used for the momement
only in opt_range.cc when doing ROR scans.
- table->use_all_columns() to install a bitmap where all columns are marked
as use in the read and the write set.
- table->default_column_bitmaps() to install the normal read and write
column bitmaps, but not signaling the handler about this.
This is mainly used when creating TABLE instances.
- table->mark_columns_needed_for_delete(),
table->mark_columns_needed_for_delete() and
table->mark_columns_needed_for_insert() to allow us to put additional
columns in column usage maps if handler so requires.
(The handler indicates what it neads in handler->table_flags())
- table->prepare_for_position() to allow us to tell handler that it
needs to read primary key parts to be able to store them in
future table->position() calls.
(This replaces the table->file->ha_retrieve_all_pk function)
- table->mark_auto_increment_column() to tell handler are going to update
columns part of any auto_increment key.
- table->mark_columns_used_by_index() to mark all columns that is part of
an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow
it to quickly know that it only needs to read colums that are part
of the key. (The handler can also use the column map for detecting this,
but simpler/faster handler can just monitor the extra() call).
- table->mark_columns_used_by_index_no_reset() to in addition to other columns,
also mark all columns that is used by the given key.
- table->restore_column_maps_after_mark_index() to restore to default
column maps after a call to table->mark_columns_used_by_index().
- New item function register_field_in_read_map(), for marking used columns
in table->read_map. Used by filesort() to mark all used columns
- Maintain in TABLE->merge_keys set of all keys that are used in query.
(Simplices some optimization loops)
- Maintain Field->part_of_key_not_clustered which is like Field->part_of_key
but the field in the clustered key is not assumed to be part of all index.
(used in opt_range.cc for faster loops)
- dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map()
tmp_use_all_columns() and tmp_restore_column_map() functions to temporally
mark all columns as usable. The 'dbug_' version is primarily intended
inside a handler when it wants to just call Field:store() & Field::val()
functions, but don't need the column maps set for any other usage.
(ie:: bitmap_is_set() is never called)
- We can't use compare_records() to skip updates for handlers that returns
a partial column set and the read_set doesn't cover all columns in the
write set. The reason for this is that if we have a column marked only for
write we can't in the MySQL level know if the value changed or not.
The reason this worked before was that MySQL marked all to be written
columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden
bug'.
- open_table_from_share() does not anymore setup temporary MEM_ROOT
object as a thread specific variable for the handler. Instead we
send the to-be-used MEMROOT to get_new_handler().
(Simpler, faster code)
Bugs fixed:
- Column marking was not done correctly in a lot of cases.
(ALTER TABLE, when using triggers, auto_increment fields etc)
(Could potentially result in wrong values inserted in table handlers
relying on that the old column maps or field->set_query_id was correct)
Especially when it comes to triggers, there may be cases where the
old code would cause lost/wrong values for NDB and/or InnoDB tables.
- Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags:
OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG.
This allowed me to remove some wrong warnings about:
"Some non-transactional changed tables couldn't be rolled back"
- Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset
(thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose
some warnings about
"Some non-transactional changed tables couldn't be rolled back")
- Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table()
which could cause delete_table to report random failures.
- Fixed core dumps for some tests when running with --debug
- Added missing FN_LIBCHAR in mysql_rm_tmp_tables()
(This has probably caused us to not properly remove temporary files after
crash)
- slow_logs was not properly initialized, which could maybe cause
extra/lost entries in slow log.
- If we get an duplicate row on insert, change column map to read and
write all columns while retrying the operation. This is required by
the definition of REPLACE and also ensures that fields that are only
part of UPDATE are properly handled. This fixed a bug in NDB and
REPLACE where REPLACE wrongly copied some column values from the replaced
row.
- For table handler that doesn't support NULL in keys, we would give an error
when creating a primary key with NULL fields, even after the fields has been
automaticly converted to NOT NULL.
- Creating a primary key on a SPATIAL key, would fail if field was not
declared as NOT NULL.
Cleanups:
- Removed not used condition argument to setup_tables
- Removed not needed item function reset_query_id_processor().
- Field->add_index is removed. Now this is instead maintained in
(field->flags & FIELD_IN_ADD_INDEX)
- Field->fieldnr is removed (use field->field_index instead)
- New argument to filesort() to indicate that it should return a set of
row pointers (not used columns). This allowed me to remove some references
to sql_command in filesort and should also enable us to return column
results in some cases where we couldn't before.
- Changed column bitmap handling in opt_range.cc to be aligned with TABLE
bitmap, which allowed me to use bitmap functions instead of looping over
all fields to create some needed bitmaps. (Faster and smaller code)
- Broke up found too long lines
- Moved some variable declaration at start of function for better code
readability.
- Removed some not used arguments from functions.
(setup_fields(), mysql_prepare_insert_check_table())
- setup_fields() now takes an enum instead of an int for marking columns
usage.
- For internal temporary tables, use handler::write_row(),
handler::delete_row() and handler::update_row() instead of
handler::ha_xxxx() for faster execution.
- Changed some constants to enum's and define's.
- Using separate column read and write sets allows for easier checking
of timestamp field was set by statement.
- Remove calls to free_io_cache() as this is now done automaticly in ha_reset()
- Don't build table->normalized_path as this is now identical to table->path
(after bar's fixes to convert filenames)
- Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to
do comparision with the 'convert-dbug-for-diff' tool.
Things left to do in 5.1:
- We wrongly log failed CREATE TABLE ... SELECT in some cases when using
row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result)
Mats has promised to look into this.
- Test that my fix for CREATE TABLE ... SELECT is indeed correct.
(I added several test cases for this, but in this case it's better that
someone else also tests this throughly).
Lars has promosed to do this.
2006-06-04 17:52:22 +02:00
|
|
|
if (!(file->ha_table_flags() & HA_CAN_INDEX_BLOBS))
|
2004-04-08 16:56:45 +02:00
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_BLOB_USED_AS_KEY, MYF(0), column->field_name);
|
2004-04-08 16:56:45 +02:00
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
2005-10-15 19:23:13 +02:00
|
|
|
if (f_is_geom(sql_field->pack_flag) && sql_field->geom_type ==
|
|
|
|
Field::GEOM_POINT)
|
|
|
|
column->length= 21;
|
2004-04-08 16:56:45 +02:00
|
|
|
if (!column->length)
|
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_BLOB_KEY_WITHOUT_LENGTH, MYF(0), column->field_name);
|
2004-04-08 16:56:45 +02:00
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
|
|
|
}
|
2004-01-15 18:06:22 +01:00
|
|
|
#ifdef HAVE_SPATIAL
|
2004-12-06 01:00:37 +01:00
|
|
|
if (key->type == Key::SPATIAL)
|
2004-04-08 16:56:45 +02:00
|
|
|
{
|
2004-12-06 01:00:37 +01:00
|
|
|
if (!column->length)
|
2004-04-08 16:56:45 +02:00
|
|
|
{
|
|
|
|
/*
|
2004-12-06 01:00:37 +01:00
|
|
|
4 is: (Xmin,Xmax,Ymin,Ymax), this is for 2D case
|
|
|
|
Lately we'll extend this code to support more dimensions
|
2004-04-08 16:56:45 +02:00
|
|
|
*/
|
2004-12-06 01:00:37 +01:00
|
|
|
column->length= 4*sizeof(double);
|
2004-04-08 16:56:45 +02:00
|
|
|
}
|
|
|
|
}
|
2004-01-15 18:06:22 +01:00
|
|
|
#endif
|
2004-04-08 16:56:45 +02:00
|
|
|
if (!(sql_field->flags & NOT_NULL_FLAG))
|
|
|
|
{
|
|
|
|
if (key->type == Key::PRIMARY)
|
|
|
|
{
|
|
|
|
/* Implicitly set primary key fields to NOT NULL for ISO conf. */
|
|
|
|
sql_field->flags|= NOT_NULL_FLAG;
|
|
|
|
sql_field->pack_flag&= ~FIELDFLAG_MAYBE_NULL;
|
2005-05-13 23:01:40 +02:00
|
|
|
null_fields--;
|
2004-04-08 16:56:45 +02:00
|
|
|
}
|
|
|
|
else
|
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes
Changes that requires code changes in other code of other storage engines.
(Note that all changes are very straightforward and one should find all issues
by compiling a --debug build and fixing all compiler errors and all
asserts in field.cc while running the test suite),
- New optional handler function introduced: reset()
This is called after every DML statement to make it easy for a handler to
statement specific cleanups.
(The only case it's not called is if force the file to be closed)
- handler::extra(HA_EXTRA_RESET) is removed. Code that was there before
should be moved to handler::reset()
- table->read_set contains a bitmap over all columns that are needed
in the query. read_row() and similar functions only needs to read these
columns
- table->write_set contains a bitmap over all columns that will be updated
in the query. write_row() and update_row() only needs to update these
columns.
The above bitmaps should now be up to date in all context
(including ALTER TABLE, filesort()).
The handler is informed of any changes to the bitmap after
fix_fields() by calling the virtual function
handler::column_bitmaps_signal(). If the handler does caching of
these bitmaps (instead of using table->read_set, table->write_set),
it should redo the caching in this code. as the signal() may be sent
several times, it's probably best to set a variable in the signal
and redo the caching on read_row() / write_row() if the variable was
set.
- Removed the read_set and write_set bitmap objects from the handler class
- Removed all column bit handling functions from the handler class.
(Now one instead uses the normal bitmap functions in my_bitmap.c instead
of handler dedicated bitmap functions)
- field->query_id is removed. One should instead instead check
table->read_set and table->write_set if a field is used in the query.
- handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and
handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now
instead use table->read_set to check for which columns to retrieve.
- If a handler needs to call Field->val() or Field->store() on columns
that are not used in the query, one should install a temporary
all-columns-used map while doing so. For this, we provide the following
functions:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
field->val();
dbug_tmp_restore_column_map(table->read_set, old_map);
and similar for the write map:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
field->val();
dbug_tmp_restore_column_map(table->write_set, old_map);
If this is not done, you will sooner or later hit a DBUG_ASSERT
in the field store() / val() functions.
(For not DBUG binaries, the dbug_tmp_restore_column_map() and
dbug_tmp_restore_column_map() are inline dummy functions and should
be optimized away be the compiler).
- If one needs to temporary set the column map for all binaries (and not
just to avoid the DBUG_ASSERT() in the Field::store() / Field::val()
methods) one should use the functions tmp_use_all_columns() and
tmp_restore_column_map() instead of the above dbug_ variants.
- All 'status' fields in the handler base class (like records,
data_file_length etc) are now stored in a 'stats' struct. This makes
it easier to know what status variables are provided by the base
handler. This requires some trivial variable names in the extra()
function.
- New virtual function handler::records(). This is called to optimize
COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true.
(stats.records is not supposed to be an exact value. It's only has to
be 'reasonable enough' for the optimizer to be able to choose a good
optimization path).
- Non virtual handler::init() function added for caching of virtual
constants from engine.
- Removed has_transactions() virtual method. Now one should instead return
HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support
transactions.
- The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument
that is to be used with 'new handler_name()' to allocate the handler
in the right area. The xxxx_create_handler() function is also
responsible for any initialization of the object before returning.
For example, one should change:
static handler *myisam_create_handler(TABLE_SHARE *table)
{
return new ha_myisam(table);
}
->
static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root)
{
return new (mem_root) ha_myisam(table);
}
- New optional virtual function: use_hidden_primary_key().
This is called in case of an update/delete when
(table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
but we don't have a primary key. This allows the handler to take precisions
in remembering any hidden primary key to able to update/delete any
found row. The default handler marks all columns to be read.
- handler::table_flags() now returns a ulonglong (to allow for more flags).
- New/changed table_flags()
- HA_HAS_RECORDS Set if ::records() is supported
- HA_NO_TRANSACTIONS Set if engine doesn't support transactions
- HA_PRIMARY_KEY_REQUIRED_FOR_DELETE
Set if we should mark all primary key columns for
read when reading rows as part of a DELETE
statement. If there is no primary key,
all columns are marked for read.
- HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some
cases (based on table->read_set)
- HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS
Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION.
- HA_DUPP_POS Renamed to HA_DUPLICATE_POS
- HA_REQUIRES_KEY_COLUMNS_FOR_DELETE
Set this if we should mark ALL key columns for
read when when reading rows as part of a DELETE
statement. In case of an update we will mark
all keys for read for which key part changed
value.
- HA_STATS_RECORDS_IS_EXACT
Set this if stats.records is exact.
(This saves us some extra records() calls
when optimizing COUNT(*))
- Removed table_flags()
- HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if
handler::records() gives an exact count() and
HA_STATS_RECORDS_IS_EXACT if stats.records is exact.
- HA_READ_RND_SAME Removed (no one supported this one)
- Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk()
- Renamed handler::dupp_pos to handler::dup_pos
- Removed not used variable handler::sortkey
Upper level handler changes:
- ha_reset() now does some overall checks and calls ::reset()
- ha_table_flags() added. This is a cached version of table_flags(). The
cache is updated on engine creation time and updated on open.
MySQL level changes (not obvious from the above):
- DBUG_ASSERT() added to check that column usage matches what is set
in the column usage bit maps. (This found a LOT of bugs in current
column marking code).
- In 5.1 before, all used columns was marked in read_set and only updated
columns was marked in write_set. Now we only mark columns for which we
need a value in read_set.
- Column bitmaps are created in open_binary_frm() and open_table_from_share().
(Before this was in table.cc)
- handler::table_flags() calls are replaced with handler::ha_table_flags()
- For calling field->val() you must have the corresponding bit set in
table->read_set. For calling field->store() you must have the
corresponding bit set in table->write_set. (There are asserts in
all store()/val() functions to catch wrong usage)
- thd->set_query_id is renamed to thd->mark_used_columns and instead
of setting this to an integer value, this has now the values:
MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE
Changed also all variables named 'set_query_id' to mark_used_columns.
- In filesort() we now inform the handler of exactly which columns are needed
doing the sort and choosing the rows.
- The TABLE_SHARE object has a 'all_set' column bitmap one can use
when one needs a column bitmap with all columns set.
(This is used for table->use_all_columns() and other places)
- The TABLE object has 3 column bitmaps:
- def_read_set Default bitmap for columns to be read
- def_write_set Default bitmap for columns to be written
- tmp_set Can be used as a temporary bitmap when needed.
The table object has also two pointer to bitmaps read_set and write_set
that the handler should use to find out which columns are used in which way.
- count() optimization now calls handler::records() instead of using
handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true).
- Added extra argument to Item::walk() to indicate if we should also
traverse sub queries.
- Added TABLE parameter to cp_buffer_from_ref()
- Don't close tables created with CREATE ... SELECT but keep them in
the table cache. (Faster usage of newly created tables).
New interfaces:
- table->clear_column_bitmaps() to initialize the bitmaps for tables
at start of new statements.
- table->column_bitmaps_set() to set up new column bitmaps and signal
the handler about this.
- table->column_bitmaps_set_no_signal() for some few cases where we need
to setup new column bitmaps but don't signal the handler (as the handler
has already been signaled about these before). Used for the momement
only in opt_range.cc when doing ROR scans.
- table->use_all_columns() to install a bitmap where all columns are marked
as use in the read and the write set.
- table->default_column_bitmaps() to install the normal read and write
column bitmaps, but not signaling the handler about this.
This is mainly used when creating TABLE instances.
- table->mark_columns_needed_for_delete(),
table->mark_columns_needed_for_delete() and
table->mark_columns_needed_for_insert() to allow us to put additional
columns in column usage maps if handler so requires.
(The handler indicates what it neads in handler->table_flags())
- table->prepare_for_position() to allow us to tell handler that it
needs to read primary key parts to be able to store them in
future table->position() calls.
(This replaces the table->file->ha_retrieve_all_pk function)
- table->mark_auto_increment_column() to tell handler are going to update
columns part of any auto_increment key.
- table->mark_columns_used_by_index() to mark all columns that is part of
an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow
it to quickly know that it only needs to read colums that are part
of the key. (The handler can also use the column map for detecting this,
but simpler/faster handler can just monitor the extra() call).
- table->mark_columns_used_by_index_no_reset() to in addition to other columns,
also mark all columns that is used by the given key.
- table->restore_column_maps_after_mark_index() to restore to default
column maps after a call to table->mark_columns_used_by_index().
- New item function register_field_in_read_map(), for marking used columns
in table->read_map. Used by filesort() to mark all used columns
- Maintain in TABLE->merge_keys set of all keys that are used in query.
(Simplices some optimization loops)
- Maintain Field->part_of_key_not_clustered which is like Field->part_of_key
but the field in the clustered key is not assumed to be part of all index.
(used in opt_range.cc for faster loops)
- dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map()
tmp_use_all_columns() and tmp_restore_column_map() functions to temporally
mark all columns as usable. The 'dbug_' version is primarily intended
inside a handler when it wants to just call Field:store() & Field::val()
functions, but don't need the column maps set for any other usage.
(ie:: bitmap_is_set() is never called)
- We can't use compare_records() to skip updates for handlers that returns
a partial column set and the read_set doesn't cover all columns in the
write set. The reason for this is that if we have a column marked only for
write we can't in the MySQL level know if the value changed or not.
The reason this worked before was that MySQL marked all to be written
columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden
bug'.
- open_table_from_share() does not anymore setup temporary MEM_ROOT
object as a thread specific variable for the handler. Instead we
send the to-be-used MEMROOT to get_new_handler().
(Simpler, faster code)
Bugs fixed:
- Column marking was not done correctly in a lot of cases.
(ALTER TABLE, when using triggers, auto_increment fields etc)
(Could potentially result in wrong values inserted in table handlers
relying on that the old column maps or field->set_query_id was correct)
Especially when it comes to triggers, there may be cases where the
old code would cause lost/wrong values for NDB and/or InnoDB tables.
- Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags:
OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG.
This allowed me to remove some wrong warnings about:
"Some non-transactional changed tables couldn't be rolled back"
- Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset
(thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose
some warnings about
"Some non-transactional changed tables couldn't be rolled back")
- Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table()
which could cause delete_table to report random failures.
- Fixed core dumps for some tests when running with --debug
- Added missing FN_LIBCHAR in mysql_rm_tmp_tables()
(This has probably caused us to not properly remove temporary files after
crash)
- slow_logs was not properly initialized, which could maybe cause
extra/lost entries in slow log.
- If we get an duplicate row on insert, change column map to read and
write all columns while retrying the operation. This is required by
the definition of REPLACE and also ensures that fields that are only
part of UPDATE are properly handled. This fixed a bug in NDB and
REPLACE where REPLACE wrongly copied some column values from the replaced
row.
- For table handler that doesn't support NULL in keys, we would give an error
when creating a primary key with NULL fields, even after the fields has been
automaticly converted to NOT NULL.
- Creating a primary key on a SPATIAL key, would fail if field was not
declared as NOT NULL.
Cleanups:
- Removed not used condition argument to setup_tables
- Removed not needed item function reset_query_id_processor().
- Field->add_index is removed. Now this is instead maintained in
(field->flags & FIELD_IN_ADD_INDEX)
- Field->fieldnr is removed (use field->field_index instead)
- New argument to filesort() to indicate that it should return a set of
row pointers (not used columns). This allowed me to remove some references
to sql_command in filesort and should also enable us to return column
results in some cases where we couldn't before.
- Changed column bitmap handling in opt_range.cc to be aligned with TABLE
bitmap, which allowed me to use bitmap functions instead of looping over
all fields to create some needed bitmaps. (Faster and smaller code)
- Broke up found too long lines
- Moved some variable declaration at start of function for better code
readability.
- Removed some not used arguments from functions.
(setup_fields(), mysql_prepare_insert_check_table())
- setup_fields() now takes an enum instead of an int for marking columns
usage.
- For internal temporary tables, use handler::write_row(),
handler::delete_row() and handler::update_row() instead of
handler::ha_xxxx() for faster execution.
- Changed some constants to enum's and define's.
- Using separate column read and write sets allows for easier checking
of timestamp field was set by statement.
- Remove calls to free_io_cache() as this is now done automaticly in ha_reset()
- Don't build table->normalized_path as this is now identical to table->path
(after bar's fixes to convert filenames)
- Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to
do comparision with the 'convert-dbug-for-diff' tool.
Things left to do in 5.1:
- We wrongly log failed CREATE TABLE ... SELECT in some cases when using
row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result)
Mats has promised to look into this.
- Test that my fix for CREATE TABLE ... SELECT is indeed correct.
(I added several test cases for this, but in this case it's better that
someone else also tests this throughly).
Lars has promosed to do this.
2006-06-04 17:52:22 +02:00
|
|
|
{
|
|
|
|
key_info->flags|= HA_NULL_PART_KEY;
|
|
|
|
if (!(file->ha_table_flags() & HA_NULL_IN_KEY))
|
|
|
|
{
|
|
|
|
my_error(ER_NULL_COLUMN_IN_INDEX, MYF(0), column->field_name);
|
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
|
|
|
if (key->type == Key::SPATIAL)
|
|
|
|
{
|
|
|
|
my_message(ER_SPATIAL_CANT_HAVE_NULL,
|
|
|
|
ER(ER_SPATIAL_CANT_HAVE_NULL), MYF(0));
|
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
|
|
|
}
|
2004-04-08 16:56:45 +02:00
|
|
|
}
|
|
|
|
if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
|
|
|
|
{
|
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes
Changes that requires code changes in other code of other storage engines.
(Note that all changes are very straightforward and one should find all issues
by compiling a --debug build and fixing all compiler errors and all
asserts in field.cc while running the test suite),
- New optional handler function introduced: reset()
This is called after every DML statement to make it easy for a handler to
statement specific cleanups.
(The only case it's not called is if force the file to be closed)
- handler::extra(HA_EXTRA_RESET) is removed. Code that was there before
should be moved to handler::reset()
- table->read_set contains a bitmap over all columns that are needed
in the query. read_row() and similar functions only needs to read these
columns
- table->write_set contains a bitmap over all columns that will be updated
in the query. write_row() and update_row() only needs to update these
columns.
The above bitmaps should now be up to date in all context
(including ALTER TABLE, filesort()).
The handler is informed of any changes to the bitmap after
fix_fields() by calling the virtual function
handler::column_bitmaps_signal(). If the handler does caching of
these bitmaps (instead of using table->read_set, table->write_set),
it should redo the caching in this code. as the signal() may be sent
several times, it's probably best to set a variable in the signal
and redo the caching on read_row() / write_row() if the variable was
set.
- Removed the read_set and write_set bitmap objects from the handler class
- Removed all column bit handling functions from the handler class.
(Now one instead uses the normal bitmap functions in my_bitmap.c instead
of handler dedicated bitmap functions)
- field->query_id is removed. One should instead instead check
table->read_set and table->write_set if a field is used in the query.
- handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and
handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now
instead use table->read_set to check for which columns to retrieve.
- If a handler needs to call Field->val() or Field->store() on columns
that are not used in the query, one should install a temporary
all-columns-used map while doing so. For this, we provide the following
functions:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
field->val();
dbug_tmp_restore_column_map(table->read_set, old_map);
and similar for the write map:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
field->val();
dbug_tmp_restore_column_map(table->write_set, old_map);
If this is not done, you will sooner or later hit a DBUG_ASSERT
in the field store() / val() functions.
(For not DBUG binaries, the dbug_tmp_restore_column_map() and
dbug_tmp_restore_column_map() are inline dummy functions and should
be optimized away be the compiler).
- If one needs to temporary set the column map for all binaries (and not
just to avoid the DBUG_ASSERT() in the Field::store() / Field::val()
methods) one should use the functions tmp_use_all_columns() and
tmp_restore_column_map() instead of the above dbug_ variants.
- All 'status' fields in the handler base class (like records,
data_file_length etc) are now stored in a 'stats' struct. This makes
it easier to know what status variables are provided by the base
handler. This requires some trivial variable names in the extra()
function.
- New virtual function handler::records(). This is called to optimize
COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true.
(stats.records is not supposed to be an exact value. It's only has to
be 'reasonable enough' for the optimizer to be able to choose a good
optimization path).
- Non virtual handler::init() function added for caching of virtual
constants from engine.
- Removed has_transactions() virtual method. Now one should instead return
HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support
transactions.
- The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument
that is to be used with 'new handler_name()' to allocate the handler
in the right area. The xxxx_create_handler() function is also
responsible for any initialization of the object before returning.
For example, one should change:
static handler *myisam_create_handler(TABLE_SHARE *table)
{
return new ha_myisam(table);
}
->
static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root)
{
return new (mem_root) ha_myisam(table);
}
- New optional virtual function: use_hidden_primary_key().
This is called in case of an update/delete when
(table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
but we don't have a primary key. This allows the handler to take precisions
in remembering any hidden primary key to able to update/delete any
found row. The default handler marks all columns to be read.
- handler::table_flags() now returns a ulonglong (to allow for more flags).
- New/changed table_flags()
- HA_HAS_RECORDS Set if ::records() is supported
- HA_NO_TRANSACTIONS Set if engine doesn't support transactions
- HA_PRIMARY_KEY_REQUIRED_FOR_DELETE
Set if we should mark all primary key columns for
read when reading rows as part of a DELETE
statement. If there is no primary key,
all columns are marked for read.
- HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some
cases (based on table->read_set)
- HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS
Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION.
- HA_DUPP_POS Renamed to HA_DUPLICATE_POS
- HA_REQUIRES_KEY_COLUMNS_FOR_DELETE
Set this if we should mark ALL key columns for
read when when reading rows as part of a DELETE
statement. In case of an update we will mark
all keys for read for which key part changed
value.
- HA_STATS_RECORDS_IS_EXACT
Set this if stats.records is exact.
(This saves us some extra records() calls
when optimizing COUNT(*))
- Removed table_flags()
- HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if
handler::records() gives an exact count() and
HA_STATS_RECORDS_IS_EXACT if stats.records is exact.
- HA_READ_RND_SAME Removed (no one supported this one)
- Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk()
- Renamed handler::dupp_pos to handler::dup_pos
- Removed not used variable handler::sortkey
Upper level handler changes:
- ha_reset() now does some overall checks and calls ::reset()
- ha_table_flags() added. This is a cached version of table_flags(). The
cache is updated on engine creation time and updated on open.
MySQL level changes (not obvious from the above):
- DBUG_ASSERT() added to check that column usage matches what is set
in the column usage bit maps. (This found a LOT of bugs in current
column marking code).
- In 5.1 before, all used columns was marked in read_set and only updated
columns was marked in write_set. Now we only mark columns for which we
need a value in read_set.
- Column bitmaps are created in open_binary_frm() and open_table_from_share().
(Before this was in table.cc)
- handler::table_flags() calls are replaced with handler::ha_table_flags()
- For calling field->val() you must have the corresponding bit set in
table->read_set. For calling field->store() you must have the
corresponding bit set in table->write_set. (There are asserts in
all store()/val() functions to catch wrong usage)
- thd->set_query_id is renamed to thd->mark_used_columns and instead
of setting this to an integer value, this has now the values:
MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE
Changed also all variables named 'set_query_id' to mark_used_columns.
- In filesort() we now inform the handler of exactly which columns are needed
doing the sort and choosing the rows.
- The TABLE_SHARE object has a 'all_set' column bitmap one can use
when one needs a column bitmap with all columns set.
(This is used for table->use_all_columns() and other places)
- The TABLE object has 3 column bitmaps:
- def_read_set Default bitmap for columns to be read
- def_write_set Default bitmap for columns to be written
- tmp_set Can be used as a temporary bitmap when needed.
The table object has also two pointer to bitmaps read_set and write_set
that the handler should use to find out which columns are used in which way.
- count() optimization now calls handler::records() instead of using
handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true).
- Added extra argument to Item::walk() to indicate if we should also
traverse sub queries.
- Added TABLE parameter to cp_buffer_from_ref()
- Don't close tables created with CREATE ... SELECT but keep them in
the table cache. (Faster usage of newly created tables).
New interfaces:
- table->clear_column_bitmaps() to initialize the bitmaps for tables
at start of new statements.
- table->column_bitmaps_set() to set up new column bitmaps and signal
the handler about this.
- table->column_bitmaps_set_no_signal() for some few cases where we need
to setup new column bitmaps but don't signal the handler (as the handler
has already been signaled about these before). Used for the momement
only in opt_range.cc when doing ROR scans.
- table->use_all_columns() to install a bitmap where all columns are marked
as use in the read and the write set.
- table->default_column_bitmaps() to install the normal read and write
column bitmaps, but not signaling the handler about this.
This is mainly used when creating TABLE instances.
- table->mark_columns_needed_for_delete(),
table->mark_columns_needed_for_delete() and
table->mark_columns_needed_for_insert() to allow us to put additional
columns in column usage maps if handler so requires.
(The handler indicates what it neads in handler->table_flags())
- table->prepare_for_position() to allow us to tell handler that it
needs to read primary key parts to be able to store them in
future table->position() calls.
(This replaces the table->file->ha_retrieve_all_pk function)
- table->mark_auto_increment_column() to tell handler are going to update
columns part of any auto_increment key.
- table->mark_columns_used_by_index() to mark all columns that is part of
an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow
it to quickly know that it only needs to read colums that are part
of the key. (The handler can also use the column map for detecting this,
but simpler/faster handler can just monitor the extra() call).
- table->mark_columns_used_by_index_no_reset() to in addition to other columns,
also mark all columns that is used by the given key.
- table->restore_column_maps_after_mark_index() to restore to default
column maps after a call to table->mark_columns_used_by_index().
- New item function register_field_in_read_map(), for marking used columns
in table->read_map. Used by filesort() to mark all used columns
- Maintain in TABLE->merge_keys set of all keys that are used in query.
(Simplices some optimization loops)
- Maintain Field->part_of_key_not_clustered which is like Field->part_of_key
but the field in the clustered key is not assumed to be part of all index.
(used in opt_range.cc for faster loops)
- dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map()
tmp_use_all_columns() and tmp_restore_column_map() functions to temporally
mark all columns as usable. The 'dbug_' version is primarily intended
inside a handler when it wants to just call Field:store() & Field::val()
functions, but don't need the column maps set for any other usage.
(ie:: bitmap_is_set() is never called)
- We can't use compare_records() to skip updates for handlers that returns
a partial column set and the read_set doesn't cover all columns in the
write set. The reason for this is that if we have a column marked only for
write we can't in the MySQL level know if the value changed or not.
The reason this worked before was that MySQL marked all to be written
columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden
bug'.
- open_table_from_share() does not anymore setup temporary MEM_ROOT
object as a thread specific variable for the handler. Instead we
send the to-be-used MEMROOT to get_new_handler().
(Simpler, faster code)
Bugs fixed:
- Column marking was not done correctly in a lot of cases.
(ALTER TABLE, when using triggers, auto_increment fields etc)
(Could potentially result in wrong values inserted in table handlers
relying on that the old column maps or field->set_query_id was correct)
Especially when it comes to triggers, there may be cases where the
old code would cause lost/wrong values for NDB and/or InnoDB tables.
- Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags:
OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG.
This allowed me to remove some wrong warnings about:
"Some non-transactional changed tables couldn't be rolled back"
- Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset
(thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose
some warnings about
"Some non-transactional changed tables couldn't be rolled back")
- Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table()
which could cause delete_table to report random failures.
- Fixed core dumps for some tests when running with --debug
- Added missing FN_LIBCHAR in mysql_rm_tmp_tables()
(This has probably caused us to not properly remove temporary files after
crash)
- slow_logs was not properly initialized, which could maybe cause
extra/lost entries in slow log.
- If we get an duplicate row on insert, change column map to read and
write all columns while retrying the operation. This is required by
the definition of REPLACE and also ensures that fields that are only
part of UPDATE are properly handled. This fixed a bug in NDB and
REPLACE where REPLACE wrongly copied some column values from the replaced
row.
- For table handler that doesn't support NULL in keys, we would give an error
when creating a primary key with NULL fields, even after the fields has been
automaticly converted to NOT NULL.
- Creating a primary key on a SPATIAL key, would fail if field was not
declared as NOT NULL.
Cleanups:
- Removed not used condition argument to setup_tables
- Removed not needed item function reset_query_id_processor().
- Field->add_index is removed. Now this is instead maintained in
(field->flags & FIELD_IN_ADD_INDEX)
- Field->fieldnr is removed (use field->field_index instead)
- New argument to filesort() to indicate that it should return a set of
row pointers (not used columns). This allowed me to remove some references
to sql_command in filesort and should also enable us to return column
results in some cases where we couldn't before.
- Changed column bitmap handling in opt_range.cc to be aligned with TABLE
bitmap, which allowed me to use bitmap functions instead of looping over
all fields to create some needed bitmaps. (Faster and smaller code)
- Broke up found too long lines
- Moved some variable declaration at start of function for better code
readability.
- Removed some not used arguments from functions.
(setup_fields(), mysql_prepare_insert_check_table())
- setup_fields() now takes an enum instead of an int for marking columns
usage.
- For internal temporary tables, use handler::write_row(),
handler::delete_row() and handler::update_row() instead of
handler::ha_xxxx() for faster execution.
- Changed some constants to enum's and define's.
- Using separate column read and write sets allows for easier checking
of timestamp field was set by statement.
- Remove calls to free_io_cache() as this is now done automaticly in ha_reset()
- Don't build table->normalized_path as this is now identical to table->path
(after bar's fixes to convert filenames)
- Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to
do comparision with the 'convert-dbug-for-diff' tool.
Things left to do in 5.1:
- We wrongly log failed CREATE TABLE ... SELECT in some cases when using
row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result)
Mats has promised to look into this.
- Test that my fix for CREATE TABLE ... SELECT is indeed correct.
(I added several test cases for this, but in this case it's better that
someone else also tests this throughly).
Lars has promosed to do this.
2006-06-04 17:52:22 +02:00
|
|
|
if (column_nr == 0 || (file->ha_table_flags() & HA_AUTO_PART_KEY))
|
2004-04-08 16:56:45 +02:00
|
|
|
auto_increment--; // Field is used
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2003-10-15 13:40:20 +02:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
key_part_info->fieldnr= field;
|
|
|
|
key_part_info->offset= (uint16) sql_field->offset;
|
|
|
|
key_part_info->key_type=sql_field->pack_flag;
|
2004-12-06 01:00:37 +01:00
|
|
|
length= sql_field->key_length;
|
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
if (column->length)
|
|
|
|
{
|
|
|
|
if (f_is_blob(sql_field->pack_flag))
|
|
|
|
{
|
2005-05-16 14:21:35 +02:00
|
|
|
if ((length=column->length) > max_key_length ||
|
2000-10-22 00:19:05 +02:00
|
|
|
length > file->max_key_part_length())
|
2004-04-08 16:56:45 +02:00
|
|
|
{
|
2005-05-16 14:21:35 +02:00
|
|
|
length=min(max_key_length, file->max_key_part_length());
|
2004-04-08 16:56:45 +02:00
|
|
|
if (key->type == Key::MULTIPLE)
|
|
|
|
{
|
|
|
|
/* not a critical problem */
|
|
|
|
char warn_buff[MYSQL_ERRMSG_SIZE];
|
|
|
|
my_snprintf(warn_buff, sizeof(warn_buff), ER(ER_TOO_LONG_KEY),
|
|
|
|
length);
|
|
|
|
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
|
|
ER_TOO_LONG_KEY, warn_buff);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
my_error(ER_TOO_LONG_KEY,MYF(0),length);
|
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2004-02-17 16:57:39 +01:00
|
|
|
else if (!f_is_geom(sql_field->pack_flag) &&
|
2004-04-08 16:56:45 +02:00
|
|
|
(column->length > length ||
|
|
|
|
((f_is_packed(sql_field->pack_flag) ||
|
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes
Changes that requires code changes in other code of other storage engines.
(Note that all changes are very straightforward and one should find all issues
by compiling a --debug build and fixing all compiler errors and all
asserts in field.cc while running the test suite),
- New optional handler function introduced: reset()
This is called after every DML statement to make it easy for a handler to
statement specific cleanups.
(The only case it's not called is if force the file to be closed)
- handler::extra(HA_EXTRA_RESET) is removed. Code that was there before
should be moved to handler::reset()
- table->read_set contains a bitmap over all columns that are needed
in the query. read_row() and similar functions only needs to read these
columns
- table->write_set contains a bitmap over all columns that will be updated
in the query. write_row() and update_row() only needs to update these
columns.
The above bitmaps should now be up to date in all context
(including ALTER TABLE, filesort()).
The handler is informed of any changes to the bitmap after
fix_fields() by calling the virtual function
handler::column_bitmaps_signal(). If the handler does caching of
these bitmaps (instead of using table->read_set, table->write_set),
it should redo the caching in this code. as the signal() may be sent
several times, it's probably best to set a variable in the signal
and redo the caching on read_row() / write_row() if the variable was
set.
- Removed the read_set and write_set bitmap objects from the handler class
- Removed all column bit handling functions from the handler class.
(Now one instead uses the normal bitmap functions in my_bitmap.c instead
of handler dedicated bitmap functions)
- field->query_id is removed. One should instead instead check
table->read_set and table->write_set if a field is used in the query.
- handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and
handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now
instead use table->read_set to check for which columns to retrieve.
- If a handler needs to call Field->val() or Field->store() on columns
that are not used in the query, one should install a temporary
all-columns-used map while doing so. For this, we provide the following
functions:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
field->val();
dbug_tmp_restore_column_map(table->read_set, old_map);
and similar for the write map:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
field->val();
dbug_tmp_restore_column_map(table->write_set, old_map);
If this is not done, you will sooner or later hit a DBUG_ASSERT
in the field store() / val() functions.
(For not DBUG binaries, the dbug_tmp_restore_column_map() and
dbug_tmp_restore_column_map() are inline dummy functions and should
be optimized away be the compiler).
- If one needs to temporary set the column map for all binaries (and not
just to avoid the DBUG_ASSERT() in the Field::store() / Field::val()
methods) one should use the functions tmp_use_all_columns() and
tmp_restore_column_map() instead of the above dbug_ variants.
- All 'status' fields in the handler base class (like records,
data_file_length etc) are now stored in a 'stats' struct. This makes
it easier to know what status variables are provided by the base
handler. This requires some trivial variable names in the extra()
function.
- New virtual function handler::records(). This is called to optimize
COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true.
(stats.records is not supposed to be an exact value. It's only has to
be 'reasonable enough' for the optimizer to be able to choose a good
optimization path).
- Non virtual handler::init() function added for caching of virtual
constants from engine.
- Removed has_transactions() virtual method. Now one should instead return
HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support
transactions.
- The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument
that is to be used with 'new handler_name()' to allocate the handler
in the right area. The xxxx_create_handler() function is also
responsible for any initialization of the object before returning.
For example, one should change:
static handler *myisam_create_handler(TABLE_SHARE *table)
{
return new ha_myisam(table);
}
->
static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root)
{
return new (mem_root) ha_myisam(table);
}
- New optional virtual function: use_hidden_primary_key().
This is called in case of an update/delete when
(table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
but we don't have a primary key. This allows the handler to take precisions
in remembering any hidden primary key to able to update/delete any
found row. The default handler marks all columns to be read.
- handler::table_flags() now returns a ulonglong (to allow for more flags).
- New/changed table_flags()
- HA_HAS_RECORDS Set if ::records() is supported
- HA_NO_TRANSACTIONS Set if engine doesn't support transactions
- HA_PRIMARY_KEY_REQUIRED_FOR_DELETE
Set if we should mark all primary key columns for
read when reading rows as part of a DELETE
statement. If there is no primary key,
all columns are marked for read.
- HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some
cases (based on table->read_set)
- HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS
Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION.
- HA_DUPP_POS Renamed to HA_DUPLICATE_POS
- HA_REQUIRES_KEY_COLUMNS_FOR_DELETE
Set this if we should mark ALL key columns for
read when when reading rows as part of a DELETE
statement. In case of an update we will mark
all keys for read for which key part changed
value.
- HA_STATS_RECORDS_IS_EXACT
Set this if stats.records is exact.
(This saves us some extra records() calls
when optimizing COUNT(*))
- Removed table_flags()
- HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if
handler::records() gives an exact count() and
HA_STATS_RECORDS_IS_EXACT if stats.records is exact.
- HA_READ_RND_SAME Removed (no one supported this one)
- Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk()
- Renamed handler::dupp_pos to handler::dup_pos
- Removed not used variable handler::sortkey
Upper level handler changes:
- ha_reset() now does some overall checks and calls ::reset()
- ha_table_flags() added. This is a cached version of table_flags(). The
cache is updated on engine creation time and updated on open.
MySQL level changes (not obvious from the above):
- DBUG_ASSERT() added to check that column usage matches what is set
in the column usage bit maps. (This found a LOT of bugs in current
column marking code).
- In 5.1 before, all used columns was marked in read_set and only updated
columns was marked in write_set. Now we only mark columns for which we
need a value in read_set.
- Column bitmaps are created in open_binary_frm() and open_table_from_share().
(Before this was in table.cc)
- handler::table_flags() calls are replaced with handler::ha_table_flags()
- For calling field->val() you must have the corresponding bit set in
table->read_set. For calling field->store() you must have the
corresponding bit set in table->write_set. (There are asserts in
all store()/val() functions to catch wrong usage)
- thd->set_query_id is renamed to thd->mark_used_columns and instead
of setting this to an integer value, this has now the values:
MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE
Changed also all variables named 'set_query_id' to mark_used_columns.
- In filesort() we now inform the handler of exactly which columns are needed
doing the sort and choosing the rows.
- The TABLE_SHARE object has a 'all_set' column bitmap one can use
when one needs a column bitmap with all columns set.
(This is used for table->use_all_columns() and other places)
- The TABLE object has 3 column bitmaps:
- def_read_set Default bitmap for columns to be read
- def_write_set Default bitmap for columns to be written
- tmp_set Can be used as a temporary bitmap when needed.
The table object has also two pointer to bitmaps read_set and write_set
that the handler should use to find out which columns are used in which way.
- count() optimization now calls handler::records() instead of using
handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true).
- Added extra argument to Item::walk() to indicate if we should also
traverse sub queries.
- Added TABLE parameter to cp_buffer_from_ref()
- Don't close tables created with CREATE ... SELECT but keep them in
the table cache. (Faster usage of newly created tables).
New interfaces:
- table->clear_column_bitmaps() to initialize the bitmaps for tables
at start of new statements.
- table->column_bitmaps_set() to set up new column bitmaps and signal
the handler about this.
- table->column_bitmaps_set_no_signal() for some few cases where we need
to setup new column bitmaps but don't signal the handler (as the handler
has already been signaled about these before). Used for the momement
only in opt_range.cc when doing ROR scans.
- table->use_all_columns() to install a bitmap where all columns are marked
as use in the read and the write set.
- table->default_column_bitmaps() to install the normal read and write
column bitmaps, but not signaling the handler about this.
This is mainly used when creating TABLE instances.
- table->mark_columns_needed_for_delete(),
table->mark_columns_needed_for_delete() and
table->mark_columns_needed_for_insert() to allow us to put additional
columns in column usage maps if handler so requires.
(The handler indicates what it neads in handler->table_flags())
- table->prepare_for_position() to allow us to tell handler that it
needs to read primary key parts to be able to store them in
future table->position() calls.
(This replaces the table->file->ha_retrieve_all_pk function)
- table->mark_auto_increment_column() to tell handler are going to update
columns part of any auto_increment key.
- table->mark_columns_used_by_index() to mark all columns that is part of
an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow
it to quickly know that it only needs to read colums that are part
of the key. (The handler can also use the column map for detecting this,
but simpler/faster handler can just monitor the extra() call).
- table->mark_columns_used_by_index_no_reset() to in addition to other columns,
also mark all columns that is used by the given key.
- table->restore_column_maps_after_mark_index() to restore to default
column maps after a call to table->mark_columns_used_by_index().
- New item function register_field_in_read_map(), for marking used columns
in table->read_map. Used by filesort() to mark all used columns
- Maintain in TABLE->merge_keys set of all keys that are used in query.
(Simplices some optimization loops)
- Maintain Field->part_of_key_not_clustered which is like Field->part_of_key
but the field in the clustered key is not assumed to be part of all index.
(used in opt_range.cc for faster loops)
- dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map()
tmp_use_all_columns() and tmp_restore_column_map() functions to temporally
mark all columns as usable. The 'dbug_' version is primarily intended
inside a handler when it wants to just call Field:store() & Field::val()
functions, but don't need the column maps set for any other usage.
(ie:: bitmap_is_set() is never called)
- We can't use compare_records() to skip updates for handlers that returns
a partial column set and the read_set doesn't cover all columns in the
write set. The reason for this is that if we have a column marked only for
write we can't in the MySQL level know if the value changed or not.
The reason this worked before was that MySQL marked all to be written
columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden
bug'.
- open_table_from_share() does not anymore setup temporary MEM_ROOT
object as a thread specific variable for the handler. Instead we
send the to-be-used MEMROOT to get_new_handler().
(Simpler, faster code)
Bugs fixed:
- Column marking was not done correctly in a lot of cases.
(ALTER TABLE, when using triggers, auto_increment fields etc)
(Could potentially result in wrong values inserted in table handlers
relying on that the old column maps or field->set_query_id was correct)
Especially when it comes to triggers, there may be cases where the
old code would cause lost/wrong values for NDB and/or InnoDB tables.
- Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags:
OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG.
This allowed me to remove some wrong warnings about:
"Some non-transactional changed tables couldn't be rolled back"
- Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset
(thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose
some warnings about
"Some non-transactional changed tables couldn't be rolled back")
- Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table()
which could cause delete_table to report random failures.
- Fixed core dumps for some tests when running with --debug
- Added missing FN_LIBCHAR in mysql_rm_tmp_tables()
(This has probably caused us to not properly remove temporary files after
crash)
- slow_logs was not properly initialized, which could maybe cause
extra/lost entries in slow log.
- If we get an duplicate row on insert, change column map to read and
write all columns while retrying the operation. This is required by
the definition of REPLACE and also ensures that fields that are only
part of UPDATE are properly handled. This fixed a bug in NDB and
REPLACE where REPLACE wrongly copied some column values from the replaced
row.
- For table handler that doesn't support NULL in keys, we would give an error
when creating a primary key with NULL fields, even after the fields has been
automaticly converted to NOT NULL.
- Creating a primary key on a SPATIAL key, would fail if field was not
declared as NOT NULL.
Cleanups:
- Removed not used condition argument to setup_tables
- Removed not needed item function reset_query_id_processor().
- Field->add_index is removed. Now this is instead maintained in
(field->flags & FIELD_IN_ADD_INDEX)
- Field->fieldnr is removed (use field->field_index instead)
- New argument to filesort() to indicate that it should return a set of
row pointers (not used columns). This allowed me to remove some references
to sql_command in filesort and should also enable us to return column
results in some cases where we couldn't before.
- Changed column bitmap handling in opt_range.cc to be aligned with TABLE
bitmap, which allowed me to use bitmap functions instead of looping over
all fields to create some needed bitmaps. (Faster and smaller code)
- Broke up found too long lines
- Moved some variable declaration at start of function for better code
readability.
- Removed some not used arguments from functions.
(setup_fields(), mysql_prepare_insert_check_table())
- setup_fields() now takes an enum instead of an int for marking columns
usage.
- For internal temporary tables, use handler::write_row(),
handler::delete_row() and handler::update_row() instead of
handler::ha_xxxx() for faster execution.
- Changed some constants to enum's and define's.
- Using separate column read and write sets allows for easier checking
of timestamp field was set by statement.
- Remove calls to free_io_cache() as this is now done automaticly in ha_reset()
- Don't build table->normalized_path as this is now identical to table->path
(after bar's fixes to convert filenames)
- Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to
do comparision with the 'convert-dbug-for-diff' tool.
Things left to do in 5.1:
- We wrongly log failed CREATE TABLE ... SELECT in some cases when using
row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result)
Mats has promised to look into this.
- Test that my fix for CREATE TABLE ... SELECT is indeed correct.
(I added several test cases for this, but in this case it's better that
someone else also tests this throughly).
Lars has promosed to do this.
2006-06-04 17:52:22 +02:00
|
|
|
((file->ha_table_flags() & HA_NO_PREFIX_CHAR_KEYS) &&
|
2004-04-08 16:56:45 +02:00
|
|
|
(key_info->flags & HA_NOSAME))) &&
|
|
|
|
column->length != length)))
|
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_WRONG_SUB_KEY, ER(ER_WRONG_SUB_KEY), MYF(0));
|
2004-04-08 16:56:45 +02:00
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes
Changes that requires code changes in other code of other storage engines.
(Note that all changes are very straightforward and one should find all issues
by compiling a --debug build and fixing all compiler errors and all
asserts in field.cc while running the test suite),
- New optional handler function introduced: reset()
This is called after every DML statement to make it easy for a handler to
statement specific cleanups.
(The only case it's not called is if force the file to be closed)
- handler::extra(HA_EXTRA_RESET) is removed. Code that was there before
should be moved to handler::reset()
- table->read_set contains a bitmap over all columns that are needed
in the query. read_row() and similar functions only needs to read these
columns
- table->write_set contains a bitmap over all columns that will be updated
in the query. write_row() and update_row() only needs to update these
columns.
The above bitmaps should now be up to date in all context
(including ALTER TABLE, filesort()).
The handler is informed of any changes to the bitmap after
fix_fields() by calling the virtual function
handler::column_bitmaps_signal(). If the handler does caching of
these bitmaps (instead of using table->read_set, table->write_set),
it should redo the caching in this code. as the signal() may be sent
several times, it's probably best to set a variable in the signal
and redo the caching on read_row() / write_row() if the variable was
set.
- Removed the read_set and write_set bitmap objects from the handler class
- Removed all column bit handling functions from the handler class.
(Now one instead uses the normal bitmap functions in my_bitmap.c instead
of handler dedicated bitmap functions)
- field->query_id is removed. One should instead instead check
table->read_set and table->write_set if a field is used in the query.
- handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and
handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now
instead use table->read_set to check for which columns to retrieve.
- If a handler needs to call Field->val() or Field->store() on columns
that are not used in the query, one should install a temporary
all-columns-used map while doing so. For this, we provide the following
functions:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
field->val();
dbug_tmp_restore_column_map(table->read_set, old_map);
and similar for the write map:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
field->val();
dbug_tmp_restore_column_map(table->write_set, old_map);
If this is not done, you will sooner or later hit a DBUG_ASSERT
in the field store() / val() functions.
(For not DBUG binaries, the dbug_tmp_restore_column_map() and
dbug_tmp_restore_column_map() are inline dummy functions and should
be optimized away be the compiler).
- If one needs to temporary set the column map for all binaries (and not
just to avoid the DBUG_ASSERT() in the Field::store() / Field::val()
methods) one should use the functions tmp_use_all_columns() and
tmp_restore_column_map() instead of the above dbug_ variants.
- All 'status' fields in the handler base class (like records,
data_file_length etc) are now stored in a 'stats' struct. This makes
it easier to know what status variables are provided by the base
handler. This requires some trivial variable names in the extra()
function.
- New virtual function handler::records(). This is called to optimize
COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true.
(stats.records is not supposed to be an exact value. It's only has to
be 'reasonable enough' for the optimizer to be able to choose a good
optimization path).
- Non virtual handler::init() function added for caching of virtual
constants from engine.
- Removed has_transactions() virtual method. Now one should instead return
HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support
transactions.
- The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument
that is to be used with 'new handler_name()' to allocate the handler
in the right area. The xxxx_create_handler() function is also
responsible for any initialization of the object before returning.
For example, one should change:
static handler *myisam_create_handler(TABLE_SHARE *table)
{
return new ha_myisam(table);
}
->
static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root)
{
return new (mem_root) ha_myisam(table);
}
- New optional virtual function: use_hidden_primary_key().
This is called in case of an update/delete when
(table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
but we don't have a primary key. This allows the handler to take precisions
in remembering any hidden primary key to able to update/delete any
found row. The default handler marks all columns to be read.
- handler::table_flags() now returns a ulonglong (to allow for more flags).
- New/changed table_flags()
- HA_HAS_RECORDS Set if ::records() is supported
- HA_NO_TRANSACTIONS Set if engine doesn't support transactions
- HA_PRIMARY_KEY_REQUIRED_FOR_DELETE
Set if we should mark all primary key columns for
read when reading rows as part of a DELETE
statement. If there is no primary key,
all columns are marked for read.
- HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some
cases (based on table->read_set)
- HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS
Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION.
- HA_DUPP_POS Renamed to HA_DUPLICATE_POS
- HA_REQUIRES_KEY_COLUMNS_FOR_DELETE
Set this if we should mark ALL key columns for
read when when reading rows as part of a DELETE
statement. In case of an update we will mark
all keys for read for which key part changed
value.
- HA_STATS_RECORDS_IS_EXACT
Set this if stats.records is exact.
(This saves us some extra records() calls
when optimizing COUNT(*))
- Removed table_flags()
- HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if
handler::records() gives an exact count() and
HA_STATS_RECORDS_IS_EXACT if stats.records is exact.
- HA_READ_RND_SAME Removed (no one supported this one)
- Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk()
- Renamed handler::dupp_pos to handler::dup_pos
- Removed not used variable handler::sortkey
Upper level handler changes:
- ha_reset() now does some overall checks and calls ::reset()
- ha_table_flags() added. This is a cached version of table_flags(). The
cache is updated on engine creation time and updated on open.
MySQL level changes (not obvious from the above):
- DBUG_ASSERT() added to check that column usage matches what is set
in the column usage bit maps. (This found a LOT of bugs in current
column marking code).
- In 5.1 before, all used columns was marked in read_set and only updated
columns was marked in write_set. Now we only mark columns for which we
need a value in read_set.
- Column bitmaps are created in open_binary_frm() and open_table_from_share().
(Before this was in table.cc)
- handler::table_flags() calls are replaced with handler::ha_table_flags()
- For calling field->val() you must have the corresponding bit set in
table->read_set. For calling field->store() you must have the
corresponding bit set in table->write_set. (There are asserts in
all store()/val() functions to catch wrong usage)
- thd->set_query_id is renamed to thd->mark_used_columns and instead
of setting this to an integer value, this has now the values:
MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE
Changed also all variables named 'set_query_id' to mark_used_columns.
- In filesort() we now inform the handler of exactly which columns are needed
doing the sort and choosing the rows.
- The TABLE_SHARE object has a 'all_set' column bitmap one can use
when one needs a column bitmap with all columns set.
(This is used for table->use_all_columns() and other places)
- The TABLE object has 3 column bitmaps:
- def_read_set Default bitmap for columns to be read
- def_write_set Default bitmap for columns to be written
- tmp_set Can be used as a temporary bitmap when needed.
The table object has also two pointer to bitmaps read_set and write_set
that the handler should use to find out which columns are used in which way.
- count() optimization now calls handler::records() instead of using
handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true).
- Added extra argument to Item::walk() to indicate if we should also
traverse sub queries.
- Added TABLE parameter to cp_buffer_from_ref()
- Don't close tables created with CREATE ... SELECT but keep them in
the table cache. (Faster usage of newly created tables).
New interfaces:
- table->clear_column_bitmaps() to initialize the bitmaps for tables
at start of new statements.
- table->column_bitmaps_set() to set up new column bitmaps and signal
the handler about this.
- table->column_bitmaps_set_no_signal() for some few cases where we need
to setup new column bitmaps but don't signal the handler (as the handler
has already been signaled about these before). Used for the momement
only in opt_range.cc when doing ROR scans.
- table->use_all_columns() to install a bitmap where all columns are marked
as use in the read and the write set.
- table->default_column_bitmaps() to install the normal read and write
column bitmaps, but not signaling the handler about this.
This is mainly used when creating TABLE instances.
- table->mark_columns_needed_for_delete(),
table->mark_columns_needed_for_delete() and
table->mark_columns_needed_for_insert() to allow us to put additional
columns in column usage maps if handler so requires.
(The handler indicates what it neads in handler->table_flags())
- table->prepare_for_position() to allow us to tell handler that it
needs to read primary key parts to be able to store them in
future table->position() calls.
(This replaces the table->file->ha_retrieve_all_pk function)
- table->mark_auto_increment_column() to tell handler are going to update
columns part of any auto_increment key.
- table->mark_columns_used_by_index() to mark all columns that is part of
an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow
it to quickly know that it only needs to read colums that are part
of the key. (The handler can also use the column map for detecting this,
but simpler/faster handler can just monitor the extra() call).
- table->mark_columns_used_by_index_no_reset() to in addition to other columns,
also mark all columns that is used by the given key.
- table->restore_column_maps_after_mark_index() to restore to default
column maps after a call to table->mark_columns_used_by_index().
- New item function register_field_in_read_map(), for marking used columns
in table->read_map. Used by filesort() to mark all used columns
- Maintain in TABLE->merge_keys set of all keys that are used in query.
(Simplices some optimization loops)
- Maintain Field->part_of_key_not_clustered which is like Field->part_of_key
but the field in the clustered key is not assumed to be part of all index.
(used in opt_range.cc for faster loops)
- dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map()
tmp_use_all_columns() and tmp_restore_column_map() functions to temporally
mark all columns as usable. The 'dbug_' version is primarily intended
inside a handler when it wants to just call Field:store() & Field::val()
functions, but don't need the column maps set for any other usage.
(ie:: bitmap_is_set() is never called)
- We can't use compare_records() to skip updates for handlers that returns
a partial column set and the read_set doesn't cover all columns in the
write set. The reason for this is that if we have a column marked only for
write we can't in the MySQL level know if the value changed or not.
The reason this worked before was that MySQL marked all to be written
columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden
bug'.
- open_table_from_share() does not anymore setup temporary MEM_ROOT
object as a thread specific variable for the handler. Instead we
send the to-be-used MEMROOT to get_new_handler().
(Simpler, faster code)
Bugs fixed:
- Column marking was not done correctly in a lot of cases.
(ALTER TABLE, when using triggers, auto_increment fields etc)
(Could potentially result in wrong values inserted in table handlers
relying on that the old column maps or field->set_query_id was correct)
Especially when it comes to triggers, there may be cases where the
old code would cause lost/wrong values for NDB and/or InnoDB tables.
- Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags:
OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG.
This allowed me to remove some wrong warnings about:
"Some non-transactional changed tables couldn't be rolled back"
- Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset
(thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose
some warnings about
"Some non-transactional changed tables couldn't be rolled back")
- Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table()
which could cause delete_table to report random failures.
- Fixed core dumps for some tests when running with --debug
- Added missing FN_LIBCHAR in mysql_rm_tmp_tables()
(This has probably caused us to not properly remove temporary files after
crash)
- slow_logs was not properly initialized, which could maybe cause
extra/lost entries in slow log.
- If we get an duplicate row on insert, change column map to read and
write all columns while retrying the operation. This is required by
the definition of REPLACE and also ensures that fields that are only
part of UPDATE are properly handled. This fixed a bug in NDB and
REPLACE where REPLACE wrongly copied some column values from the replaced
row.
- For table handler that doesn't support NULL in keys, we would give an error
when creating a primary key with NULL fields, even after the fields has been
automaticly converted to NOT NULL.
- Creating a primary key on a SPATIAL key, would fail if field was not
declared as NOT NULL.
Cleanups:
- Removed not used condition argument to setup_tables
- Removed not needed item function reset_query_id_processor().
- Field->add_index is removed. Now this is instead maintained in
(field->flags & FIELD_IN_ADD_INDEX)
- Field->fieldnr is removed (use field->field_index instead)
- New argument to filesort() to indicate that it should return a set of
row pointers (not used columns). This allowed me to remove some references
to sql_command in filesort and should also enable us to return column
results in some cases where we couldn't before.
- Changed column bitmap handling in opt_range.cc to be aligned with TABLE
bitmap, which allowed me to use bitmap functions instead of looping over
all fields to create some needed bitmaps. (Faster and smaller code)
- Broke up found too long lines
- Moved some variable declaration at start of function for better code
readability.
- Removed some not used arguments from functions.
(setup_fields(), mysql_prepare_insert_check_table())
- setup_fields() now takes an enum instead of an int for marking columns
usage.
- For internal temporary tables, use handler::write_row(),
handler::delete_row() and handler::update_row() instead of
handler::ha_xxxx() for faster execution.
- Changed some constants to enum's and define's.
- Using separate column read and write sets allows for easier checking
of timestamp field was set by statement.
- Remove calls to free_io_cache() as this is now done automaticly in ha_reset()
- Don't build table->normalized_path as this is now identical to table->path
(after bar's fixes to convert filenames)
- Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to
do comparision with the 'convert-dbug-for-diff' tool.
Things left to do in 5.1:
- We wrongly log failed CREATE TABLE ... SELECT in some cases when using
row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result)
Mats has promised to look into this.
- Test that my fix for CREATE TABLE ... SELECT is indeed correct.
(I added several test cases for this, but in this case it's better that
someone else also tests this throughly).
Lars has promosed to do this.
2006-06-04 17:52:22 +02:00
|
|
|
else if (!(file->ha_table_flags() & HA_NO_PREFIX_CHAR_KEYS))
|
2004-04-08 16:56:45 +02:00
|
|
|
length=column->length;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
else if (length == 0)
|
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_WRONG_KEY_COLUMN, MYF(0), column->field_name);
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
2006-01-13 18:25:12 +01:00
|
|
|
if (length > file->max_key_part_length() && key->type != Key::FULLTEXT)
|
2004-02-13 14:58:02 +01:00
|
|
|
{
|
2006-02-21 16:09:32 +01:00
|
|
|
length= file->max_key_part_length();
|
|
|
|
/* Align key length to multibyte char boundary */
|
|
|
|
length-= length % sql_field->charset->mbmaxlen;
|
2004-04-08 16:56:45 +02:00
|
|
|
if (key->type == Key::MULTIPLE)
|
|
|
|
{
|
|
|
|
/* not a critical problem */
|
|
|
|
char warn_buff[MYSQL_ERRMSG_SIZE];
|
|
|
|
my_snprintf(warn_buff, sizeof(warn_buff), ER(ER_TOO_LONG_KEY),
|
|
|
|
length);
|
|
|
|
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
|
|
ER_TOO_LONG_KEY, warn_buff);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
my_error(ER_TOO_LONG_KEY,MYF(0),length);
|
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
2004-02-13 14:58:02 +01:00
|
|
|
}
|
|
|
|
key_part_info->length=(uint16) length;
|
2000-07-31 21:29:14 +02:00
|
|
|
/* Use packed keys for long strings on the first column */
|
2005-04-01 14:04:50 +02:00
|
|
|
if (!((*db_options) & HA_OPTION_NO_PACK_KEYS) &&
|
2000-07-31 21:29:14 +02:00
|
|
|
(length >= KEY_DEFAULT_PACK_LENGTH &&
|
2004-12-06 01:00:37 +01:00
|
|
|
(sql_field->sql_type == MYSQL_TYPE_STRING ||
|
|
|
|
sql_field->sql_type == MYSQL_TYPE_VARCHAR ||
|
2000-07-31 21:29:14 +02:00
|
|
|
sql_field->pack_flag & FIELDFLAG_BLOB)))
|
|
|
|
{
|
2004-12-06 01:00:37 +01:00
|
|
|
if (column_nr == 0 && (sql_field->pack_flag & FIELDFLAG_BLOB) ||
|
|
|
|
sql_field->sql_type == MYSQL_TYPE_VARCHAR)
|
|
|
|
key_info->flags|= HA_BINARY_PACK_KEY | HA_VAR_LENGTH_KEY;
|
2000-07-31 21:29:14 +02:00
|
|
|
else
|
|
|
|
key_info->flags|= HA_PACK_KEY;
|
|
|
|
}
|
|
|
|
key_length+=length;
|
|
|
|
key_part_info++;
|
|
|
|
|
|
|
|
/* Create the key name based on the first column (if not given) */
|
|
|
|
if (column_nr == 0)
|
|
|
|
{
|
|
|
|
if (key->type == Key::PRIMARY)
|
2002-01-02 20:29:41 +01:00
|
|
|
{
|
|
|
|
if (primary_key)
|
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_MULTIPLE_PRI_KEY, ER(ER_MULTIPLE_PRI_KEY),
|
|
|
|
MYF(0));
|
2002-01-02 20:29:41 +01:00
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
|
|
|
key_name=primary_key_name;
|
|
|
|
primary_key=1;
|
|
|
|
}
|
2002-06-02 20:22:20 +02:00
|
|
|
else if (!(key_name = key->name))
|
2000-07-31 21:29:14 +02:00
|
|
|
key_name=make_unique_key_name(sql_field->field_name,
|
2005-04-01 14:04:50 +02:00
|
|
|
*key_info_buffer, key_info);
|
|
|
|
if (check_if_keyname_exists(key_name, *key_info_buffer, key_info))
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_DUP_KEYNAME, MYF(0), key_name);
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
|
|
|
key_info->name=(char*) key_name;
|
|
|
|
}
|
|
|
|
}
|
2003-10-16 17:33:44 +02:00
|
|
|
if (!key_info->name || check_column_name(key_info->name))
|
|
|
|
{
|
2003-11-18 10:23:49 +01:00
|
|
|
my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key_info->name);
|
2003-10-16 17:33:44 +02:00
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
2002-01-02 20:29:41 +01:00
|
|
|
if (!(key_info->flags & HA_NULL_PART_KEY))
|
|
|
|
unique_key=1;
|
2000-07-31 21:29:14 +02:00
|
|
|
key_info->key_length=(uint16) key_length;
|
2001-12-23 01:43:46 +01:00
|
|
|
if (key_length > max_key_length && key->type != Key::FULLTEXT)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2001-12-23 01:43:46 +01:00
|
|
|
my_error(ER_TOO_LONG_KEY,MYF(0),max_key_length);
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
2002-11-15 15:37:44 +01:00
|
|
|
key_info++;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2002-01-02 20:29:41 +01:00
|
|
|
if (!unique_key && !primary_key &&
|
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes
Changes that requires code changes in other code of other storage engines.
(Note that all changes are very straightforward and one should find all issues
by compiling a --debug build and fixing all compiler errors and all
asserts in field.cc while running the test suite),
- New optional handler function introduced: reset()
This is called after every DML statement to make it easy for a handler to
statement specific cleanups.
(The only case it's not called is if force the file to be closed)
- handler::extra(HA_EXTRA_RESET) is removed. Code that was there before
should be moved to handler::reset()
- table->read_set contains a bitmap over all columns that are needed
in the query. read_row() and similar functions only needs to read these
columns
- table->write_set contains a bitmap over all columns that will be updated
in the query. write_row() and update_row() only needs to update these
columns.
The above bitmaps should now be up to date in all context
(including ALTER TABLE, filesort()).
The handler is informed of any changes to the bitmap after
fix_fields() by calling the virtual function
handler::column_bitmaps_signal(). If the handler does caching of
these bitmaps (instead of using table->read_set, table->write_set),
it should redo the caching in this code. as the signal() may be sent
several times, it's probably best to set a variable in the signal
and redo the caching on read_row() / write_row() if the variable was
set.
- Removed the read_set and write_set bitmap objects from the handler class
- Removed all column bit handling functions from the handler class.
(Now one instead uses the normal bitmap functions in my_bitmap.c instead
of handler dedicated bitmap functions)
- field->query_id is removed. One should instead instead check
table->read_set and table->write_set if a field is used in the query.
- handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and
handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now
instead use table->read_set to check for which columns to retrieve.
- If a handler needs to call Field->val() or Field->store() on columns
that are not used in the query, one should install a temporary
all-columns-used map while doing so. For this, we provide the following
functions:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
field->val();
dbug_tmp_restore_column_map(table->read_set, old_map);
and similar for the write map:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
field->val();
dbug_tmp_restore_column_map(table->write_set, old_map);
If this is not done, you will sooner or later hit a DBUG_ASSERT
in the field store() / val() functions.
(For not DBUG binaries, the dbug_tmp_restore_column_map() and
dbug_tmp_restore_column_map() are inline dummy functions and should
be optimized away be the compiler).
- If one needs to temporary set the column map for all binaries (and not
just to avoid the DBUG_ASSERT() in the Field::store() / Field::val()
methods) one should use the functions tmp_use_all_columns() and
tmp_restore_column_map() instead of the above dbug_ variants.
- All 'status' fields in the handler base class (like records,
data_file_length etc) are now stored in a 'stats' struct. This makes
it easier to know what status variables are provided by the base
handler. This requires some trivial variable names in the extra()
function.
- New virtual function handler::records(). This is called to optimize
COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true.
(stats.records is not supposed to be an exact value. It's only has to
be 'reasonable enough' for the optimizer to be able to choose a good
optimization path).
- Non virtual handler::init() function added for caching of virtual
constants from engine.
- Removed has_transactions() virtual method. Now one should instead return
HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support
transactions.
- The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument
that is to be used with 'new handler_name()' to allocate the handler
in the right area. The xxxx_create_handler() function is also
responsible for any initialization of the object before returning.
For example, one should change:
static handler *myisam_create_handler(TABLE_SHARE *table)
{
return new ha_myisam(table);
}
->
static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root)
{
return new (mem_root) ha_myisam(table);
}
- New optional virtual function: use_hidden_primary_key().
This is called in case of an update/delete when
(table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
but we don't have a primary key. This allows the handler to take precisions
in remembering any hidden primary key to able to update/delete any
found row. The default handler marks all columns to be read.
- handler::table_flags() now returns a ulonglong (to allow for more flags).
- New/changed table_flags()
- HA_HAS_RECORDS Set if ::records() is supported
- HA_NO_TRANSACTIONS Set if engine doesn't support transactions
- HA_PRIMARY_KEY_REQUIRED_FOR_DELETE
Set if we should mark all primary key columns for
read when reading rows as part of a DELETE
statement. If there is no primary key,
all columns are marked for read.
- HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some
cases (based on table->read_set)
- HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS
Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION.
- HA_DUPP_POS Renamed to HA_DUPLICATE_POS
- HA_REQUIRES_KEY_COLUMNS_FOR_DELETE
Set this if we should mark ALL key columns for
read when when reading rows as part of a DELETE
statement. In case of an update we will mark
all keys for read for which key part changed
value.
- HA_STATS_RECORDS_IS_EXACT
Set this if stats.records is exact.
(This saves us some extra records() calls
when optimizing COUNT(*))
- Removed table_flags()
- HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if
handler::records() gives an exact count() and
HA_STATS_RECORDS_IS_EXACT if stats.records is exact.
- HA_READ_RND_SAME Removed (no one supported this one)
- Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk()
- Renamed handler::dupp_pos to handler::dup_pos
- Removed not used variable handler::sortkey
Upper level handler changes:
- ha_reset() now does some overall checks and calls ::reset()
- ha_table_flags() added. This is a cached version of table_flags(). The
cache is updated on engine creation time and updated on open.
MySQL level changes (not obvious from the above):
- DBUG_ASSERT() added to check that column usage matches what is set
in the column usage bit maps. (This found a LOT of bugs in current
column marking code).
- In 5.1 before, all used columns was marked in read_set and only updated
columns was marked in write_set. Now we only mark columns for which we
need a value in read_set.
- Column bitmaps are created in open_binary_frm() and open_table_from_share().
(Before this was in table.cc)
- handler::table_flags() calls are replaced with handler::ha_table_flags()
- For calling field->val() you must have the corresponding bit set in
table->read_set. For calling field->store() you must have the
corresponding bit set in table->write_set. (There are asserts in
all store()/val() functions to catch wrong usage)
- thd->set_query_id is renamed to thd->mark_used_columns and instead
of setting this to an integer value, this has now the values:
MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE
Changed also all variables named 'set_query_id' to mark_used_columns.
- In filesort() we now inform the handler of exactly which columns are needed
doing the sort and choosing the rows.
- The TABLE_SHARE object has a 'all_set' column bitmap one can use
when one needs a column bitmap with all columns set.
(This is used for table->use_all_columns() and other places)
- The TABLE object has 3 column bitmaps:
- def_read_set Default bitmap for columns to be read
- def_write_set Default bitmap for columns to be written
- tmp_set Can be used as a temporary bitmap when needed.
The table object has also two pointer to bitmaps read_set and write_set
that the handler should use to find out which columns are used in which way.
- count() optimization now calls handler::records() instead of using
handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true).
- Added extra argument to Item::walk() to indicate if we should also
traverse sub queries.
- Added TABLE parameter to cp_buffer_from_ref()
- Don't close tables created with CREATE ... SELECT but keep them in
the table cache. (Faster usage of newly created tables).
New interfaces:
- table->clear_column_bitmaps() to initialize the bitmaps for tables
at start of new statements.
- table->column_bitmaps_set() to set up new column bitmaps and signal
the handler about this.
- table->column_bitmaps_set_no_signal() for some few cases where we need
to setup new column bitmaps but don't signal the handler (as the handler
has already been signaled about these before). Used for the momement
only in opt_range.cc when doing ROR scans.
- table->use_all_columns() to install a bitmap where all columns are marked
as use in the read and the write set.
- table->default_column_bitmaps() to install the normal read and write
column bitmaps, but not signaling the handler about this.
This is mainly used when creating TABLE instances.
- table->mark_columns_needed_for_delete(),
table->mark_columns_needed_for_delete() and
table->mark_columns_needed_for_insert() to allow us to put additional
columns in column usage maps if handler so requires.
(The handler indicates what it neads in handler->table_flags())
- table->prepare_for_position() to allow us to tell handler that it
needs to read primary key parts to be able to store them in
future table->position() calls.
(This replaces the table->file->ha_retrieve_all_pk function)
- table->mark_auto_increment_column() to tell handler are going to update
columns part of any auto_increment key.
- table->mark_columns_used_by_index() to mark all columns that is part of
an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow
it to quickly know that it only needs to read colums that are part
of the key. (The handler can also use the column map for detecting this,
but simpler/faster handler can just monitor the extra() call).
- table->mark_columns_used_by_index_no_reset() to in addition to other columns,
also mark all columns that is used by the given key.
- table->restore_column_maps_after_mark_index() to restore to default
column maps after a call to table->mark_columns_used_by_index().
- New item function register_field_in_read_map(), for marking used columns
in table->read_map. Used by filesort() to mark all used columns
- Maintain in TABLE->merge_keys set of all keys that are used in query.
(Simplices some optimization loops)
- Maintain Field->part_of_key_not_clustered which is like Field->part_of_key
but the field in the clustered key is not assumed to be part of all index.
(used in opt_range.cc for faster loops)
- dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map()
tmp_use_all_columns() and tmp_restore_column_map() functions to temporally
mark all columns as usable. The 'dbug_' version is primarily intended
inside a handler when it wants to just call Field:store() & Field::val()
functions, but don't need the column maps set for any other usage.
(ie:: bitmap_is_set() is never called)
- We can't use compare_records() to skip updates for handlers that returns
a partial column set and the read_set doesn't cover all columns in the
write set. The reason for this is that if we have a column marked only for
write we can't in the MySQL level know if the value changed or not.
The reason this worked before was that MySQL marked all to be written
columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden
bug'.
- open_table_from_share() does not anymore setup temporary MEM_ROOT
object as a thread specific variable for the handler. Instead we
send the to-be-used MEMROOT to get_new_handler().
(Simpler, faster code)
Bugs fixed:
- Column marking was not done correctly in a lot of cases.
(ALTER TABLE, when using triggers, auto_increment fields etc)
(Could potentially result in wrong values inserted in table handlers
relying on that the old column maps or field->set_query_id was correct)
Especially when it comes to triggers, there may be cases where the
old code would cause lost/wrong values for NDB and/or InnoDB tables.
- Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags:
OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG.
This allowed me to remove some wrong warnings about:
"Some non-transactional changed tables couldn't be rolled back"
- Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset
(thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose
some warnings about
"Some non-transactional changed tables couldn't be rolled back")
- Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table()
which could cause delete_table to report random failures.
- Fixed core dumps for some tests when running with --debug
- Added missing FN_LIBCHAR in mysql_rm_tmp_tables()
(This has probably caused us to not properly remove temporary files after
crash)
- slow_logs was not properly initialized, which could maybe cause
extra/lost entries in slow log.
- If we get an duplicate row on insert, change column map to read and
write all columns while retrying the operation. This is required by
the definition of REPLACE and also ensures that fields that are only
part of UPDATE are properly handled. This fixed a bug in NDB and
REPLACE where REPLACE wrongly copied some column values from the replaced
row.
- For table handler that doesn't support NULL in keys, we would give an error
when creating a primary key with NULL fields, even after the fields has been
automaticly converted to NOT NULL.
- Creating a primary key on a SPATIAL key, would fail if field was not
declared as NOT NULL.
Cleanups:
- Removed not used condition argument to setup_tables
- Removed not needed item function reset_query_id_processor().
- Field->add_index is removed. Now this is instead maintained in
(field->flags & FIELD_IN_ADD_INDEX)
- Field->fieldnr is removed (use field->field_index instead)
- New argument to filesort() to indicate that it should return a set of
row pointers (not used columns). This allowed me to remove some references
to sql_command in filesort and should also enable us to return column
results in some cases where we couldn't before.
- Changed column bitmap handling in opt_range.cc to be aligned with TABLE
bitmap, which allowed me to use bitmap functions instead of looping over
all fields to create some needed bitmaps. (Faster and smaller code)
- Broke up found too long lines
- Moved some variable declaration at start of function for better code
readability.
- Removed some not used arguments from functions.
(setup_fields(), mysql_prepare_insert_check_table())
- setup_fields() now takes an enum instead of an int for marking columns
usage.
- For internal temporary tables, use handler::write_row(),
handler::delete_row() and handler::update_row() instead of
handler::ha_xxxx() for faster execution.
- Changed some constants to enum's and define's.
- Using separate column read and write sets allows for easier checking
of timestamp field was set by statement.
- Remove calls to free_io_cache() as this is now done automaticly in ha_reset()
- Don't build table->normalized_path as this is now identical to table->path
(after bar's fixes to convert filenames)
- Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to
do comparision with the 'convert-dbug-for-diff' tool.
Things left to do in 5.1:
- We wrongly log failed CREATE TABLE ... SELECT in some cases when using
row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result)
Mats has promised to look into this.
- Test that my fix for CREATE TABLE ... SELECT is indeed correct.
(I added several test cases for this, but in this case it's better that
someone else also tests this throughly).
Lars has promosed to do this.
2006-06-04 17:52:22 +02:00
|
|
|
(file->ha_table_flags() & HA_REQUIRE_PRIMARY_KEY))
|
2002-01-02 20:29:41 +01:00
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_REQUIRES_PRIMARY_KEY, ER(ER_REQUIRES_PRIMARY_KEY), MYF(0));
|
2002-01-02 20:29:41 +01:00
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
if (auto_increment > 0)
|
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_WRONG_AUTO_KEY, ER(ER_WRONG_AUTO_KEY), MYF(0));
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
2002-01-02 20:29:41 +01:00
|
|
|
/* Sort keys in optimized order */
|
2005-04-01 14:04:50 +02:00
|
|
|
qsort((gptr) *key_info_buffer, *key_count, sizeof(KEY),
|
2004-04-08 16:56:45 +02:00
|
|
|
(qsort_cmp) sort_keys);
|
2005-05-14 15:24:36 +02:00
|
|
|
create_info->null_bits= null_fields;
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2004-03-30 19:22:14 +02:00
|
|
|
DBUG_RETURN(0);
|
|
|
|
}
|
|
|
|
|
2004-04-08 16:56:45 +02:00
|
|
|
|
2005-07-22 22:43:59 +02:00
|
|
|
/*
|
|
|
|
Set table default charset, if not set
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
set_table_default_charset()
|
|
|
|
create_info Table create information
|
|
|
|
|
|
|
|
DESCRIPTION
|
|
|
|
If the table character set was not given explicitely,
|
|
|
|
let's fetch the database default character set and
|
|
|
|
apply it to the table.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void set_table_default_charset(THD *thd,
|
|
|
|
HA_CREATE_INFO *create_info, char *db)
|
|
|
|
{
|
|
|
|
if (!create_info->default_table_charset)
|
|
|
|
{
|
|
|
|
HA_CREATE_INFO db_info;
|
|
|
|
char path[FN_REFLEN];
|
2005-12-31 06:01:26 +01:00
|
|
|
/* Abuse build_table_filename() to build the path to the db.opt file */
|
|
|
|
build_table_filename(path, sizeof(path), db, "", MY_DB_OPT_FILE);
|
2005-07-22 22:43:59 +02:00
|
|
|
load_db_opt(thd, path, &db_info);
|
|
|
|
create_info->default_table_charset= db_info.default_table_charset;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-05-06 10:39:30 +02:00
|
|
|
/*
|
|
|
|
Extend long VARCHAR fields to blob & prepare field if it's a blob
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
prepare_blob_field()
|
|
|
|
sql_field Field to check
|
|
|
|
|
|
|
|
RETURN
|
|
|
|
0 ok
|
|
|
|
1 Error (sql_field can't be converted to blob)
|
|
|
|
In this case the error is given
|
|
|
|
*/
|
|
|
|
|
|
|
|
static bool prepare_blob_field(THD *thd, create_field *sql_field)
|
|
|
|
{
|
|
|
|
DBUG_ENTER("prepare_blob_field");
|
|
|
|
|
|
|
|
if (sql_field->length > MAX_FIELD_VARCHARLENGTH &&
|
|
|
|
!(sql_field->flags & BLOB_FLAG))
|
|
|
|
{
|
|
|
|
/* Convert long VARCHAR columns to TEXT or BLOB */
|
|
|
|
char warn_buff[MYSQL_ERRMSG_SIZE];
|
|
|
|
|
2005-05-12 12:56:04 +02:00
|
|
|
if (sql_field->def || (thd->variables.sql_mode & (MODE_STRICT_TRANS_TABLES |
|
|
|
|
MODE_STRICT_ALL_TABLES)))
|
2005-05-06 10:39:30 +02:00
|
|
|
{
|
|
|
|
my_error(ER_TOO_BIG_FIELDLENGTH, MYF(0), sql_field->field_name,
|
|
|
|
MAX_FIELD_VARCHARLENGTH / sql_field->charset->mbmaxlen);
|
|
|
|
DBUG_RETURN(1);
|
|
|
|
}
|
|
|
|
sql_field->sql_type= FIELD_TYPE_BLOB;
|
|
|
|
sql_field->flags|= BLOB_FLAG;
|
|
|
|
sprintf(warn_buff, ER(ER_AUTO_CONVERT), sql_field->field_name,
|
2005-05-12 12:56:04 +02:00
|
|
|
(sql_field->charset == &my_charset_bin) ? "VARBINARY" : "VARCHAR",
|
2005-05-06 10:39:30 +02:00
|
|
|
(sql_field->charset == &my_charset_bin) ? "BLOB" : "TEXT");
|
|
|
|
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_AUTO_CONVERT,
|
|
|
|
warn_buff);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((sql_field->flags & BLOB_FLAG) && sql_field->length)
|
|
|
|
{
|
|
|
|
if (sql_field->sql_type == FIELD_TYPE_BLOB)
|
|
|
|
{
|
|
|
|
/* The user has given a length to the blob column */
|
|
|
|
sql_field->sql_type= get_blob_type_from_length(sql_field->length);
|
|
|
|
sql_field->pack_length= calc_pack_length(sql_field->sql_type, 0);
|
|
|
|
}
|
|
|
|
sql_field->length= 0;
|
|
|
|
}
|
|
|
|
DBUG_RETURN(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-04-19 10:09:25 +02:00
|
|
|
/*
|
|
|
|
Preparation of create_field for SP function return values.
|
|
|
|
Based on code used in the inner loop of mysql_prepare_table() above
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
sp_prepare_create_field()
|
|
|
|
thd Thread object
|
|
|
|
sql_field Field to prepare
|
|
|
|
|
|
|
|
DESCRIPTION
|
|
|
|
Prepares the field structures for field creation.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
void sp_prepare_create_field(THD *thd, create_field *sql_field)
|
|
|
|
{
|
|
|
|
if (sql_field->sql_type == FIELD_TYPE_SET ||
|
|
|
|
sql_field->sql_type == FIELD_TYPE_ENUM)
|
|
|
|
{
|
|
|
|
uint32 field_length, dummy;
|
|
|
|
if (sql_field->sql_type == FIELD_TYPE_SET)
|
|
|
|
{
|
|
|
|
calculate_interval_lengths(sql_field->charset,
|
|
|
|
sql_field->interval, &dummy,
|
|
|
|
&field_length);
|
|
|
|
sql_field->length= field_length +
|
|
|
|
(sql_field->interval->count - 1);
|
|
|
|
}
|
|
|
|
else /* FIELD_TYPE_ENUM */
|
|
|
|
{
|
|
|
|
calculate_interval_lengths(sql_field->charset,
|
|
|
|
sql_field->interval,
|
|
|
|
&field_length, &dummy);
|
|
|
|
sql_field->length= field_length;
|
|
|
|
}
|
|
|
|
set_if_smaller(sql_field->length, MAX_FIELD_WIDTH-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sql_field->sql_type == FIELD_TYPE_BIT)
|
|
|
|
{
|
|
|
|
sql_field->pack_flag= FIELDFLAG_NUMBER |
|
|
|
|
FIELDFLAG_TREAT_BIT_AS_CHAR;
|
|
|
|
}
|
|
|
|
sql_field->create_length_to_internal_length();
|
2005-05-06 10:39:30 +02:00
|
|
|
DBUG_ASSERT(sql_field->def == 0);
|
|
|
|
/* Can't go wrong as sql_field->def is not defined */
|
|
|
|
(void) prepare_blob_field(thd, sql_field);
|
|
|
|
}
|
2005-04-19 10:09:25 +02:00
|
|
|
|
|
|
|
|
2006-06-15 20:03:17 +02:00
|
|
|
/*
|
|
|
|
Copy HA_CREATE_INFO struct
|
|
|
|
SYNOPSIS
|
|
|
|
copy_create_info()
|
|
|
|
lex_create_info The create_info struct setup by parser
|
|
|
|
RETURN VALUES
|
|
|
|
> 0 A pointer to a copy of the lex_create_info
|
|
|
|
0 Memory allocation error
|
|
|
|
DESCRIPTION
|
|
|
|
Allocate memory for copy of HA_CREATE_INFO structure from parser
|
|
|
|
to ensure we can reuse the parser struct in stored procedures
|
|
|
|
and prepared statements.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static HA_CREATE_INFO *copy_create_info(HA_CREATE_INFO *lex_create_info)
|
|
|
|
{
|
|
|
|
HA_CREATE_INFO *create_info;
|
|
|
|
if (!(create_info= (HA_CREATE_INFO*)sql_alloc(sizeof(HA_CREATE_INFO))))
|
|
|
|
mem_alloc_error(sizeof(HA_CREATE_INFO));
|
|
|
|
else
|
|
|
|
memcpy((void*)create_info, (void*)lex_create_info, sizeof(HA_CREATE_INFO));
|
|
|
|
return create_info;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-30 19:22:14 +02:00
|
|
|
/*
|
|
|
|
Create a table
|
|
|
|
|
|
|
|
SYNOPSIS
|
2006-02-13 08:49:28 +01:00
|
|
|
mysql_create_table_internal()
|
2004-04-08 16:56:45 +02:00
|
|
|
thd Thread object
|
|
|
|
db Database
|
|
|
|
table_name Table name
|
2006-06-21 16:57:30 +02:00
|
|
|
lex_create_info Create information (like MAX_ROWS)
|
2004-04-08 16:56:45 +02:00
|
|
|
fields List of fields to create
|
|
|
|
keys List of keys to create
|
2005-01-27 22:38:56 +01:00
|
|
|
internal_tmp_table Set to 1 if this is an internal temporary table
|
2004-04-08 16:56:45 +02:00
|
|
|
(From ALTER TABLE)
|
2006-06-21 16:57:30 +02:00
|
|
|
select_field_count
|
|
|
|
use_copy_create_info Should we make a copy of create info (we do this
|
|
|
|
when this is called from sql_parse.cc where we
|
|
|
|
want to ensure lex object isn't manipulated.
|
2004-03-30 19:22:14 +02:00
|
|
|
|
|
|
|
DESCRIPTION
|
2004-10-21 18:10:58 +02:00
|
|
|
If one creates a temporary table, this is automatically opened
|
2004-03-30 19:22:14 +02:00
|
|
|
|
|
|
|
no_log is needed for the case of CREATE ... SELECT,
|
|
|
|
as the logging will be done later in sql_insert.cc
|
|
|
|
select_field_count is also used for CREATE ... SELECT,
|
|
|
|
and must be zero for standard create of table.
|
|
|
|
|
|
|
|
RETURN VALUES
|
2004-10-20 03:04:37 +02:00
|
|
|
FALSE OK
|
|
|
|
TRUE error
|
2004-03-30 19:22:14 +02:00
|
|
|
*/
|
|
|
|
|
2006-02-13 08:49:28 +01:00
|
|
|
bool mysql_create_table_internal(THD *thd,
|
|
|
|
const char *db, const char *table_name,
|
2006-06-15 20:03:17 +02:00
|
|
|
HA_CREATE_INFO *lex_create_info,
|
2006-02-13 08:49:28 +01:00
|
|
|
List<create_field> &fields,
|
|
|
|
List<Key> &keys,bool internal_tmp_table,
|
2006-06-21 16:57:30 +02:00
|
|
|
uint select_field_count,
|
|
|
|
bool use_copy_create_info)
|
2004-03-30 19:22:14 +02:00
|
|
|
{
|
2004-04-08 16:56:45 +02:00
|
|
|
char path[FN_REFLEN];
|
2005-12-31 06:01:26 +01:00
|
|
|
uint path_length;
|
2004-04-08 16:56:45 +02:00
|
|
|
const char *alias;
|
|
|
|
uint db_options, key_count;
|
|
|
|
KEY *key_info_buffer;
|
2006-06-15 20:03:17 +02:00
|
|
|
HA_CREATE_INFO *create_info;
|
2004-04-08 16:56:45 +02:00
|
|
|
handler *file;
|
2004-10-20 03:04:37 +02:00
|
|
|
bool error= TRUE;
|
2006-02-13 08:49:28 +01:00
|
|
|
DBUG_ENTER("mysql_create_table_internal");
|
2004-03-30 19:22:14 +02:00
|
|
|
|
2006-06-21 16:57:30 +02:00
|
|
|
if (use_copy_create_info)
|
2006-06-15 20:03:17 +02:00
|
|
|
{
|
2006-06-21 16:57:30 +02:00
|
|
|
if (!(create_info= copy_create_info(lex_create_info)))
|
|
|
|
{
|
|
|
|
DBUG_RETURN(TRUE);
|
|
|
|
}
|
2006-06-15 20:03:17 +02:00
|
|
|
}
|
2006-06-21 16:57:30 +02:00
|
|
|
else
|
|
|
|
create_info= lex_create_info;
|
|
|
|
|
2004-03-30 19:22:14 +02:00
|
|
|
/* Check for duplicate fields and check type of table to create */
|
|
|
|
if (!fields.elements)
|
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_TABLE_MUST_HAVE_COLUMNS, ER(ER_TABLE_MUST_HAVE_COLUMNS),
|
|
|
|
MYF(0));
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
2004-03-30 19:22:14 +02:00
|
|
|
}
|
2006-02-17 17:12:35 +01:00
|
|
|
if (check_engine(thd, table_name, create_info))
|
2005-06-17 23:14:44 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
2005-04-01 14:04:50 +02:00
|
|
|
db_options= create_info->table_options;
|
2004-03-30 19:22:14 +02:00
|
|
|
if (create_info->row_type == ROW_TYPE_DYNAMIC)
|
|
|
|
db_options|=HA_OPTION_PACK_RECORD;
|
|
|
|
alias= table_case_name(create_info, table_name);
|
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes
Changes that requires code changes in other code of other storage engines.
(Note that all changes are very straightforward and one should find all issues
by compiling a --debug build and fixing all compiler errors and all
asserts in field.cc while running the test suite),
- New optional handler function introduced: reset()
This is called after every DML statement to make it easy for a handler to
statement specific cleanups.
(The only case it's not called is if force the file to be closed)
- handler::extra(HA_EXTRA_RESET) is removed. Code that was there before
should be moved to handler::reset()
- table->read_set contains a bitmap over all columns that are needed
in the query. read_row() and similar functions only needs to read these
columns
- table->write_set contains a bitmap over all columns that will be updated
in the query. write_row() and update_row() only needs to update these
columns.
The above bitmaps should now be up to date in all context
(including ALTER TABLE, filesort()).
The handler is informed of any changes to the bitmap after
fix_fields() by calling the virtual function
handler::column_bitmaps_signal(). If the handler does caching of
these bitmaps (instead of using table->read_set, table->write_set),
it should redo the caching in this code. as the signal() may be sent
several times, it's probably best to set a variable in the signal
and redo the caching on read_row() / write_row() if the variable was
set.
- Removed the read_set and write_set bitmap objects from the handler class
- Removed all column bit handling functions from the handler class.
(Now one instead uses the normal bitmap functions in my_bitmap.c instead
of handler dedicated bitmap functions)
- field->query_id is removed. One should instead instead check
table->read_set and table->write_set if a field is used in the query.
- handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and
handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now
instead use table->read_set to check for which columns to retrieve.
- If a handler needs to call Field->val() or Field->store() on columns
that are not used in the query, one should install a temporary
all-columns-used map while doing so. For this, we provide the following
functions:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
field->val();
dbug_tmp_restore_column_map(table->read_set, old_map);
and similar for the write map:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
field->val();
dbug_tmp_restore_column_map(table->write_set, old_map);
If this is not done, you will sooner or later hit a DBUG_ASSERT
in the field store() / val() functions.
(For not DBUG binaries, the dbug_tmp_restore_column_map() and
dbug_tmp_restore_column_map() are inline dummy functions and should
be optimized away be the compiler).
- If one needs to temporary set the column map for all binaries (and not
just to avoid the DBUG_ASSERT() in the Field::store() / Field::val()
methods) one should use the functions tmp_use_all_columns() and
tmp_restore_column_map() instead of the above dbug_ variants.
- All 'status' fields in the handler base class (like records,
data_file_length etc) are now stored in a 'stats' struct. This makes
it easier to know what status variables are provided by the base
handler. This requires some trivial variable names in the extra()
function.
- New virtual function handler::records(). This is called to optimize
COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true.
(stats.records is not supposed to be an exact value. It's only has to
be 'reasonable enough' for the optimizer to be able to choose a good
optimization path).
- Non virtual handler::init() function added for caching of virtual
constants from engine.
- Removed has_transactions() virtual method. Now one should instead return
HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support
transactions.
- The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument
that is to be used with 'new handler_name()' to allocate the handler
in the right area. The xxxx_create_handler() function is also
responsible for any initialization of the object before returning.
For example, one should change:
static handler *myisam_create_handler(TABLE_SHARE *table)
{
return new ha_myisam(table);
}
->
static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root)
{
return new (mem_root) ha_myisam(table);
}
- New optional virtual function: use_hidden_primary_key().
This is called in case of an update/delete when
(table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
but we don't have a primary key. This allows the handler to take precisions
in remembering any hidden primary key to able to update/delete any
found row. The default handler marks all columns to be read.
- handler::table_flags() now returns a ulonglong (to allow for more flags).
- New/changed table_flags()
- HA_HAS_RECORDS Set if ::records() is supported
- HA_NO_TRANSACTIONS Set if engine doesn't support transactions
- HA_PRIMARY_KEY_REQUIRED_FOR_DELETE
Set if we should mark all primary key columns for
read when reading rows as part of a DELETE
statement. If there is no primary key,
all columns are marked for read.
- HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some
cases (based on table->read_set)
- HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS
Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION.
- HA_DUPP_POS Renamed to HA_DUPLICATE_POS
- HA_REQUIRES_KEY_COLUMNS_FOR_DELETE
Set this if we should mark ALL key columns for
read when when reading rows as part of a DELETE
statement. In case of an update we will mark
all keys for read for which key part changed
value.
- HA_STATS_RECORDS_IS_EXACT
Set this if stats.records is exact.
(This saves us some extra records() calls
when optimizing COUNT(*))
- Removed table_flags()
- HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if
handler::records() gives an exact count() and
HA_STATS_RECORDS_IS_EXACT if stats.records is exact.
- HA_READ_RND_SAME Removed (no one supported this one)
- Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk()
- Renamed handler::dupp_pos to handler::dup_pos
- Removed not used variable handler::sortkey
Upper level handler changes:
- ha_reset() now does some overall checks and calls ::reset()
- ha_table_flags() added. This is a cached version of table_flags(). The
cache is updated on engine creation time and updated on open.
MySQL level changes (not obvious from the above):
- DBUG_ASSERT() added to check that column usage matches what is set
in the column usage bit maps. (This found a LOT of bugs in current
column marking code).
- In 5.1 before, all used columns was marked in read_set and only updated
columns was marked in write_set. Now we only mark columns for which we
need a value in read_set.
- Column bitmaps are created in open_binary_frm() and open_table_from_share().
(Before this was in table.cc)
- handler::table_flags() calls are replaced with handler::ha_table_flags()
- For calling field->val() you must have the corresponding bit set in
table->read_set. For calling field->store() you must have the
corresponding bit set in table->write_set. (There are asserts in
all store()/val() functions to catch wrong usage)
- thd->set_query_id is renamed to thd->mark_used_columns and instead
of setting this to an integer value, this has now the values:
MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE
Changed also all variables named 'set_query_id' to mark_used_columns.
- In filesort() we now inform the handler of exactly which columns are needed
doing the sort and choosing the rows.
- The TABLE_SHARE object has a 'all_set' column bitmap one can use
when one needs a column bitmap with all columns set.
(This is used for table->use_all_columns() and other places)
- The TABLE object has 3 column bitmaps:
- def_read_set Default bitmap for columns to be read
- def_write_set Default bitmap for columns to be written
- tmp_set Can be used as a temporary bitmap when needed.
The table object has also two pointer to bitmaps read_set and write_set
that the handler should use to find out which columns are used in which way.
- count() optimization now calls handler::records() instead of using
handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true).
- Added extra argument to Item::walk() to indicate if we should also
traverse sub queries.
- Added TABLE parameter to cp_buffer_from_ref()
- Don't close tables created with CREATE ... SELECT but keep them in
the table cache. (Faster usage of newly created tables).
New interfaces:
- table->clear_column_bitmaps() to initialize the bitmaps for tables
at start of new statements.
- table->column_bitmaps_set() to set up new column bitmaps and signal
the handler about this.
- table->column_bitmaps_set_no_signal() for some few cases where we need
to setup new column bitmaps but don't signal the handler (as the handler
has already been signaled about these before). Used for the momement
only in opt_range.cc when doing ROR scans.
- table->use_all_columns() to install a bitmap where all columns are marked
as use in the read and the write set.
- table->default_column_bitmaps() to install the normal read and write
column bitmaps, but not signaling the handler about this.
This is mainly used when creating TABLE instances.
- table->mark_columns_needed_for_delete(),
table->mark_columns_needed_for_delete() and
table->mark_columns_needed_for_insert() to allow us to put additional
columns in column usage maps if handler so requires.
(The handler indicates what it neads in handler->table_flags())
- table->prepare_for_position() to allow us to tell handler that it
needs to read primary key parts to be able to store them in
future table->position() calls.
(This replaces the table->file->ha_retrieve_all_pk function)
- table->mark_auto_increment_column() to tell handler are going to update
columns part of any auto_increment key.
- table->mark_columns_used_by_index() to mark all columns that is part of
an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow
it to quickly know that it only needs to read colums that are part
of the key. (The handler can also use the column map for detecting this,
but simpler/faster handler can just monitor the extra() call).
- table->mark_columns_used_by_index_no_reset() to in addition to other columns,
also mark all columns that is used by the given key.
- table->restore_column_maps_after_mark_index() to restore to default
column maps after a call to table->mark_columns_used_by_index().
- New item function register_field_in_read_map(), for marking used columns
in table->read_map. Used by filesort() to mark all used columns
- Maintain in TABLE->merge_keys set of all keys that are used in query.
(Simplices some optimization loops)
- Maintain Field->part_of_key_not_clustered which is like Field->part_of_key
but the field in the clustered key is not assumed to be part of all index.
(used in opt_range.cc for faster loops)
- dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map()
tmp_use_all_columns() and tmp_restore_column_map() functions to temporally
mark all columns as usable. The 'dbug_' version is primarily intended
inside a handler when it wants to just call Field:store() & Field::val()
functions, but don't need the column maps set for any other usage.
(ie:: bitmap_is_set() is never called)
- We can't use compare_records() to skip updates for handlers that returns
a partial column set and the read_set doesn't cover all columns in the
write set. The reason for this is that if we have a column marked only for
write we can't in the MySQL level know if the value changed or not.
The reason this worked before was that MySQL marked all to be written
columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden
bug'.
- open_table_from_share() does not anymore setup temporary MEM_ROOT
object as a thread specific variable for the handler. Instead we
send the to-be-used MEMROOT to get_new_handler().
(Simpler, faster code)
Bugs fixed:
- Column marking was not done correctly in a lot of cases.
(ALTER TABLE, when using triggers, auto_increment fields etc)
(Could potentially result in wrong values inserted in table handlers
relying on that the old column maps or field->set_query_id was correct)
Especially when it comes to triggers, there may be cases where the
old code would cause lost/wrong values for NDB and/or InnoDB tables.
- Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags:
OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG.
This allowed me to remove some wrong warnings about:
"Some non-transactional changed tables couldn't be rolled back"
- Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset
(thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose
some warnings about
"Some non-transactional changed tables couldn't be rolled back")
- Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table()
which could cause delete_table to report random failures.
- Fixed core dumps for some tests when running with --debug
- Added missing FN_LIBCHAR in mysql_rm_tmp_tables()
(This has probably caused us to not properly remove temporary files after
crash)
- slow_logs was not properly initialized, which could maybe cause
extra/lost entries in slow log.
- If we get an duplicate row on insert, change column map to read and
write all columns while retrying the operation. This is required by
the definition of REPLACE and also ensures that fields that are only
part of UPDATE are properly handled. This fixed a bug in NDB and
REPLACE where REPLACE wrongly copied some column values from the replaced
row.
- For table handler that doesn't support NULL in keys, we would give an error
when creating a primary key with NULL fields, even after the fields has been
automaticly converted to NOT NULL.
- Creating a primary key on a SPATIAL key, would fail if field was not
declared as NOT NULL.
Cleanups:
- Removed not used condition argument to setup_tables
- Removed not needed item function reset_query_id_processor().
- Field->add_index is removed. Now this is instead maintained in
(field->flags & FIELD_IN_ADD_INDEX)
- Field->fieldnr is removed (use field->field_index instead)
- New argument to filesort() to indicate that it should return a set of
row pointers (not used columns). This allowed me to remove some references
to sql_command in filesort and should also enable us to return column
results in some cases where we couldn't before.
- Changed column bitmap handling in opt_range.cc to be aligned with TABLE
bitmap, which allowed me to use bitmap functions instead of looping over
all fields to create some needed bitmaps. (Faster and smaller code)
- Broke up found too long lines
- Moved some variable declaration at start of function for better code
readability.
- Removed some not used arguments from functions.
(setup_fields(), mysql_prepare_insert_check_table())
- setup_fields() now takes an enum instead of an int for marking columns
usage.
- For internal temporary tables, use handler::write_row(),
handler::delete_row() and handler::update_row() instead of
handler::ha_xxxx() for faster execution.
- Changed some constants to enum's and define's.
- Using separate column read and write sets allows for easier checking
of timestamp field was set by statement.
- Remove calls to free_io_cache() as this is now done automaticly in ha_reset()
- Don't build table->normalized_path as this is now identical to table->path
(after bar's fixes to convert filenames)
- Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to
do comparision with the 'convert-dbug-for-diff' tool.
Things left to do in 5.1:
- We wrongly log failed CREATE TABLE ... SELECT in some cases when using
row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result)
Mats has promised to look into this.
- Test that my fix for CREATE TABLE ... SELECT is indeed correct.
(I added several test cases for this, but in this case it's better that
someone else also tests this throughly).
Lars has promosed to do this.
2006-06-04 17:52:22 +02:00
|
|
|
if (!(file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root,
|
|
|
|
create_info->db_type)))
|
2005-07-18 13:31:02 +02:00
|
|
|
{
|
2006-01-17 08:40:00 +01:00
|
|
|
mem_alloc_error(sizeof(handler));
|
2005-07-18 13:31:02 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
|
|
|
}
|
2005-11-07 16:25:06 +01:00
|
|
|
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
2006-03-20 14:30:01 +01:00
|
|
|
partition_info *part_info= thd->work_part_info;
|
|
|
|
|
2006-01-17 08:40:00 +01:00
|
|
|
if (!part_info && create_info->db_type->partition_flags &&
|
|
|
|
(create_info->db_type->partition_flags() & HA_USE_AUTO_PARTITION))
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
Table is not defined as a partitioned table but the engine handles
|
|
|
|
all tables as partitioned. The handler will set up the partition info
|
|
|
|
object with the default settings.
|
|
|
|
*/
|
2006-03-18 15:48:21 +01:00
|
|
|
thd->work_part_info= part_info= new partition_info();
|
2006-01-17 08:40:00 +01:00
|
|
|
if (!part_info)
|
|
|
|
{
|
|
|
|
mem_alloc_error(sizeof(partition_info));
|
|
|
|
DBUG_RETURN(TRUE);
|
|
|
|
}
|
|
|
|
file->set_auto_partitions(part_info);
|
2006-03-20 20:36:21 +01:00
|
|
|
part_info->default_engine_type= create_info->db_type;
|
2006-05-10 18:53:40 +02:00
|
|
|
part_info->is_auto_partitioned= TRUE;
|
2006-01-17 08:40:00 +01:00
|
|
|
}
|
2005-07-18 13:31:02 +02:00
|
|
|
if (part_info)
|
|
|
|
{
|
|
|
|
/*
|
2006-01-17 08:40:00 +01:00
|
|
|
The table has been specified as a partitioned table.
|
|
|
|
If this is part of an ALTER TABLE the handler will be the partition
|
|
|
|
handler but we need to specify the default handler to use for
|
|
|
|
partitions also in the call to check_partition_info. We transport
|
|
|
|
this information in the default_db_type variable, it is either
|
|
|
|
DB_TYPE_DEFAULT or the engine set in the ALTER TABLE command.
|
|
|
|
|
|
|
|
Check that we don't use foreign keys in the table since it won't
|
|
|
|
work even with InnoDB beneath it.
|
2005-07-18 13:31:02 +02:00
|
|
|
*/
|
2006-01-17 08:40:00 +01:00
|
|
|
List_iterator<Key> key_iterator(keys);
|
|
|
|
Key *key;
|
2005-12-21 19:18:40 +01:00
|
|
|
handlerton *part_engine_type= create_info->db_type;
|
2005-07-18 13:31:02 +02:00
|
|
|
char *part_syntax_buf;
|
|
|
|
uint syntax_len;
|
2006-01-17 08:40:00 +01:00
|
|
|
handlerton *engine_type;
|
2006-03-08 18:48:40 +01:00
|
|
|
if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
|
|
|
|
{
|
|
|
|
my_error(ER_PARTITION_NO_TEMPORARY, MYF(0));
|
|
|
|
goto err;
|
|
|
|
}
|
2006-01-17 08:40:00 +01:00
|
|
|
while ((key= key_iterator++))
|
|
|
|
{
|
2006-06-01 14:51:58 +02:00
|
|
|
if (key->type == Key::FOREIGN_KEY &&
|
|
|
|
!part_info->is_auto_partitioned)
|
2006-01-17 08:40:00 +01:00
|
|
|
{
|
|
|
|
my_error(ER_CANNOT_ADD_FOREIGN, MYF(0));
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
2006-03-20 14:30:01 +01:00
|
|
|
if ((part_engine_type == &partition_hton) &&
|
|
|
|
part_info->default_engine_type)
|
2005-07-18 13:31:02 +02:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
This only happens at ALTER TABLE.
|
|
|
|
default_engine_type was assigned from the engine set in the ALTER
|
|
|
|
TABLE command.
|
|
|
|
*/
|
2006-01-17 08:40:00 +01:00
|
|
|
;
|
2005-07-18 13:31:02 +02:00
|
|
|
}
|
2005-09-20 16:29:59 +02:00
|
|
|
else
|
|
|
|
{
|
2006-01-17 08:40:00 +01:00
|
|
|
if (create_info->used_fields & HA_CREATE_USED_ENGINE)
|
|
|
|
{
|
|
|
|
part_info->default_engine_type= create_info->db_type;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (part_info->default_engine_type == NULL)
|
|
|
|
{
|
|
|
|
part_info->default_engine_type= ha_checktype(thd,
|
|
|
|
DB_TYPE_DEFAULT, 0, 0);
|
|
|
|
}
|
|
|
|
}
|
2005-09-20 16:29:59 +02:00
|
|
|
}
|
2006-01-17 08:40:00 +01:00
|
|
|
DBUG_PRINT("info", ("db_type = %d",
|
|
|
|
ha_legacy_type(part_info->default_engine_type)));
|
2006-06-27 22:19:27 +02:00
|
|
|
if (part_info->check_partition_info(thd, &engine_type, file, create_info))
|
2005-11-05 00:32:55 +01:00
|
|
|
goto err;
|
2006-01-17 08:40:00 +01:00
|
|
|
part_info->default_engine_type= engine_type;
|
2005-11-05 00:32:55 +01:00
|
|
|
|
2005-07-18 13:31:02 +02:00
|
|
|
/*
|
|
|
|
We reverse the partitioning parser and generate a standard format
|
|
|
|
for syntax stored in frm file.
|
|
|
|
*/
|
|
|
|
if (!(part_syntax_buf= generate_partition_syntax(part_info,
|
|
|
|
&syntax_len,
|
2006-06-12 20:42:07 +02:00
|
|
|
TRUE, TRUE)))
|
2005-11-05 00:32:55 +01:00
|
|
|
goto err;
|
2005-07-18 13:31:02 +02:00
|
|
|
part_info->part_info_string= part_syntax_buf;
|
|
|
|
part_info->part_info_len= syntax_len;
|
2006-01-17 08:40:00 +01:00
|
|
|
if ((!(engine_type->partition_flags &&
|
|
|
|
engine_type->partition_flags() & HA_CAN_PARTITION)) ||
|
2005-12-21 19:18:40 +01:00
|
|
|
create_info->db_type == &partition_hton)
|
2005-07-18 13:31:02 +02:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
The handler assigned to the table cannot handle partitioning.
|
|
|
|
Assign the partition handler as the handler of the table.
|
|
|
|
*/
|
2006-01-17 08:40:00 +01:00
|
|
|
DBUG_PRINT("info", ("db_type: %d",
|
|
|
|
ha_legacy_type(create_info->db_type)));
|
2005-07-18 13:31:02 +02:00
|
|
|
delete file;
|
2005-12-21 19:18:40 +01:00
|
|
|
create_info->db_type= &partition_hton;
|
2005-07-18 13:31:02 +02:00
|
|
|
if (!(file= get_ha_partition(part_info)))
|
|
|
|
{
|
|
|
|
DBUG_RETURN(TRUE);
|
|
|
|
}
|
2006-03-23 22:00:58 +01:00
|
|
|
/*
|
|
|
|
If we have default number of partitions or subpartitions we
|
|
|
|
might require to set-up the part_info object such that it
|
|
|
|
creates a proper .par file. The current part_info object is
|
|
|
|
only used to create the frm-file and .par-file.
|
|
|
|
*/
|
|
|
|
if (part_info->use_default_no_partitions &&
|
|
|
|
part_info->no_parts &&
|
2006-06-27 22:19:27 +02:00
|
|
|
(int)part_info->no_parts !=
|
|
|
|
file->get_default_no_partitions(create_info))
|
2006-03-23 22:00:58 +01:00
|
|
|
{
|
2006-03-25 00:21:43 +01:00
|
|
|
uint i;
|
2006-03-23 22:00:58 +01:00
|
|
|
List_iterator<partition_element> part_it(part_info->partitions);
|
2006-03-25 00:21:43 +01:00
|
|
|
part_it++;
|
|
|
|
DBUG_ASSERT(thd->lex->sql_command != SQLCOM_CREATE_TABLE);
|
|
|
|
for (i= 1; i < part_info->partitions.elements; i++)
|
|
|
|
(part_it++)->part_state= PART_TO_BE_DROPPED;
|
2006-03-23 22:00:58 +01:00
|
|
|
}
|
|
|
|
else if (part_info->is_sub_partitioned() &&
|
|
|
|
part_info->use_default_no_subpartitions &&
|
|
|
|
part_info->no_subparts &&
|
2006-03-25 00:21:43 +01:00
|
|
|
(int)part_info->no_subparts !=
|
2006-06-27 22:19:27 +02:00
|
|
|
file->get_default_no_partitions(create_info))
|
2006-03-23 22:00:58 +01:00
|
|
|
{
|
2006-03-25 00:21:43 +01:00
|
|
|
DBUG_ASSERT(thd->lex->sql_command != SQLCOM_CREATE_TABLE);
|
2006-06-27 22:19:27 +02:00
|
|
|
part_info->no_subparts= file->get_default_no_partitions(create_info);
|
2006-03-23 22:00:58 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (create_info->db_type != engine_type)
|
|
|
|
{
|
2006-05-09 03:41:10 +02:00
|
|
|
/*
|
|
|
|
We come here when we don't use a partitioned handler.
|
|
|
|
Since we use a partitioned table it must be "native partitioned".
|
|
|
|
We have switched engine from defaults, most likely only specified
|
|
|
|
engines in partition clauses.
|
|
|
|
*/
|
2006-03-23 22:00:58 +01:00
|
|
|
delete file;
|
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes
Changes that requires code changes in other code of other storage engines.
(Note that all changes are very straightforward and one should find all issues
by compiling a --debug build and fixing all compiler errors and all
asserts in field.cc while running the test suite),
- New optional handler function introduced: reset()
This is called after every DML statement to make it easy for a handler to
statement specific cleanups.
(The only case it's not called is if force the file to be closed)
- handler::extra(HA_EXTRA_RESET) is removed. Code that was there before
should be moved to handler::reset()
- table->read_set contains a bitmap over all columns that are needed
in the query. read_row() and similar functions only needs to read these
columns
- table->write_set contains a bitmap over all columns that will be updated
in the query. write_row() and update_row() only needs to update these
columns.
The above bitmaps should now be up to date in all context
(including ALTER TABLE, filesort()).
The handler is informed of any changes to the bitmap after
fix_fields() by calling the virtual function
handler::column_bitmaps_signal(). If the handler does caching of
these bitmaps (instead of using table->read_set, table->write_set),
it should redo the caching in this code. as the signal() may be sent
several times, it's probably best to set a variable in the signal
and redo the caching on read_row() / write_row() if the variable was
set.
- Removed the read_set and write_set bitmap objects from the handler class
- Removed all column bit handling functions from the handler class.
(Now one instead uses the normal bitmap functions in my_bitmap.c instead
of handler dedicated bitmap functions)
- field->query_id is removed. One should instead instead check
table->read_set and table->write_set if a field is used in the query.
- handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and
handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now
instead use table->read_set to check for which columns to retrieve.
- If a handler needs to call Field->val() or Field->store() on columns
that are not used in the query, one should install a temporary
all-columns-used map while doing so. For this, we provide the following
functions:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
field->val();
dbug_tmp_restore_column_map(table->read_set, old_map);
and similar for the write map:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
field->val();
dbug_tmp_restore_column_map(table->write_set, old_map);
If this is not done, you will sooner or later hit a DBUG_ASSERT
in the field store() / val() functions.
(For not DBUG binaries, the dbug_tmp_restore_column_map() and
dbug_tmp_restore_column_map() are inline dummy functions and should
be optimized away be the compiler).
- If one needs to temporary set the column map for all binaries (and not
just to avoid the DBUG_ASSERT() in the Field::store() / Field::val()
methods) one should use the functions tmp_use_all_columns() and
tmp_restore_column_map() instead of the above dbug_ variants.
- All 'status' fields in the handler base class (like records,
data_file_length etc) are now stored in a 'stats' struct. This makes
it easier to know what status variables are provided by the base
handler. This requires some trivial variable names in the extra()
function.
- New virtual function handler::records(). This is called to optimize
COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true.
(stats.records is not supposed to be an exact value. It's only has to
be 'reasonable enough' for the optimizer to be able to choose a good
optimization path).
- Non virtual handler::init() function added for caching of virtual
constants from engine.
- Removed has_transactions() virtual method. Now one should instead return
HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support
transactions.
- The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument
that is to be used with 'new handler_name()' to allocate the handler
in the right area. The xxxx_create_handler() function is also
responsible for any initialization of the object before returning.
For example, one should change:
static handler *myisam_create_handler(TABLE_SHARE *table)
{
return new ha_myisam(table);
}
->
static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root)
{
return new (mem_root) ha_myisam(table);
}
- New optional virtual function: use_hidden_primary_key().
This is called in case of an update/delete when
(table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
but we don't have a primary key. This allows the handler to take precisions
in remembering any hidden primary key to able to update/delete any
found row. The default handler marks all columns to be read.
- handler::table_flags() now returns a ulonglong (to allow for more flags).
- New/changed table_flags()
- HA_HAS_RECORDS Set if ::records() is supported
- HA_NO_TRANSACTIONS Set if engine doesn't support transactions
- HA_PRIMARY_KEY_REQUIRED_FOR_DELETE
Set if we should mark all primary key columns for
read when reading rows as part of a DELETE
statement. If there is no primary key,
all columns are marked for read.
- HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some
cases (based on table->read_set)
- HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS
Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION.
- HA_DUPP_POS Renamed to HA_DUPLICATE_POS
- HA_REQUIRES_KEY_COLUMNS_FOR_DELETE
Set this if we should mark ALL key columns for
read when when reading rows as part of a DELETE
statement. In case of an update we will mark
all keys for read for which key part changed
value.
- HA_STATS_RECORDS_IS_EXACT
Set this if stats.records is exact.
(This saves us some extra records() calls
when optimizing COUNT(*))
- Removed table_flags()
- HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if
handler::records() gives an exact count() and
HA_STATS_RECORDS_IS_EXACT if stats.records is exact.
- HA_READ_RND_SAME Removed (no one supported this one)
- Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk()
- Renamed handler::dupp_pos to handler::dup_pos
- Removed not used variable handler::sortkey
Upper level handler changes:
- ha_reset() now does some overall checks and calls ::reset()
- ha_table_flags() added. This is a cached version of table_flags(). The
cache is updated on engine creation time and updated on open.
MySQL level changes (not obvious from the above):
- DBUG_ASSERT() added to check that column usage matches what is set
in the column usage bit maps. (This found a LOT of bugs in current
column marking code).
- In 5.1 before, all used columns was marked in read_set and only updated
columns was marked in write_set. Now we only mark columns for which we
need a value in read_set.
- Column bitmaps are created in open_binary_frm() and open_table_from_share().
(Before this was in table.cc)
- handler::table_flags() calls are replaced with handler::ha_table_flags()
- For calling field->val() you must have the corresponding bit set in
table->read_set. For calling field->store() you must have the
corresponding bit set in table->write_set. (There are asserts in
all store()/val() functions to catch wrong usage)
- thd->set_query_id is renamed to thd->mark_used_columns and instead
of setting this to an integer value, this has now the values:
MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE
Changed also all variables named 'set_query_id' to mark_used_columns.
- In filesort() we now inform the handler of exactly which columns are needed
doing the sort and choosing the rows.
- The TABLE_SHARE object has a 'all_set' column bitmap one can use
when one needs a column bitmap with all columns set.
(This is used for table->use_all_columns() and other places)
- The TABLE object has 3 column bitmaps:
- def_read_set Default bitmap for columns to be read
- def_write_set Default bitmap for columns to be written
- tmp_set Can be used as a temporary bitmap when needed.
The table object has also two pointer to bitmaps read_set and write_set
that the handler should use to find out which columns are used in which way.
- count() optimization now calls handler::records() instead of using
handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true).
- Added extra argument to Item::walk() to indicate if we should also
traverse sub queries.
- Added TABLE parameter to cp_buffer_from_ref()
- Don't close tables created with CREATE ... SELECT but keep them in
the table cache. (Faster usage of newly created tables).
New interfaces:
- table->clear_column_bitmaps() to initialize the bitmaps for tables
at start of new statements.
- table->column_bitmaps_set() to set up new column bitmaps and signal
the handler about this.
- table->column_bitmaps_set_no_signal() for some few cases where we need
to setup new column bitmaps but don't signal the handler (as the handler
has already been signaled about these before). Used for the momement
only in opt_range.cc when doing ROR scans.
- table->use_all_columns() to install a bitmap where all columns are marked
as use in the read and the write set.
- table->default_column_bitmaps() to install the normal read and write
column bitmaps, but not signaling the handler about this.
This is mainly used when creating TABLE instances.
- table->mark_columns_needed_for_delete(),
table->mark_columns_needed_for_delete() and
table->mark_columns_needed_for_insert() to allow us to put additional
columns in column usage maps if handler so requires.
(The handler indicates what it neads in handler->table_flags())
- table->prepare_for_position() to allow us to tell handler that it
needs to read primary key parts to be able to store them in
future table->position() calls.
(This replaces the table->file->ha_retrieve_all_pk function)
- table->mark_auto_increment_column() to tell handler are going to update
columns part of any auto_increment key.
- table->mark_columns_used_by_index() to mark all columns that is part of
an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow
it to quickly know that it only needs to read colums that are part
of the key. (The handler can also use the column map for detecting this,
but simpler/faster handler can just monitor the extra() call).
- table->mark_columns_used_by_index_no_reset() to in addition to other columns,
also mark all columns that is used by the given key.
- table->restore_column_maps_after_mark_index() to restore to default
column maps after a call to table->mark_columns_used_by_index().
- New item function register_field_in_read_map(), for marking used columns
in table->read_map. Used by filesort() to mark all used columns
- Maintain in TABLE->merge_keys set of all keys that are used in query.
(Simplices some optimization loops)
- Maintain Field->part_of_key_not_clustered which is like Field->part_of_key
but the field in the clustered key is not assumed to be part of all index.
(used in opt_range.cc for faster loops)
- dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map()
tmp_use_all_columns() and tmp_restore_column_map() functions to temporally
mark all columns as usable. The 'dbug_' version is primarily intended
inside a handler when it wants to just call Field:store() & Field::val()
functions, but don't need the column maps set for any other usage.
(ie:: bitmap_is_set() is never called)
- We can't use compare_records() to skip updates for handlers that returns
a partial column set and the read_set doesn't cover all columns in the
write set. The reason for this is that if we have a column marked only for
write we can't in the MySQL level know if the value changed or not.
The reason this worked before was that MySQL marked all to be written
columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden
bug'.
- open_table_from_share() does not anymore setup temporary MEM_ROOT
object as a thread specific variable for the handler. Instead we
send the to-be-used MEMROOT to get_new_handler().
(Simpler, faster code)
Bugs fixed:
- Column marking was not done correctly in a lot of cases.
(ALTER TABLE, when using triggers, auto_increment fields etc)
(Could potentially result in wrong values inserted in table handlers
relying on that the old column maps or field->set_query_id was correct)
Especially when it comes to triggers, there may be cases where the
old code would cause lost/wrong values for NDB and/or InnoDB tables.
- Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags:
OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG.
This allowed me to remove some wrong warnings about:
"Some non-transactional changed tables couldn't be rolled back"
- Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset
(thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose
some warnings about
"Some non-transactional changed tables couldn't be rolled back")
- Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table()
which could cause delete_table to report random failures.
- Fixed core dumps for some tests when running with --debug
- Added missing FN_LIBCHAR in mysql_rm_tmp_tables()
(This has probably caused us to not properly remove temporary files after
crash)
- slow_logs was not properly initialized, which could maybe cause
extra/lost entries in slow log.
- If we get an duplicate row on insert, change column map to read and
write all columns while retrying the operation. This is required by
the definition of REPLACE and also ensures that fields that are only
part of UPDATE are properly handled. This fixed a bug in NDB and
REPLACE where REPLACE wrongly copied some column values from the replaced
row.
- For table handler that doesn't support NULL in keys, we would give an error
when creating a primary key with NULL fields, even after the fields has been
automaticly converted to NOT NULL.
- Creating a primary key on a SPATIAL key, would fail if field was not
declared as NOT NULL.
Cleanups:
- Removed not used condition argument to setup_tables
- Removed not needed item function reset_query_id_processor().
- Field->add_index is removed. Now this is instead maintained in
(field->flags & FIELD_IN_ADD_INDEX)
- Field->fieldnr is removed (use field->field_index instead)
- New argument to filesort() to indicate that it should return a set of
row pointers (not used columns). This allowed me to remove some references
to sql_command in filesort and should also enable us to return column
results in some cases where we couldn't before.
- Changed column bitmap handling in opt_range.cc to be aligned with TABLE
bitmap, which allowed me to use bitmap functions instead of looping over
all fields to create some needed bitmaps. (Faster and smaller code)
- Broke up found too long lines
- Moved some variable declaration at start of function for better code
readability.
- Removed some not used arguments from functions.
(setup_fields(), mysql_prepare_insert_check_table())
- setup_fields() now takes an enum instead of an int for marking columns
usage.
- For internal temporary tables, use handler::write_row(),
handler::delete_row() and handler::update_row() instead of
handler::ha_xxxx() for faster execution.
- Changed some constants to enum's and define's.
- Using separate column read and write sets allows for easier checking
of timestamp field was set by statement.
- Remove calls to free_io_cache() as this is now done automaticly in ha_reset()
- Don't build table->normalized_path as this is now identical to table->path
(after bar's fixes to convert filenames)
- Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to
do comparision with the 'convert-dbug-for-diff' tool.
Things left to do in 5.1:
- We wrongly log failed CREATE TABLE ... SELECT in some cases when using
row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result)
Mats has promised to look into this.
- Test that my fix for CREATE TABLE ... SELECT is indeed correct.
(I added several test cases for this, but in this case it's better that
someone else also tests this throughly).
Lars has promosed to do this.
2006-06-04 17:52:22 +02:00
|
|
|
if (!(file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root,
|
|
|
|
engine_type)))
|
2006-03-23 22:00:58 +01:00
|
|
|
{
|
|
|
|
mem_alloc_error(sizeof(handler));
|
|
|
|
DBUG_RETURN(TRUE);
|
|
|
|
}
|
2005-07-18 13:31:02 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
2004-03-30 19:22:14 +02:00
|
|
|
|
2005-07-22 22:43:59 +02:00
|
|
|
set_table_default_charset(thd, create_info, (char*) db);
|
2004-07-08 12:03:01 +02:00
|
|
|
|
2005-04-01 14:04:50 +02:00
|
|
|
if (mysql_prepare_table(thd, create_info, &fields,
|
|
|
|
&keys, internal_tmp_table, &db_options, file,
|
|
|
|
&key_info_buffer, &key_count,
|
2004-04-08 16:56:45 +02:00
|
|
|
select_field_count))
|
2005-07-18 13:31:02 +02:00
|
|
|
goto err;
|
2000-07-31 21:29:14 +02:00
|
|
|
|
|
|
|
/* Check if table exists */
|
|
|
|
if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
|
|
|
|
{
|
2006-01-23 13:28:42 +01:00
|
|
|
path_length= build_tmptable_filename(thd, path, sizeof(path));
|
2004-05-25 00:30:09 +02:00
|
|
|
if (lower_case_table_names)
|
2005-06-21 20:25:51 +02:00
|
|
|
my_casedn_str(files_charset_info, path);
|
2000-07-31 21:29:14 +02:00
|
|
|
create_info->table_options|=HA_CREATE_DELAY_KEY_WRITE;
|
|
|
|
}
|
2006-06-23 18:15:39 +02:00
|
|
|
else
|
|
|
|
{
|
2006-07-03 17:35:58 +02:00
|
|
|
#ifdef FN_DEVCHAR
|
|
|
|
/* check if the table name contains FN_DEVCHAR when defined */
|
|
|
|
const char *start= alias;
|
|
|
|
while (*start != '\0')
|
|
|
|
{
|
|
|
|
if (*start == FN_DEVCHAR)
|
|
|
|
{
|
|
|
|
my_error(ER_WRONG_TABLE_NAME, MYF(0), alias);
|
|
|
|
DBUG_RETURN(TRUE);
|
|
|
|
}
|
|
|
|
start++;
|
|
|
|
}
|
|
|
|
#endif
|
2005-12-31 06:01:26 +01:00
|
|
|
path_length= build_table_filename(path, sizeof(path), db, alias, reg_ext);
|
2006-06-23 18:15:39 +02:00
|
|
|
}
|
2005-05-26 05:26:40 +02:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
/* Check if table already exists */
|
2005-11-23 21:45:02 +01:00
|
|
|
if ((create_info->options & HA_LEX_CREATE_TMP_TABLE) &&
|
|
|
|
find_temporary_table(thd, db, table_name))
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2000-10-16 23:47:15 +02:00
|
|
|
if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
|
2003-10-15 20:41:13 +02:00
|
|
|
{
|
|
|
|
create_info->table_existed= 1; // Mark that table existed
|
2005-09-12 14:09:19 +02:00
|
|
|
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
|
|
|
|
ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR),
|
|
|
|
alias);
|
2005-11-05 00:32:55 +01:00
|
|
|
error= 0;
|
|
|
|
goto err;
|
2003-10-15 20:41:13 +02:00
|
|
|
}
|
2004-02-11 00:06:46 +01:00
|
|
|
my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alias);
|
2005-07-18 13:31:02 +02:00
|
|
|
goto err;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2006-07-04 10:02:11 +02:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
VOID(pthread_mutex_lock(&LOCK_open));
|
2005-01-27 22:38:56 +01:00
|
|
|
if (!internal_tmp_table && !(create_info->options & HA_LEX_CREATE_TMP_TABLE))
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
|
|
|
if (!access(path,F_OK))
|
|
|
|
{
|
|
|
|
if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
|
2005-09-12 14:09:19 +02:00
|
|
|
goto warn;
|
|
|
|
my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
|
2005-11-05 00:32:55 +01:00
|
|
|
goto unlock_and_end;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2005-11-23 21:45:02 +01:00
|
|
|
DBUG_ASSERT(get_cached_table_share(db, alias) == 0);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
2004-04-15 09:14:14 +02:00
|
|
|
/*
|
|
|
|
Check that table with given name does not already
|
|
|
|
exist in any storage engine. In such a case it should
|
|
|
|
be discovered and the error ER_TABLE_EXISTS_ERROR be returned
|
|
|
|
unless user specified CREATE TABLE IF EXISTS
|
|
|
|
The LOCK_open mutex has been locked to make sure no
|
|
|
|
one else is attempting to discover the table. Since
|
|
|
|
it's not on disk as a frm file, no one could be using it!
|
|
|
|
*/
|
|
|
|
if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE))
|
|
|
|
{
|
|
|
|
bool create_if_not_exists =
|
|
|
|
create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS;
|
2005-06-08 13:31:59 +02:00
|
|
|
if (ha_table_exists_in_engine(thd, db, table_name))
|
2004-04-15 09:14:14 +02:00
|
|
|
{
|
2005-06-08 13:31:59 +02:00
|
|
|
DBUG_PRINT("info", ("Table with same name already existed in handler"));
|
2004-04-15 09:14:14 +02:00
|
|
|
|
|
|
|
if (create_if_not_exists)
|
2005-09-12 14:09:19 +02:00
|
|
|
goto warn;
|
|
|
|
my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
|
2005-11-05 00:32:55 +01:00
|
|
|
goto unlock_and_end;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
thd->proc_info="creating table";
|
2003-10-15 20:41:13 +02:00
|
|
|
create_info->table_existed= 0; // Mark that table is created
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2003-09-11 18:06:23 +02:00
|
|
|
if (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE)
|
2003-08-23 11:25:39 +02:00
|
|
|
create_info->data_file_name= create_info->index_file_name= 0;
|
2000-07-31 21:29:14 +02:00
|
|
|
create_info->table_options=db_options;
|
2003-08-23 11:25:39 +02:00
|
|
|
|
2005-12-31 06:01:26 +01:00
|
|
|
path[path_length - reg_ext_length]= '\0'; // Remove .frm extension
|
2005-11-23 21:45:02 +01:00
|
|
|
if (rea_create_table(thd, path, db, table_name, create_info, fields,
|
|
|
|
key_count, key_info_buffer, file))
|
2005-11-05 00:32:55 +01:00
|
|
|
goto unlock_and_end;
|
2005-11-23 21:45:02 +01:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
|
|
|
|
{
|
|
|
|
/* Open table and put in temporary table list */
|
|
|
|
if (!(open_temporary_table(thd, path, db, table_name, 1)))
|
|
|
|
{
|
|
|
|
(void) rm_temporary_table(create_info->db_type, path);
|
2005-11-05 00:32:55 +01:00
|
|
|
goto unlock_and_end;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2003-02-10 16:59:16 +01:00
|
|
|
thd->tmp_table_used= 1;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2004-12-06 10:38:56 +01:00
|
|
|
|
2005-12-22 06:39:02 +01:00
|
|
|
/*
|
|
|
|
Don't write statement if:
|
|
|
|
- It is an internal temporary table,
|
|
|
|
- Row-based logging is used and it we are creating a temporary table, or
|
|
|
|
- The binary log is not open.
|
2005-12-22 11:09:50 +01:00
|
|
|
Otherwise, the statement shall be binlogged.
|
2005-12-22 06:39:02 +01:00
|
|
|
*/
|
|
|
|
if (!internal_tmp_table &&
|
WL#2977 and WL#2712 global and session-level variable to set the binlog format (row/statement),
and new binlog format called "mixed" (which is statement-based except if only row-based is correct,
in this cset it means if UDF or UUID is used; more cases could be added in later 5.1 release):
SET GLOBAL|SESSION BINLOG_FORMAT=row|statement|mixed|default;
the global default is statement unless cluster is enabled (then it's row) as in 5.1-alpha.
It's not possible to use SET on this variable if a session is currently in row-based mode and has open temporary tables (because CREATE
TEMPORARY TABLE was not binlogged so temp table is not known on slave), or if NDB is enabled (because
NDB does not support such change on-the-fly, though it will later), of if in a stored function (see below).
The added tests test the possibility or impossibility to SET, their effects, and the mixed mode,
including in prepared statements and in stored procedures and functions.
Caveats:
a) The mixed mode will not work for stored functions: in mixed mode, a stored function will
always be binlogged as one call and in a statement-based way (e.g. INSERT VALUES(myfunc()) or SELECT myfunc()).
b) for the same reason, changing the thread's binlog format inside a stored function is
refused with an error message.
c) the same problems apply to triggers; implementing b) for triggers will be done later (will ask
Dmitri).
Additionally, as the binlog format is now changeable by each user for his session, I remove the implication
which was done at startup, where row-based automatically set log-bin-trust-routine-creators to 1
(not possible anymore as a user can now switch to stmt-based and do nasty things again), and automatically
set --innodb-locks-unsafe-for-binlog to 1 (was anyway theoretically incorrect as it disabled
phantom protection).
Plus fixes for compiler warnings.
2006-02-25 22:21:03 +01:00
|
|
|
(!thd->current_stmt_binlog_row_based ||
|
|
|
|
(thd->current_stmt_binlog_row_based &&
|
2005-12-22 11:09:50 +01:00
|
|
|
!(create_info->options & HA_LEX_CREATE_TMP_TABLE))))
|
2005-12-22 06:39:02 +01:00
|
|
|
write_bin_log(thd, TRUE, thd->query, thd->query_length);
|
2004-10-20 03:04:37 +02:00
|
|
|
error= FALSE;
|
2005-11-05 00:32:55 +01:00
|
|
|
unlock_and_end:
|
2000-07-31 21:29:14 +02:00
|
|
|
VOID(pthread_mutex_unlock(&LOCK_open));
|
2005-11-05 00:32:55 +01:00
|
|
|
|
|
|
|
err:
|
2000-07-31 21:29:14 +02:00
|
|
|
thd->proc_info="After create";
|
2005-11-05 00:32:55 +01:00
|
|
|
delete file;
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(error);
|
2005-09-12 14:09:19 +02:00
|
|
|
|
|
|
|
warn:
|
2005-10-07 23:57:40 +02:00
|
|
|
error= FALSE;
|
2005-09-12 14:09:19 +02:00
|
|
|
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
|
|
|
|
ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR),
|
|
|
|
alias);
|
|
|
|
create_info->table_existed= 1; // Mark that table existed
|
2005-11-05 00:32:55 +01:00
|
|
|
goto unlock_and_end;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
2006-02-13 08:49:28 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
Database locking aware wrapper for mysql_create_table_internal(),
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool mysql_create_table(THD *thd, const char *db, const char *table_name,
|
|
|
|
HA_CREATE_INFO *create_info,
|
|
|
|
List<create_field> &fields,
|
|
|
|
List<Key> &keys,bool internal_tmp_table,
|
2006-06-21 16:57:30 +02:00
|
|
|
uint select_field_count,
|
|
|
|
bool use_copy_create_info)
|
2006-02-13 08:49:28 +01:00
|
|
|
{
|
|
|
|
bool result;
|
|
|
|
DBUG_ENTER("mysql_create_table");
|
|
|
|
|
|
|
|
/* Wait for any database locks */
|
|
|
|
pthread_mutex_lock(&LOCK_lock_db);
|
|
|
|
while (!thd->killed &&
|
|
|
|
hash_search(&lock_db_cache,(byte*) db, strlen(db)))
|
|
|
|
{
|
|
|
|
wait_for_condition(thd, &LOCK_lock_db, &COND_refresh);
|
|
|
|
pthread_mutex_lock(&LOCK_lock_db);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (thd->killed)
|
|
|
|
{
|
|
|
|
pthread_mutex_unlock(&LOCK_lock_db);
|
|
|
|
DBUG_RETURN(TRUE);
|
|
|
|
}
|
|
|
|
creating_table++;
|
|
|
|
pthread_mutex_unlock(&LOCK_lock_db);
|
|
|
|
|
|
|
|
result= mysql_create_table_internal(thd, db, table_name, create_info,
|
|
|
|
fields, keys, internal_tmp_table,
|
2006-06-21 16:57:30 +02:00
|
|
|
select_field_count,
|
|
|
|
use_copy_create_info);
|
2006-02-13 08:49:28 +01:00
|
|
|
|
|
|
|
pthread_mutex_lock(&LOCK_lock_db);
|
|
|
|
if (!--creating_table && creating_database)
|
|
|
|
pthread_cond_signal(&COND_refresh);
|
|
|
|
pthread_mutex_unlock(&LOCK_lock_db);
|
|
|
|
DBUG_RETURN(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
/*
|
|
|
|
** Give the key name after the first field with an optional '_#' after
|
|
|
|
**/
|
|
|
|
|
|
|
|
static bool
|
|
|
|
check_if_keyname_exists(const char *name, KEY *start, KEY *end)
|
|
|
|
{
|
|
|
|
for (KEY *key=start ; key != end ; key++)
|
2002-03-12 18:37:58 +01:00
|
|
|
if (!my_strcasecmp(system_charset_info,name,key->name))
|
2000-07-31 21:29:14 +02:00
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static char *
|
|
|
|
make_unique_key_name(const char *field_name,KEY *start,KEY *end)
|
|
|
|
{
|
|
|
|
char buff[MAX_FIELD_NAME],*buff_end;
|
|
|
|
|
2004-03-13 22:31:30 +01:00
|
|
|
if (!check_if_keyname_exists(field_name,start,end) &&
|
|
|
|
my_strcasecmp(system_charset_info,field_name,primary_key_name))
|
2000-07-31 21:29:14 +02:00
|
|
|
return (char*) field_name; // Use fieldname
|
2004-04-08 16:56:45 +02:00
|
|
|
buff_end=strmake(buff,field_name, sizeof(buff)-4);
|
|
|
|
|
|
|
|
/*
|
|
|
|
Only 3 chars + '\0' left, so need to limit to 2 digit
|
|
|
|
This is ok as we can't have more than 100 keys anyway
|
|
|
|
*/
|
2004-04-07 13:12:05 +02:00
|
|
|
for (uint i=2 ; i< 100; i++)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-04-08 16:56:45 +02:00
|
|
|
*buff_end= '_';
|
|
|
|
int10_to_str(i, buff_end+1, 10);
|
2000-07-31 21:29:14 +02:00
|
|
|
if (!check_if_keyname_exists(buff,start,end))
|
|
|
|
return sql_strdup(buff);
|
|
|
|
}
|
2004-04-08 16:56:45 +02:00
|
|
|
return (char*) "not_specified"; // Should never happen
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
2004-04-08 16:56:45 +02:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
/****************************************************************************
|
|
|
|
** Alter a table definition
|
|
|
|
****************************************************************************/
|
|
|
|
|
2000-08-21 02:00:52 +02:00
|
|
|
bool
|
2005-12-21 19:18:40 +01:00
|
|
|
mysql_rename_table(handlerton *base,
|
2000-07-31 21:29:14 +02:00
|
|
|
const char *old_db,
|
2003-12-30 12:14:21 +01:00
|
|
|
const char *old_name,
|
2000-07-31 21:29:14 +02:00
|
|
|
const char *new_db,
|
2003-12-30 12:14:21 +01:00
|
|
|
const char *new_name)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2005-11-03 12:20:13 +01:00
|
|
|
THD *thd= current_thd;
|
2005-05-26 05:26:40 +02:00
|
|
|
char from[FN_REFLEN], to[FN_REFLEN], lc_from[FN_REFLEN], lc_to[FN_REFLEN];
|
|
|
|
char *from_base= from, *to_base= to;
|
|
|
|
char tmp_name[NAME_LEN+1];
|
2005-11-23 21:45:02 +01:00
|
|
|
handler *file;
|
2000-08-21 23:18:32 +02:00
|
|
|
int error=0;
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_ENTER("mysql_rename_table");
|
2003-12-30 12:14:21 +01:00
|
|
|
|
2005-12-21 19:18:40 +01:00
|
|
|
file= (base == NULL ? 0 :
|
2005-11-23 21:45:02 +01:00
|
|
|
get_new_handler((TABLE_SHARE*) 0, thd->mem_root, base));
|
|
|
|
|
2005-12-31 06:01:26 +01:00
|
|
|
build_table_filename(from, sizeof(from), old_db, old_name, "");
|
|
|
|
build_table_filename(to, sizeof(to), new_db, new_name, "");
|
2005-05-26 05:26:40 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
If lower_case_table_names == 2 (case-preserving but case-insensitive
|
|
|
|
file system) and the storage is not HA_FILE_BASED, we need to provide
|
|
|
|
a lowercase file name, but we leave the .frm in mixed case.
|
|
|
|
*/
|
2004-10-21 18:10:58 +02:00
|
|
|
if (lower_case_table_names == 2 && file &&
|
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes
Changes that requires code changes in other code of other storage engines.
(Note that all changes are very straightforward and one should find all issues
by compiling a --debug build and fixing all compiler errors and all
asserts in field.cc while running the test suite),
- New optional handler function introduced: reset()
This is called after every DML statement to make it easy for a handler to
statement specific cleanups.
(The only case it's not called is if force the file to be closed)
- handler::extra(HA_EXTRA_RESET) is removed. Code that was there before
should be moved to handler::reset()
- table->read_set contains a bitmap over all columns that are needed
in the query. read_row() and similar functions only needs to read these
columns
- table->write_set contains a bitmap over all columns that will be updated
in the query. write_row() and update_row() only needs to update these
columns.
The above bitmaps should now be up to date in all context
(including ALTER TABLE, filesort()).
The handler is informed of any changes to the bitmap after
fix_fields() by calling the virtual function
handler::column_bitmaps_signal(). If the handler does caching of
these bitmaps (instead of using table->read_set, table->write_set),
it should redo the caching in this code. as the signal() may be sent
several times, it's probably best to set a variable in the signal
and redo the caching on read_row() / write_row() if the variable was
set.
- Removed the read_set and write_set bitmap objects from the handler class
- Removed all column bit handling functions from the handler class.
(Now one instead uses the normal bitmap functions in my_bitmap.c instead
of handler dedicated bitmap functions)
- field->query_id is removed. One should instead instead check
table->read_set and table->write_set if a field is used in the query.
- handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and
handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now
instead use table->read_set to check for which columns to retrieve.
- If a handler needs to call Field->val() or Field->store() on columns
that are not used in the query, one should install a temporary
all-columns-used map while doing so. For this, we provide the following
functions:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
field->val();
dbug_tmp_restore_column_map(table->read_set, old_map);
and similar for the write map:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
field->val();
dbug_tmp_restore_column_map(table->write_set, old_map);
If this is not done, you will sooner or later hit a DBUG_ASSERT
in the field store() / val() functions.
(For not DBUG binaries, the dbug_tmp_restore_column_map() and
dbug_tmp_restore_column_map() are inline dummy functions and should
be optimized away be the compiler).
- If one needs to temporary set the column map for all binaries (and not
just to avoid the DBUG_ASSERT() in the Field::store() / Field::val()
methods) one should use the functions tmp_use_all_columns() and
tmp_restore_column_map() instead of the above dbug_ variants.
- All 'status' fields in the handler base class (like records,
data_file_length etc) are now stored in a 'stats' struct. This makes
it easier to know what status variables are provided by the base
handler. This requires some trivial variable names in the extra()
function.
- New virtual function handler::records(). This is called to optimize
COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true.
(stats.records is not supposed to be an exact value. It's only has to
be 'reasonable enough' for the optimizer to be able to choose a good
optimization path).
- Non virtual handler::init() function added for caching of virtual
constants from engine.
- Removed has_transactions() virtual method. Now one should instead return
HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support
transactions.
- The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument
that is to be used with 'new handler_name()' to allocate the handler
in the right area. The xxxx_create_handler() function is also
responsible for any initialization of the object before returning.
For example, one should change:
static handler *myisam_create_handler(TABLE_SHARE *table)
{
return new ha_myisam(table);
}
->
static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root)
{
return new (mem_root) ha_myisam(table);
}
- New optional virtual function: use_hidden_primary_key().
This is called in case of an update/delete when
(table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
but we don't have a primary key. This allows the handler to take precisions
in remembering any hidden primary key to able to update/delete any
found row. The default handler marks all columns to be read.
- handler::table_flags() now returns a ulonglong (to allow for more flags).
- New/changed table_flags()
- HA_HAS_RECORDS Set if ::records() is supported
- HA_NO_TRANSACTIONS Set if engine doesn't support transactions
- HA_PRIMARY_KEY_REQUIRED_FOR_DELETE
Set if we should mark all primary key columns for
read when reading rows as part of a DELETE
statement. If there is no primary key,
all columns are marked for read.
- HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some
cases (based on table->read_set)
- HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS
Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION.
- HA_DUPP_POS Renamed to HA_DUPLICATE_POS
- HA_REQUIRES_KEY_COLUMNS_FOR_DELETE
Set this if we should mark ALL key columns for
read when when reading rows as part of a DELETE
statement. In case of an update we will mark
all keys for read for which key part changed
value.
- HA_STATS_RECORDS_IS_EXACT
Set this if stats.records is exact.
(This saves us some extra records() calls
when optimizing COUNT(*))
- Removed table_flags()
- HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if
handler::records() gives an exact count() and
HA_STATS_RECORDS_IS_EXACT if stats.records is exact.
- HA_READ_RND_SAME Removed (no one supported this one)
- Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk()
- Renamed handler::dupp_pos to handler::dup_pos
- Removed not used variable handler::sortkey
Upper level handler changes:
- ha_reset() now does some overall checks and calls ::reset()
- ha_table_flags() added. This is a cached version of table_flags(). The
cache is updated on engine creation time and updated on open.
MySQL level changes (not obvious from the above):
- DBUG_ASSERT() added to check that column usage matches what is set
in the column usage bit maps. (This found a LOT of bugs in current
column marking code).
- In 5.1 before, all used columns was marked in read_set and only updated
columns was marked in write_set. Now we only mark columns for which we
need a value in read_set.
- Column bitmaps are created in open_binary_frm() and open_table_from_share().
(Before this was in table.cc)
- handler::table_flags() calls are replaced with handler::ha_table_flags()
- For calling field->val() you must have the corresponding bit set in
table->read_set. For calling field->store() you must have the
corresponding bit set in table->write_set. (There are asserts in
all store()/val() functions to catch wrong usage)
- thd->set_query_id is renamed to thd->mark_used_columns and instead
of setting this to an integer value, this has now the values:
MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE
Changed also all variables named 'set_query_id' to mark_used_columns.
- In filesort() we now inform the handler of exactly which columns are needed
doing the sort and choosing the rows.
- The TABLE_SHARE object has a 'all_set' column bitmap one can use
when one needs a column bitmap with all columns set.
(This is used for table->use_all_columns() and other places)
- The TABLE object has 3 column bitmaps:
- def_read_set Default bitmap for columns to be read
- def_write_set Default bitmap for columns to be written
- tmp_set Can be used as a temporary bitmap when needed.
The table object has also two pointer to bitmaps read_set and write_set
that the handler should use to find out which columns are used in which way.
- count() optimization now calls handler::records() instead of using
handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true).
- Added extra argument to Item::walk() to indicate if we should also
traverse sub queries.
- Added TABLE parameter to cp_buffer_from_ref()
- Don't close tables created with CREATE ... SELECT but keep them in
the table cache. (Faster usage of newly created tables).
New interfaces:
- table->clear_column_bitmaps() to initialize the bitmaps for tables
at start of new statements.
- table->column_bitmaps_set() to set up new column bitmaps and signal
the handler about this.
- table->column_bitmaps_set_no_signal() for some few cases where we need
to setup new column bitmaps but don't signal the handler (as the handler
has already been signaled about these before). Used for the momement
only in opt_range.cc when doing ROR scans.
- table->use_all_columns() to install a bitmap where all columns are marked
as use in the read and the write set.
- table->default_column_bitmaps() to install the normal read and write
column bitmaps, but not signaling the handler about this.
This is mainly used when creating TABLE instances.
- table->mark_columns_needed_for_delete(),
table->mark_columns_needed_for_delete() and
table->mark_columns_needed_for_insert() to allow us to put additional
columns in column usage maps if handler so requires.
(The handler indicates what it neads in handler->table_flags())
- table->prepare_for_position() to allow us to tell handler that it
needs to read primary key parts to be able to store them in
future table->position() calls.
(This replaces the table->file->ha_retrieve_all_pk function)
- table->mark_auto_increment_column() to tell handler are going to update
columns part of any auto_increment key.
- table->mark_columns_used_by_index() to mark all columns that is part of
an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow
it to quickly know that it only needs to read colums that are part
of the key. (The handler can also use the column map for detecting this,
but simpler/faster handler can just monitor the extra() call).
- table->mark_columns_used_by_index_no_reset() to in addition to other columns,
also mark all columns that is used by the given key.
- table->restore_column_maps_after_mark_index() to restore to default
column maps after a call to table->mark_columns_used_by_index().
- New item function register_field_in_read_map(), for marking used columns
in table->read_map. Used by filesort() to mark all used columns
- Maintain in TABLE->merge_keys set of all keys that are used in query.
(Simplices some optimization loops)
- Maintain Field->part_of_key_not_clustered which is like Field->part_of_key
but the field in the clustered key is not assumed to be part of all index.
(used in opt_range.cc for faster loops)
- dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map()
tmp_use_all_columns() and tmp_restore_column_map() functions to temporally
mark all columns as usable. The 'dbug_' version is primarily intended
inside a handler when it wants to just call Field:store() & Field::val()
functions, but don't need the column maps set for any other usage.
(ie:: bitmap_is_set() is never called)
- We can't use compare_records() to skip updates for handlers that returns
a partial column set and the read_set doesn't cover all columns in the
write set. The reason for this is that if we have a column marked only for
write we can't in the MySQL level know if the value changed or not.
The reason this worked before was that MySQL marked all to be written
columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden
bug'.
- open_table_from_share() does not anymore setup temporary MEM_ROOT
object as a thread specific variable for the handler. Instead we
send the to-be-used MEMROOT to get_new_handler().
(Simpler, faster code)
Bugs fixed:
- Column marking was not done correctly in a lot of cases.
(ALTER TABLE, when using triggers, auto_increment fields etc)
(Could potentially result in wrong values inserted in table handlers
relying on that the old column maps or field->set_query_id was correct)
Especially when it comes to triggers, there may be cases where the
old code would cause lost/wrong values for NDB and/or InnoDB tables.
- Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags:
OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG.
This allowed me to remove some wrong warnings about:
"Some non-transactional changed tables couldn't be rolled back"
- Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset
(thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose
some warnings about
"Some non-transactional changed tables couldn't be rolled back")
- Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table()
which could cause delete_table to report random failures.
- Fixed core dumps for some tests when running with --debug
- Added missing FN_LIBCHAR in mysql_rm_tmp_tables()
(This has probably caused us to not properly remove temporary files after
crash)
- slow_logs was not properly initialized, which could maybe cause
extra/lost entries in slow log.
- If we get an duplicate row on insert, change column map to read and
write all columns while retrying the operation. This is required by
the definition of REPLACE and also ensures that fields that are only
part of UPDATE are properly handled. This fixed a bug in NDB and
REPLACE where REPLACE wrongly copied some column values from the replaced
row.
- For table handler that doesn't support NULL in keys, we would give an error
when creating a primary key with NULL fields, even after the fields has been
automaticly converted to NOT NULL.
- Creating a primary key on a SPATIAL key, would fail if field was not
declared as NOT NULL.
Cleanups:
- Removed not used condition argument to setup_tables
- Removed not needed item function reset_query_id_processor().
- Field->add_index is removed. Now this is instead maintained in
(field->flags & FIELD_IN_ADD_INDEX)
- Field->fieldnr is removed (use field->field_index instead)
- New argument to filesort() to indicate that it should return a set of
row pointers (not used columns). This allowed me to remove some references
to sql_command in filesort and should also enable us to return column
results in some cases where we couldn't before.
- Changed column bitmap handling in opt_range.cc to be aligned with TABLE
bitmap, which allowed me to use bitmap functions instead of looping over
all fields to create some needed bitmaps. (Faster and smaller code)
- Broke up found too long lines
- Moved some variable declaration at start of function for better code
readability.
- Removed some not used arguments from functions.
(setup_fields(), mysql_prepare_insert_check_table())
- setup_fields() now takes an enum instead of an int for marking columns
usage.
- For internal temporary tables, use handler::write_row(),
handler::delete_row() and handler::update_row() instead of
handler::ha_xxxx() for faster execution.
- Changed some constants to enum's and define's.
- Using separate column read and write sets allows for easier checking
of timestamp field was set by statement.
- Remove calls to free_io_cache() as this is now done automaticly in ha_reset()
- Don't build table->normalized_path as this is now identical to table->path
(after bar's fixes to convert filenames)
- Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to
do comparision with the 'convert-dbug-for-diff' tool.
Things left to do in 5.1:
- We wrongly log failed CREATE TABLE ... SELECT in some cases when using
row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result)
Mats has promised to look into this.
- Test that my fix for CREATE TABLE ... SELECT is indeed correct.
(I added several test cases for this, but in this case it's better that
someone else also tests this throughly).
Lars has promosed to do this.
2006-06-04 17:52:22 +02:00
|
|
|
!(file->ha_table_flags() & HA_FILE_BASED))
|
2003-12-30 12:14:21 +01:00
|
|
|
{
|
2005-05-26 05:26:40 +02:00
|
|
|
strmov(tmp_name, old_name);
|
|
|
|
my_casedn_str(files_charset_info, tmp_name);
|
2005-12-31 06:01:26 +01:00
|
|
|
build_table_filename(lc_from, sizeof(lc_from), old_db, tmp_name, "");
|
2005-05-26 05:26:40 +02:00
|
|
|
from_base= lc_from;
|
2003-12-30 12:14:21 +01:00
|
|
|
|
2005-05-26 05:26:40 +02:00
|
|
|
strmov(tmp_name, new_name);
|
|
|
|
my_casedn_str(files_charset_info, tmp_name);
|
2005-12-31 06:01:26 +01:00
|
|
|
build_table_filename(lc_to, sizeof(lc_to), new_db, tmp_name, "");
|
2005-05-26 05:26:40 +02:00
|
|
|
to_base= lc_to;
|
2003-12-30 12:14:21 +01:00
|
|
|
}
|
|
|
|
|
2005-06-02 01:13:24 +02:00
|
|
|
if (!file || !(error=file->rename_table(from_base, to_base)))
|
2000-08-21 02:00:52 +02:00
|
|
|
{
|
|
|
|
if (rename_file_ext(from,to,reg_ext))
|
|
|
|
{
|
2000-08-21 23:18:32 +02:00
|
|
|
error=my_errno;
|
2000-08-21 02:00:52 +02:00
|
|
|
/* Restore old file name */
|
2004-10-21 18:10:58 +02:00
|
|
|
if (file)
|
2005-06-02 01:13:24 +02:00
|
|
|
file->rename_table(to_base, from_base);
|
2000-08-21 02:00:52 +02:00
|
|
|
}
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
delete file;
|
2006-04-25 12:27:28 +02:00
|
|
|
if (error == HA_ERR_WRONG_COMMAND)
|
|
|
|
my_error(ER_NOT_SUPPORTED_YET, MYF(0), "ALTER TABLE");
|
|
|
|
else if (error)
|
2000-08-21 23:18:32 +02:00
|
|
|
my_error(ER_ERROR_ON_RENAME, MYF(0), from, to, error);
|
|
|
|
DBUG_RETURN(error != 0);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
2003-12-30 12:14:21 +01:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
/*
|
2003-06-01 11:32:53 +02:00
|
|
|
Force all other threads to stop using the table
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
wait_while_table_is_used()
|
|
|
|
thd Thread handler
|
|
|
|
table Table to remove from cache
|
2003-07-08 22:58:04 +02:00
|
|
|
function HA_EXTRA_PREPARE_FOR_DELETE if table is to be deleted
|
2004-04-08 16:56:45 +02:00
|
|
|
HA_EXTRA_FORCE_REOPEN if table is not be used
|
2003-06-01 11:32:53 +02:00
|
|
|
NOTES
|
|
|
|
When returning, the table will be unusable for other threads until
|
|
|
|
the table is closed.
|
|
|
|
|
|
|
|
PREREQUISITES
|
|
|
|
Lock on LOCK_open
|
|
|
|
Win32 clients must also have a WRITE LOCK on the table !
|
2000-07-31 21:29:14 +02:00
|
|
|
*/
|
|
|
|
|
2003-07-08 22:58:04 +02:00
|
|
|
static void wait_while_table_is_used(THD *thd,TABLE *table,
|
|
|
|
enum ha_extra_function function)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2003-06-01 11:32:53 +02:00
|
|
|
DBUG_ENTER("wait_while_table_is_used");
|
2005-11-23 21:45:02 +01:00
|
|
|
DBUG_PRINT("enter", ("table: '%s' share: 0x%lx db_stat: %u version: %u",
|
|
|
|
table->s->table_name.str, (ulong) table->s,
|
|
|
|
table->db_stat, table->s->version));
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2003-07-08 22:58:04 +02:00
|
|
|
VOID(table->file->extra(function));
|
2003-06-01 11:32:53 +02:00
|
|
|
/* Mark all tables that are in use as 'old' */
|
2006-01-17 08:40:00 +01:00
|
|
|
mysql_lock_abort(thd, table, TRUE); /* end threads waiting on lock */
|
2003-06-01 11:32:53 +02:00
|
|
|
|
|
|
|
/* Wait until all there are no other threads that has this table open */
|
2005-11-23 21:45:02 +01:00
|
|
|
remove_table_from_cache(thd, table->s->db.str,
|
|
|
|
table->s->table_name.str,
|
|
|
|
RTFC_WAIT_OTHER_THREAD_FLAG);
|
2003-04-03 19:24:15 +02:00
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2003-06-01 11:32:53 +02:00
|
|
|
/*
|
|
|
|
Close a cached table
|
2003-05-14 00:27:26 +02:00
|
|
|
|
2003-06-01 11:32:53 +02:00
|
|
|
SYNOPSIS
|
2003-12-30 12:14:21 +01:00
|
|
|
close_cached_table()
|
2003-06-01 11:32:53 +02:00
|
|
|
thd Thread handler
|
|
|
|
table Table to remove from cache
|
|
|
|
|
|
|
|
NOTES
|
|
|
|
Function ends by signaling threads waiting for the table to try to
|
|
|
|
reopen the table.
|
2003-05-14 00:27:26 +02:00
|
|
|
|
2003-06-01 11:32:53 +02:00
|
|
|
PREREQUISITES
|
|
|
|
Lock on LOCK_open
|
|
|
|
Win32 clients must also have a WRITE LOCK on the table !
|
|
|
|
*/
|
2004-04-08 16:56:45 +02:00
|
|
|
|
2004-09-07 14:29:46 +02:00
|
|
|
void close_cached_table(THD *thd, TABLE *table)
|
2003-04-03 19:24:15 +02:00
|
|
|
{
|
|
|
|
DBUG_ENTER("close_cached_table");
|
2004-04-08 16:56:45 +02:00
|
|
|
|
2003-07-08 22:58:04 +02:00
|
|
|
wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_DELETE);
|
2003-06-01 11:32:53 +02:00
|
|
|
/* Close lock if this is not got with LOCK TABLES */
|
|
|
|
if (thd->lock)
|
2003-04-03 19:24:15 +02:00
|
|
|
{
|
2003-06-01 11:32:53 +02:00
|
|
|
mysql_unlock_tables(thd, thd->lock);
|
|
|
|
thd->lock=0; // Start locked threads
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2003-06-01 11:32:53 +02:00
|
|
|
/* Close all copies of 'table'. This also frees all LOCK TABLES lock */
|
|
|
|
thd->open_tables=unlink_open_table(thd,thd->open_tables,table);
|
|
|
|
|
|
|
|
/* When lock on LOCK_open is freed other threads can continue */
|
2006-06-26 19:14:35 +02:00
|
|
|
broadcast_refresh();
|
2004-12-06 16:15:54 +01:00
|
|
|
DBUG_VOID_RETURN;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
2002-12-11 08:17:51 +01:00
|
|
|
static int send_check_errmsg(THD *thd, TABLE_LIST* table,
|
2000-09-15 00:34:50 +02:00
|
|
|
const char* operator_name, const char* errmsg)
|
2000-10-04 17:26:31 +02:00
|
|
|
|
2000-09-15 00:34:50 +02:00
|
|
|
{
|
2002-12-11 08:17:51 +01:00
|
|
|
Protocol *protocol= thd->protocol;
|
|
|
|
protocol->prepare_for_resend();
|
2003-03-17 10:14:04 +01:00
|
|
|
protocol->store(table->alias, system_charset_info);
|
|
|
|
protocol->store((char*) operator_name, system_charset_info);
|
2005-11-20 19:47:07 +01:00
|
|
|
protocol->store(STRING_WITH_LEN("error"), system_charset_info);
|
2003-03-17 10:14:04 +01:00
|
|
|
protocol->store(errmsg, system_charset_info);
|
2004-10-20 03:04:37 +02:00
|
|
|
thd->clear_error();
|
2002-12-11 08:17:51 +01:00
|
|
|
if (protocol->write())
|
2000-09-15 00:34:50 +02:00
|
|
|
return -1;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2002-06-11 10:20:31 +02:00
|
|
|
|
2002-03-22 13:40:29 +01:00
|
|
|
static int prepare_for_restore(THD* thd, TABLE_LIST* table,
|
2002-06-11 10:20:31 +02:00
|
|
|
HA_CHECK_OPT *check_opt)
|
2000-09-15 00:34:50 +02:00
|
|
|
{
|
2001-01-22 14:33:34 +01:00
|
|
|
DBUG_ENTER("prepare_for_restore");
|
2000-10-04 17:26:31 +02:00
|
|
|
|
2001-01-22 14:33:34 +01:00
|
|
|
if (table->table) // do not overwrite existing tables on restore
|
|
|
|
{
|
|
|
|
DBUG_RETURN(send_check_errmsg(thd, table, "restore",
|
|
|
|
"table exists, will not overwrite on restore"
|
|
|
|
));
|
|
|
|
}
|
2000-09-15 00:34:50 +02:00
|
|
|
else
|
2001-01-22 14:33:34 +01:00
|
|
|
{
|
2003-12-19 18:52:13 +01:00
|
|
|
char* backup_dir= thd->lex->backup_dir;
|
2005-12-31 06:01:26 +01:00
|
|
|
char src_path[FN_REFLEN], dst_path[FN_REFLEN], uname[FN_REFLEN];
|
2005-10-17 20:37:24 +02:00
|
|
|
char* table_name= table->table_name;
|
|
|
|
char* db= table->db;
|
2000-10-04 17:26:31 +02:00
|
|
|
|
2005-12-31 06:01:26 +01:00
|
|
|
VOID(tablename_to_filename(table->table_name, uname, sizeof(uname)));
|
|
|
|
|
|
|
|
if (fn_format_relative_to_data_home(src_path, uname, backup_dir, reg_ext))
|
2001-01-22 14:33:34 +01:00
|
|
|
DBUG_RETURN(-1); // protect buffer overflow
|
2000-09-15 00:34:50 +02:00
|
|
|
|
2005-12-31 06:01:26 +01:00
|
|
|
build_table_filename(dst_path, sizeof(dst_path), db, table_name, reg_ext);
|
2000-10-04 17:26:31 +02:00
|
|
|
|
2001-09-02 12:47:00 +02:00
|
|
|
if (lock_and_wait_for_table_name(thd,table))
|
2001-01-22 14:33:34 +01:00
|
|
|
DBUG_RETURN(-1);
|
2000-10-04 17:26:31 +02:00
|
|
|
|
2005-12-31 06:01:26 +01:00
|
|
|
if (my_copy(src_path, dst_path, MYF(MY_WME)))
|
2001-01-22 14:33:34 +01:00
|
|
|
{
|
2002-08-08 02:12:02 +02:00
|
|
|
pthread_mutex_lock(&LOCK_open);
|
2001-01-22 14:33:34 +01:00
|
|
|
unlock_table_name(thd, table);
|
2002-08-08 02:12:02 +02:00
|
|
|
pthread_mutex_unlock(&LOCK_open);
|
2001-01-22 14:33:34 +01:00
|
|
|
DBUG_RETURN(send_check_errmsg(thd, table, "restore",
|
|
|
|
"Failed copying .frm file"));
|
|
|
|
}
|
2001-09-02 12:47:00 +02:00
|
|
|
if (mysql_truncate(thd, table, 1))
|
2001-01-22 14:33:34 +01:00
|
|
|
{
|
2002-08-08 02:12:02 +02:00
|
|
|
pthread_mutex_lock(&LOCK_open);
|
2001-01-22 14:33:34 +01:00
|
|
|
unlock_table_name(thd, table);
|
2002-08-08 02:12:02 +02:00
|
|
|
pthread_mutex_unlock(&LOCK_open);
|
2001-01-22 14:33:34 +01:00
|
|
|
DBUG_RETURN(send_check_errmsg(thd, table, "restore",
|
|
|
|
"Failed generating table from .frm file"));
|
2000-09-15 00:34:50 +02:00
|
|
|
}
|
2001-01-22 14:33:34 +01:00
|
|
|
}
|
2002-01-18 21:19:41 +01:00
|
|
|
|
2002-06-11 10:20:31 +02:00
|
|
|
/*
|
|
|
|
Now we should be able to open the partially restored table
|
|
|
|
to finish the restore in the handler later on
|
|
|
|
*/
|
2005-10-17 20:37:24 +02:00
|
|
|
pthread_mutex_lock(&LOCK_open);
|
|
|
|
if (reopen_name_locked_table(thd, table))
|
2002-08-08 02:12:02 +02:00
|
|
|
{
|
2002-06-11 10:20:31 +02:00
|
|
|
unlock_table_name(thd, table);
|
2002-08-08 02:12:02 +02:00
|
|
|
pthread_mutex_unlock(&LOCK_open);
|
2005-10-17 20:37:24 +02:00
|
|
|
DBUG_RETURN(send_check_errmsg(thd, table, "restore",
|
|
|
|
"Failed to open partially restored table"));
|
2002-08-08 02:12:02 +02:00
|
|
|
}
|
2005-10-17 20:37:24 +02:00
|
|
|
pthread_mutex_unlock(&LOCK_open);
|
2001-01-22 14:33:34 +01:00
|
|
|
DBUG_RETURN(0);
|
2000-09-15 00:34:50 +02:00
|
|
|
}
|
2000-08-15 19:09:37 +02:00
|
|
|
|
2002-06-11 10:20:31 +02:00
|
|
|
|
2005-11-23 21:45:02 +01:00
|
|
|
static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
|
2002-06-11 10:20:31 +02:00
|
|
|
HA_CHECK_OPT *check_opt)
|
2002-03-22 13:40:29 +01:00
|
|
|
{
|
2003-05-14 00:27:26 +02:00
|
|
|
int error= 0;
|
|
|
|
TABLE tmp_table, *table;
|
2005-11-23 21:45:02 +01:00
|
|
|
TABLE_SHARE *share;
|
|
|
|
char from[FN_REFLEN],tmp[FN_REFLEN+32];
|
|
|
|
const char **ext;
|
|
|
|
MY_STAT stat_info;
|
2002-03-22 13:40:29 +01:00
|
|
|
DBUG_ENTER("prepare_for_repair");
|
|
|
|
|
|
|
|
if (!(check_opt->sql_flags & TT_USEFRM))
|
|
|
|
DBUG_RETURN(0);
|
2003-05-14 00:27:26 +02:00
|
|
|
|
|
|
|
if (!(table= table_list->table)) /* if open_ltable failed */
|
2002-03-22 13:40:29 +01:00
|
|
|
{
|
2005-11-23 21:45:02 +01:00
|
|
|
char key[MAX_DBKEY_LENGTH];
|
|
|
|
uint key_length;
|
|
|
|
|
|
|
|
key_length= create_table_def_key(thd, key, table_list, 0);
|
|
|
|
pthread_mutex_lock(&LOCK_open);
|
|
|
|
if (!(share= (get_table_share(thd, table_list, key, key_length, 0,
|
|
|
|
&error))))
|
|
|
|
{
|
|
|
|
pthread_mutex_unlock(&LOCK_open);
|
2003-05-14 00:27:26 +02:00
|
|
|
DBUG_RETURN(0); // Can't open frm file
|
2005-11-23 21:45:02 +01:00
|
|
|
}
|
|
|
|
|
2006-01-17 08:40:00 +01:00
|
|
|
if (open_table_from_share(thd, share, "", 0, 0, 0, &tmp_table, FALSE))
|
2005-11-23 21:45:02 +01:00
|
|
|
{
|
|
|
|
release_table_share(share, RELEASE_NORMAL);
|
|
|
|
pthread_mutex_unlock(&LOCK_open);
|
|
|
|
DBUG_RETURN(0); // Out of memory
|
|
|
|
}
|
2003-05-14 00:27:26 +02:00
|
|
|
table= &tmp_table;
|
2005-11-23 21:45:02 +01:00
|
|
|
pthread_mutex_unlock(&LOCK_open);
|
2003-05-02 22:12:15 +02:00
|
|
|
}
|
2002-03-22 13:40:29 +01:00
|
|
|
|
2003-05-14 00:27:26 +02:00
|
|
|
/*
|
|
|
|
User gave us USE_FRM which means that the header in the index file is
|
|
|
|
trashed.
|
|
|
|
In this case we will try to fix the table the following way:
|
|
|
|
- Rename the data file to a temporary name
|
|
|
|
- Truncate the table
|
|
|
|
- Replace the new data file with the old one
|
|
|
|
- Run a normal repair using the new index file and the old data file
|
|
|
|
*/
|
2002-03-22 13:40:29 +01:00
|
|
|
|
2003-05-14 00:27:26 +02:00
|
|
|
/*
|
|
|
|
Check if this is a table type that stores index and data separately,
|
|
|
|
like ISAM or MyISAM
|
|
|
|
*/
|
2005-11-23 21:45:02 +01:00
|
|
|
ext= table->file->bas_ext();
|
2003-05-14 00:27:26 +02:00
|
|
|
if (!ext[0] || !ext[1])
|
|
|
|
goto end; // No data file
|
2002-03-22 13:40:29 +01:00
|
|
|
|
2005-11-23 21:45:02 +01:00
|
|
|
// Name of data file
|
|
|
|
strxmov(from, table->s->normalized_path.str, ext[1], NullS);
|
2003-05-14 00:27:26 +02:00
|
|
|
if (!my_stat(from, &stat_info, MYF(0)))
|
|
|
|
goto end; // Can't use USE_FRM flag
|
2002-03-22 13:40:29 +01:00
|
|
|
|
2004-04-08 16:56:45 +02:00
|
|
|
my_snprintf(tmp, sizeof(tmp), "%s-%lx_%lx",
|
|
|
|
from, current_pid, thd->thread_id);
|
2003-05-14 00:27:26 +02:00
|
|
|
|
2003-06-01 11:32:53 +02:00
|
|
|
/* If we could open the table, close it */
|
|
|
|
if (table_list->table)
|
|
|
|
{
|
|
|
|
pthread_mutex_lock(&LOCK_open);
|
|
|
|
close_cached_table(thd, table);
|
|
|
|
pthread_mutex_unlock(&LOCK_open);
|
|
|
|
}
|
2003-05-14 00:27:26 +02:00
|
|
|
if (lock_and_wait_for_table_name(thd,table_list))
|
2002-03-22 13:40:29 +01:00
|
|
|
{
|
2003-05-14 00:27:26 +02:00
|
|
|
error= -1;
|
|
|
|
goto end;
|
2002-03-22 13:40:29 +01:00
|
|
|
}
|
2003-05-14 00:27:26 +02:00
|
|
|
if (my_rename(from, tmp, MYF(MY_WME)))
|
2002-03-22 13:40:29 +01:00
|
|
|
{
|
2002-09-01 20:17:33 +02:00
|
|
|
pthread_mutex_lock(&LOCK_open);
|
2003-05-14 00:27:26 +02:00
|
|
|
unlock_table_name(thd, table_list);
|
2002-09-01 20:17:33 +02:00
|
|
|
pthread_mutex_unlock(&LOCK_open);
|
2003-05-14 00:27:26 +02:00
|
|
|
error= send_check_errmsg(thd, table_list, "repair",
|
|
|
|
"Failed renaming data file");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
if (mysql_truncate(thd, table_list, 1))
|
|
|
|
{
|
|
|
|
pthread_mutex_lock(&LOCK_open);
|
|
|
|
unlock_table_name(thd, table_list);
|
|
|
|
pthread_mutex_unlock(&LOCK_open);
|
|
|
|
error= send_check_errmsg(thd, table_list, "repair",
|
|
|
|
"Failed generating table from .frm file");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
if (my_rename(tmp, from, MYF(MY_WME)))
|
|
|
|
{
|
|
|
|
pthread_mutex_lock(&LOCK_open);
|
|
|
|
unlock_table_name(thd, table_list);
|
|
|
|
pthread_mutex_unlock(&LOCK_open);
|
|
|
|
error= send_check_errmsg(thd, table_list, "repair",
|
|
|
|
"Failed restoring .MYD file");
|
|
|
|
goto end;
|
2002-03-22 13:40:29 +01:00
|
|
|
}
|
|
|
|
|
2002-06-11 10:20:31 +02:00
|
|
|
/*
|
|
|
|
Now we should be able to open the partially repaired table
|
|
|
|
to finish the repair in the handler later on.
|
|
|
|
*/
|
2005-10-17 20:37:24 +02:00
|
|
|
pthread_mutex_lock(&LOCK_open);
|
|
|
|
if (reopen_name_locked_table(thd, table_list))
|
2002-08-08 14:24:47 +02:00
|
|
|
{
|
2003-05-14 00:27:26 +02:00
|
|
|
unlock_table_name(thd, table_list);
|
2002-08-08 14:24:47 +02:00
|
|
|
pthread_mutex_unlock(&LOCK_open);
|
2005-10-17 20:37:24 +02:00
|
|
|
error= send_check_errmsg(thd, table_list, "repair",
|
|
|
|
"Failed to open partially repaired table");
|
|
|
|
goto end;
|
2002-08-08 14:24:47 +02:00
|
|
|
}
|
2005-10-17 20:37:24 +02:00
|
|
|
pthread_mutex_unlock(&LOCK_open);
|
2003-05-14 00:27:26 +02:00
|
|
|
|
|
|
|
end:
|
|
|
|
if (table == &tmp_table)
|
2005-11-23 21:45:02 +01:00
|
|
|
{
|
|
|
|
pthread_mutex_lock(&LOCK_open);
|
|
|
|
closefrm(table, 1); // Free allocated memory
|
|
|
|
pthread_mutex_unlock(&LOCK_open);
|
|
|
|
}
|
2003-05-14 00:27:26 +02:00
|
|
|
DBUG_RETURN(error);
|
2002-03-22 13:40:29 +01:00
|
|
|
}
|
2001-09-02 12:47:00 +02:00
|
|
|
|
2002-06-11 10:20:31 +02:00
|
|
|
|
2005-07-05 12:37:02 +02:00
|
|
|
|
2004-11-03 18:23:58 +01:00
|
|
|
/*
|
|
|
|
RETURN VALUES
|
2004-11-12 14:36:31 +01:00
|
|
|
FALSE Message sent to net (admin operation went ok)
|
|
|
|
TRUE Message should be sent by caller
|
|
|
|
(admin operation or network communication failed)
|
2004-11-03 18:23:58 +01:00
|
|
|
*/
|
2004-10-20 03:04:37 +02:00
|
|
|
static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
|
|
|
|
HA_CHECK_OPT* check_opt,
|
|
|
|
const char *operator_name,
|
|
|
|
thr_lock_type lock_type,
|
|
|
|
bool open_for_modify,
|
2005-02-24 22:33:42 +01:00
|
|
|
bool no_warnings_for_error,
|
2004-10-20 03:04:37 +02:00
|
|
|
uint extra_open_options,
|
|
|
|
int (*prepare_func)(THD *, TABLE_LIST *,
|
|
|
|
HA_CHECK_OPT *),
|
2004-11-21 15:35:42 +01:00
|
|
|
int (handler::*operator_func)(THD *,
|
|
|
|
HA_CHECK_OPT *),
|
|
|
|
int (view_operator_func)(THD *, TABLE_LIST*))
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2005-07-05 12:37:02 +02:00
|
|
|
TABLE_LIST *table, *save_next_global, *save_next_local;
|
|
|
|
SELECT_LEX *select= &thd->lex->select_lex;
|
2000-07-31 21:29:14 +02:00
|
|
|
List<Item> field_list;
|
2002-12-11 08:17:51 +01:00
|
|
|
Item *item;
|
|
|
|
Protocol *protocol= thd->protocol;
|
2005-07-05 12:37:02 +02:00
|
|
|
LEX *lex= thd->lex;
|
2004-10-28 18:37:25 +02:00
|
|
|
int result_code;
|
2000-09-12 02:02:33 +02:00
|
|
|
DBUG_ENTER("mysql_admin_table");
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2006-05-05 19:08:40 +02:00
|
|
|
if (end_active_trans(thd))
|
|
|
|
DBUG_RETURN(1);
|
2000-07-31 21:29:14 +02:00
|
|
|
field_list.push_back(item = new Item_empty_string("Table", NAME_LEN*2));
|
|
|
|
item->maybe_null = 1;
|
|
|
|
field_list.push_back(item = new Item_empty_string("Op", 10));
|
|
|
|
item->maybe_null = 1;
|
|
|
|
field_list.push_back(item = new Item_empty_string("Msg_type", 10));
|
|
|
|
item->maybe_null = 1;
|
|
|
|
field_list.push_back(item = new Item_empty_string("Msg_text", 255));
|
|
|
|
item->maybe_null = 1;
|
2004-08-03 12:32:21 +02:00
|
|
|
if (protocol->send_fields(&field_list,
|
|
|
|
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2005-11-15 21:57:02 +01:00
|
|
|
mysql_ha_flush(thd, tables, MYSQL_HA_CLOSE_FINAL, FALSE);
|
2004-07-16 00:15:55 +02:00
|
|
|
for (table= tables; table; table= table->next_local)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
|
|
|
char table_name[NAME_LEN*2+2];
|
2005-01-24 15:48:25 +01:00
|
|
|
char* db = table->db;
|
2000-10-14 02:16:35 +02:00
|
|
|
bool fatal_error=0;
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2005-01-24 19:41:42 +01:00
|
|
|
strxmov(table_name, db, ".", table->table_name, NullS);
|
2001-01-22 14:33:34 +01:00
|
|
|
thd->open_options|= extra_open_options;
|
2004-10-28 18:37:25 +02:00
|
|
|
table->lock_type= lock_type;
|
|
|
|
/* open only one table from local list of command */
|
2005-07-05 12:37:02 +02:00
|
|
|
save_next_global= table->next_global;
|
2004-10-28 18:37:25 +02:00
|
|
|
table->next_global= 0;
|
2005-07-05 12:37:02 +02:00
|
|
|
save_next_local= table->next_local;
|
|
|
|
table->next_local= 0;
|
|
|
|
select->table_list.first= (byte*)table;
|
|
|
|
/*
|
|
|
|
Time zone tables and SP tables can be add to lex->query_tables list,
|
|
|
|
so it have to be prepared.
|
|
|
|
TODO: Investigate if we can put extra tables into argument instead of
|
|
|
|
using lex->query_tables
|
|
|
|
*/
|
|
|
|
lex->query_tables= table;
|
|
|
|
lex->query_tables_last= &table->next_global;
|
2005-12-03 15:02:09 +01:00
|
|
|
lex->query_tables_own_last= 0;
|
2005-02-24 22:33:42 +01:00
|
|
|
thd->no_warnings_for_error= no_warnings_for_error;
|
2005-12-03 15:02:09 +01:00
|
|
|
if (view_operator_func == NULL)
|
2005-12-05 12:08:30 +01:00
|
|
|
table->required_type=FRMTYPE_TABLE;
|
2004-10-28 18:37:25 +02:00
|
|
|
open_and_lock_tables(thd, table);
|
2005-02-24 22:33:42 +01:00
|
|
|
thd->no_warnings_for_error= 0;
|
2005-07-05 12:37:02 +02:00
|
|
|
table->next_global= save_next_global;
|
|
|
|
table->next_local= save_next_local;
|
2001-01-22 14:33:34 +01:00
|
|
|
thd->open_options&= ~extra_open_options;
|
2002-12-11 08:17:51 +01:00
|
|
|
|
2002-01-18 21:19:41 +01:00
|
|
|
if (prepare_func)
|
2000-09-25 23:33:25 +02:00
|
|
|
{
|
2002-03-22 13:40:29 +01:00
|
|
|
switch ((*prepare_func)(thd, table, check_opt)) {
|
2004-10-28 20:14:00 +02:00
|
|
|
case 1: // error, message written to net
|
2006-05-05 19:08:40 +02:00
|
|
|
ha_autocommit_or_rollback(thd, 1);
|
2004-10-28 20:14:00 +02:00
|
|
|
close_thread_tables(thd);
|
|
|
|
continue;
|
|
|
|
case -1: // error, message could be written to net
|
|
|
|
goto err;
|
|
|
|
default: // should be 0 otherwise
|
|
|
|
;
|
2000-09-15 00:34:50 +02:00
|
|
|
}
|
2000-09-25 23:33:25 +02:00
|
|
|
}
|
2000-10-04 17:26:31 +02:00
|
|
|
|
2004-10-28 18:37:25 +02:00
|
|
|
/*
|
2004-11-21 15:35:42 +01:00
|
|
|
CHECK TABLE command is only command where VIEW allowed here and this
|
|
|
|
command use only temporary teble method for VIEWs resolving => there
|
|
|
|
can't be VIEW tree substitition of join view => if opening table
|
|
|
|
succeed then table->table will have real TABLE pointer as value (in
|
|
|
|
case of join view substitution table->table can be 0, but here it is
|
|
|
|
impossible)
|
2004-10-28 18:37:25 +02:00
|
|
|
*/
|
2000-07-31 21:29:14 +02:00
|
|
|
if (!table->table)
|
|
|
|
{
|
2004-11-21 15:35:42 +01:00
|
|
|
char buf[ERRMSGSIZE+ERRMSGSIZE+2];
|
2000-07-31 21:29:14 +02:00
|
|
|
const char *err_msg;
|
2002-12-11 08:17:51 +01:00
|
|
|
protocol->prepare_for_resend();
|
2003-03-17 10:14:04 +01:00
|
|
|
protocol->store(table_name, system_charset_info);
|
|
|
|
protocol->store(operator_name, system_charset_info);
|
2005-11-20 19:47:07 +01:00
|
|
|
protocol->store(STRING_WITH_LEN("error"), system_charset_info);
|
2000-07-31 21:29:14 +02:00
|
|
|
if (!(err_msg=thd->net.last_error))
|
|
|
|
err_msg=ER(ER_CHECK_NO_SUCH_TABLE);
|
2004-10-28 18:37:25 +02:00
|
|
|
/* if it was a view will check md5 sum */
|
|
|
|
if (table->view &&
|
|
|
|
view_checksum(thd, table) == HA_ADMIN_WRONG_CHECKSUM)
|
|
|
|
{
|
2004-11-21 15:35:42 +01:00
|
|
|
strxmov(buf, err_msg, "; ", ER(ER_VIEW_CHECKSUM), NullS);
|
2004-10-28 18:37:25 +02:00
|
|
|
err_msg= (const char *)buf;
|
|
|
|
}
|
2003-03-17 10:14:04 +01:00
|
|
|
protocol->store(err_msg, system_charset_info);
|
2005-07-05 12:37:02 +02:00
|
|
|
lex->cleanup_after_one_table_open();
|
2004-10-20 03:04:37 +02:00
|
|
|
thd->clear_error();
|
2005-07-05 12:37:02 +02:00
|
|
|
/*
|
|
|
|
View opening can be interrupted in the middle of process so some
|
|
|
|
tables can be left opening
|
|
|
|
*/
|
2006-05-05 19:08:40 +02:00
|
|
|
ha_autocommit_or_rollback(thd, 1);
|
2005-07-05 12:37:02 +02:00
|
|
|
close_thread_tables(thd);
|
2006-05-30 07:45:23 +02:00
|
|
|
lex->reset_query_tables_list(FALSE);
|
2002-12-11 08:17:51 +01:00
|
|
|
if (protocol->write())
|
2000-07-31 21:29:14 +02:00
|
|
|
goto err;
|
|
|
|
continue;
|
|
|
|
}
|
2004-10-28 18:37:25 +02:00
|
|
|
|
|
|
|
if (table->view)
|
|
|
|
{
|
|
|
|
result_code= (*view_operator_func)(thd, table);
|
|
|
|
goto send_result;
|
|
|
|
}
|
|
|
|
|
2003-06-12 13:29:02 +02:00
|
|
|
table->table->pos_in_table_list= table;
|
2000-09-12 02:02:33 +02:00
|
|
|
if ((table->table->db_stat & HA_READ_ONLY) && open_for_modify)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2000-10-10 23:48:03 +02:00
|
|
|
char buff[FN_REFLEN + MYSQL_ERRMSG_SIZE];
|
2005-05-25 17:33:36 +02:00
|
|
|
uint length;
|
2002-12-11 08:17:51 +01:00
|
|
|
protocol->prepare_for_resend();
|
2003-03-17 10:14:04 +01:00
|
|
|
protocol->store(table_name, system_charset_info);
|
|
|
|
protocol->store(operator_name, system_charset_info);
|
2005-11-20 19:47:07 +01:00
|
|
|
protocol->store(STRING_WITH_LEN("error"), system_charset_info);
|
2005-05-25 17:33:36 +02:00
|
|
|
length= my_snprintf(buff, sizeof(buff), ER(ER_OPEN_AS_READONLY),
|
|
|
|
table_name);
|
|
|
|
protocol->store(buff, length, system_charset_info);
|
2006-05-05 19:08:40 +02:00
|
|
|
ha_autocommit_or_rollback(thd, 0);
|
2000-09-12 02:02:33 +02:00
|
|
|
close_thread_tables(thd);
|
2006-05-30 15:07:49 +02:00
|
|
|
lex->reset_query_tables_list(FALSE);
|
2001-12-06 00:05:30 +01:00
|
|
|
table->table=0; // For query cache
|
2002-12-11 08:17:51 +01:00
|
|
|
if (protocol->write())
|
2000-07-31 21:29:14 +02:00
|
|
|
goto err;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2000-10-17 15:19:24 +02:00
|
|
|
/* Close all instances of the table to allow repair to rename files */
|
2006-01-19 03:56:06 +01:00
|
|
|
if (lock_type == TL_WRITE && table->table->s->version &&
|
|
|
|
!table->table->s->log_table)
|
2000-10-17 15:19:24 +02:00
|
|
|
{
|
|
|
|
pthread_mutex_lock(&LOCK_open);
|
2001-11-26 01:16:38 +01:00
|
|
|
const char *old_message=thd->enter_cond(&COND_refresh, &LOCK_open,
|
|
|
|
"Waiting to get writelock");
|
2006-01-17 08:40:00 +01:00
|
|
|
mysql_lock_abort(thd,table->table, TRUE);
|
2005-11-23 21:45:02 +01:00
|
|
|
remove_table_from_cache(thd, table->table->s->db.str,
|
|
|
|
table->table->s->table_name.str,
|
2005-07-27 12:05:30 +02:00
|
|
|
RTFC_WAIT_OTHER_THREAD_FLAG |
|
|
|
|
RTFC_CHECK_KILLED_FLAG);
|
2001-11-26 01:16:38 +01:00
|
|
|
thd->exit_cond(old_message);
|
2000-10-17 15:19:24 +02:00
|
|
|
if (thd->killed)
|
|
|
|
goto err;
|
2005-03-08 03:15:19 +01:00
|
|
|
/* Flush entries in the query cache involving this table. */
|
|
|
|
query_cache_invalidate3(thd, table->table, 0);
|
|
|
|
open_for_modify= 0;
|
2000-10-17 15:19:24 +02:00
|
|
|
}
|
|
|
|
|
2006-02-17 07:52:32 +01:00
|
|
|
if (table->table->s->crashed && operator_func == &handler::ha_check)
|
2005-05-25 17:33:36 +02:00
|
|
|
{
|
|
|
|
protocol->prepare_for_resend();
|
|
|
|
protocol->store(table_name, system_charset_info);
|
|
|
|
protocol->store(operator_name, system_charset_info);
|
2005-11-20 19:47:07 +01:00
|
|
|
protocol->store(STRING_WITH_LEN("warning"), system_charset_info);
|
|
|
|
protocol->store(STRING_WITH_LEN("Table is marked as crashed"),
|
|
|
|
system_charset_info);
|
2005-05-25 17:33:36 +02:00
|
|
|
if (protocol->write())
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2006-02-17 07:52:32 +01:00
|
|
|
if (operator_func == &handler::ha_repair)
|
|
|
|
{
|
|
|
|
if ((table->table->file->check_old_types() == HA_ADMIN_NEEDS_ALTER) ||
|
|
|
|
(table->table->file->ha_check_for_upgrade(check_opt) ==
|
|
|
|
HA_ADMIN_NEEDS_ALTER))
|
|
|
|
{
|
2006-05-05 19:08:40 +02:00
|
|
|
ha_autocommit_or_rollback(thd, 1);
|
2006-02-17 07:52:32 +01:00
|
|
|
close_thread_tables(thd);
|
|
|
|
tmp_disable_binlog(thd); // binlogging is done by caller if wanted
|
|
|
|
result_code= mysql_recreate_table(thd, table, 0);
|
|
|
|
reenable_binlog(thd);
|
|
|
|
goto send_result;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2004-10-28 18:37:25 +02:00
|
|
|
result_code = (table->table->file->*operator_func)(thd, check_opt);
|
|
|
|
|
|
|
|
send_result:
|
|
|
|
|
2005-07-05 12:37:02 +02:00
|
|
|
lex->cleanup_after_one_table_open();
|
2004-10-20 03:04:37 +02:00
|
|
|
thd->clear_error(); // these errors shouldn't get client
|
2002-12-11 08:17:51 +01:00
|
|
|
protocol->prepare_for_resend();
|
2003-03-17 10:14:04 +01:00
|
|
|
protocol->store(table_name, system_charset_info);
|
|
|
|
protocol->store(operator_name, system_charset_info);
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2004-06-10 16:41:24 +02:00
|
|
|
send_result_message:
|
|
|
|
|
|
|
|
DBUG_PRINT("info", ("result_code: %d", result_code));
|
2000-09-12 02:02:33 +02:00
|
|
|
switch (result_code) {
|
|
|
|
case HA_ADMIN_NOT_IMPLEMENTED:
|
2002-11-08 22:22:23 +01:00
|
|
|
{
|
2004-04-08 16:56:45 +02:00
|
|
|
char buf[ERRMSGSIZE+20];
|
|
|
|
uint length=my_snprintf(buf, ERRMSGSIZE,
|
2002-12-11 08:17:51 +01:00
|
|
|
ER(ER_CHECK_NOT_IMPLEMENTED), operator_name);
|
2005-11-20 19:47:07 +01:00
|
|
|
protocol->store(STRING_WITH_LEN("note"), system_charset_info);
|
2004-04-08 16:56:45 +02:00
|
|
|
protocol->store(buf, length, system_charset_info);
|
2002-11-08 22:22:23 +01:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
|
2005-11-02 22:44:58 +01:00
|
|
|
case HA_ADMIN_NOT_BASE_TABLE:
|
|
|
|
{
|
2005-11-03 07:13:10 +01:00
|
|
|
char buf[ERRMSGSIZE+20];
|
|
|
|
uint length= my_snprintf(buf, ERRMSGSIZE,
|
2005-11-06 07:41:36 +01:00
|
|
|
ER(ER_BAD_TABLE_ERROR), table_name);
|
2005-11-20 19:47:07 +01:00
|
|
|
protocol->store(STRING_WITH_LEN("note"), system_charset_info);
|
2005-11-03 07:13:10 +01:00
|
|
|
protocol->store(buf, length, system_charset_info);
|
2005-11-02 22:44:58 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2000-09-12 02:02:33 +02:00
|
|
|
case HA_ADMIN_OK:
|
2005-11-20 19:47:07 +01:00
|
|
|
protocol->store(STRING_WITH_LEN("status"), system_charset_info);
|
|
|
|
protocol->store(STRING_WITH_LEN("OK"), system_charset_info);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
|
2000-09-12 02:02:33 +02:00
|
|
|
case HA_ADMIN_FAILED:
|
2005-11-20 19:47:07 +01:00
|
|
|
protocol->store(STRING_WITH_LEN("status"), system_charset_info);
|
|
|
|
protocol->store(STRING_WITH_LEN("Operation failed"),
|
|
|
|
system_charset_info);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
|
2004-03-26 23:29:31 +01:00
|
|
|
case HA_ADMIN_REJECT:
|
2005-11-20 19:47:07 +01:00
|
|
|
protocol->store(STRING_WITH_LEN("status"), system_charset_info);
|
|
|
|
protocol->store(STRING_WITH_LEN("Operation need committed state"),
|
|
|
|
system_charset_info);
|
2004-03-30 01:32:41 +02:00
|
|
|
open_for_modify= FALSE;
|
2004-03-26 21:35:45 +01:00
|
|
|
break;
|
|
|
|
|
2000-09-12 02:02:33 +02:00
|
|
|
case HA_ADMIN_ALREADY_DONE:
|
2005-11-20 19:47:07 +01:00
|
|
|
protocol->store(STRING_WITH_LEN("status"), system_charset_info);
|
|
|
|
protocol->store(STRING_WITH_LEN("Table is already up to date"),
|
|
|
|
system_charset_info);
|
2000-08-17 00:05:02 +02:00
|
|
|
break;
|
|
|
|
|
2000-09-12 02:02:33 +02:00
|
|
|
case HA_ADMIN_CORRUPT:
|
2005-11-20 19:47:07 +01:00
|
|
|
protocol->store(STRING_WITH_LEN("error"), system_charset_info);
|
|
|
|
protocol->store(STRING_WITH_LEN("Corrupt"), system_charset_info);
|
2000-10-14 02:16:35 +02:00
|
|
|
fatal_error=1;
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
|
2000-09-15 00:34:50 +02:00
|
|
|
case HA_ADMIN_INVALID:
|
2005-11-20 19:47:07 +01:00
|
|
|
protocol->store(STRING_WITH_LEN("error"), system_charset_info);
|
|
|
|
protocol->store(STRING_WITH_LEN("Invalid argument"),
|
|
|
|
system_charset_info);
|
2000-09-15 00:34:50 +02:00
|
|
|
break;
|
|
|
|
|
2004-06-10 16:41:24 +02:00
|
|
|
case HA_ADMIN_TRY_ALTER:
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
This is currently used only by InnoDB. ha_innobase::optimize() answers
|
|
|
|
"try with alter", so here we close the table, do an ALTER TABLE,
|
|
|
|
reopen the table and do ha_innobase::analyze() on it.
|
|
|
|
*/
|
2006-05-05 19:08:40 +02:00
|
|
|
ha_autocommit_or_rollback(thd, 0);
|
2004-06-10 16:41:24 +02:00
|
|
|
close_thread_tables(thd);
|
2004-07-16 00:15:55 +02:00
|
|
|
TABLE_LIST *save_next_local= table->next_local,
|
|
|
|
*save_next_global= table->next_global;
|
|
|
|
table->next_local= table->next_global= 0;
|
2005-04-11 16:46:03 +02:00
|
|
|
tmp_disable_binlog(thd); // binlogging is done by caller if wanted
|
2004-06-10 16:41:24 +02:00
|
|
|
result_code= mysql_recreate_table(thd, table, 0);
|
2005-04-11 16:46:03 +02:00
|
|
|
reenable_binlog(thd);
|
2006-05-05 19:08:40 +02:00
|
|
|
ha_autocommit_or_rollback(thd, 0);
|
2004-06-10 20:33:15 +02:00
|
|
|
close_thread_tables(thd);
|
2004-06-10 16:41:24 +02:00
|
|
|
if (!result_code) // recreation went ok
|
|
|
|
{
|
|
|
|
if ((table->table= open_ltable(thd, table, lock_type)) &&
|
|
|
|
((result_code= table->table->file->analyze(thd, check_opt)) > 0))
|
|
|
|
result_code= 0; // analyze went ok
|
|
|
|
}
|
2005-05-24 20:11:40 +02:00
|
|
|
if (result_code) // either mysql_recreate_table or analyze failed
|
|
|
|
{
|
|
|
|
const char *err_msg;
|
|
|
|
if ((err_msg= thd->net.last_error))
|
|
|
|
{
|
|
|
|
if (!thd->vio_ok())
|
|
|
|
{
|
|
|
|
sql_print_error(err_msg);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Hijack the row already in-progress. */
|
2005-11-20 19:47:07 +01:00
|
|
|
protocol->store(STRING_WITH_LEN("error"), system_charset_info);
|
2005-05-24 20:11:40 +02:00
|
|
|
protocol->store(err_msg, system_charset_info);
|
|
|
|
(void)protocol->write();
|
|
|
|
/* Start off another row for HA_ADMIN_FAILED */
|
|
|
|
protocol->prepare_for_resend();
|
|
|
|
protocol->store(table_name, system_charset_info);
|
|
|
|
protocol->store(operator_name, system_charset_info);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2004-06-10 16:41:24 +02:00
|
|
|
result_code= result_code ? HA_ADMIN_FAILED : HA_ADMIN_OK;
|
2004-07-16 00:15:55 +02:00
|
|
|
table->next_local= save_next_local;
|
|
|
|
table->next_global= save_next_global;
|
2004-06-10 16:41:24 +02:00
|
|
|
goto send_result_message;
|
|
|
|
}
|
2004-10-28 18:37:25 +02:00
|
|
|
case HA_ADMIN_WRONG_CHECKSUM:
|
|
|
|
{
|
2005-11-20 19:47:07 +01:00
|
|
|
protocol->store(STRING_WITH_LEN("note"), system_charset_info);
|
2004-11-21 15:35:42 +01:00
|
|
|
protocol->store(ER(ER_VIEW_CHECKSUM), strlen(ER(ER_VIEW_CHECKSUM)),
|
|
|
|
system_charset_info);
|
2004-10-28 18:37:25 +02:00
|
|
|
break;
|
|
|
|
}
|
2004-06-10 16:41:24 +02:00
|
|
|
|
2006-02-17 07:52:32 +01:00
|
|
|
case HA_ADMIN_NEEDS_UPGRADE:
|
|
|
|
case HA_ADMIN_NEEDS_ALTER:
|
|
|
|
{
|
|
|
|
char buf[ERRMSGSIZE];
|
|
|
|
uint length;
|
|
|
|
|
|
|
|
protocol->store(STRING_WITH_LEN("error"), system_charset_info);
|
|
|
|
length=my_snprintf(buf, ERRMSGSIZE, ER(ER_TABLE_NEEDS_UPGRADE), table->table_name);
|
|
|
|
protocol->store(buf, length, system_charset_info);
|
|
|
|
fatal_error=1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2000-09-12 02:02:33 +02:00
|
|
|
default: // Probably HA_ADMIN_INTERNAL_ERROR
|
2005-08-24 10:41:44 +02:00
|
|
|
{
|
|
|
|
char buf[ERRMSGSIZE+20];
|
|
|
|
uint length=my_snprintf(buf, ERRMSGSIZE,
|
|
|
|
"Unknown - internal error %d during operation",
|
|
|
|
result_code);
|
2005-11-20 19:47:07 +01:00
|
|
|
protocol->store(STRING_WITH_LEN("error"), system_charset_info);
|
2005-08-24 10:41:44 +02:00
|
|
|
protocol->store(buf, length, system_charset_info);
|
|
|
|
fatal_error=1;
|
|
|
|
break;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2005-11-02 22:44:58 +01:00
|
|
|
if (table->table)
|
2002-04-28 23:33:52 +02:00
|
|
|
{
|
2006-01-19 03:56:06 +01:00
|
|
|
/* in the below check we do not refresh the log tables */
|
2005-11-02 22:44:58 +01:00
|
|
|
if (fatal_error)
|
|
|
|
table->table->s->version=0; // Force close of table
|
2006-01-19 03:56:06 +01:00
|
|
|
else if (open_for_modify && !table->table->s->log_table)
|
2005-11-02 22:44:58 +01:00
|
|
|
{
|
2006-05-03 14:43:32 +02:00
|
|
|
if (table->table->s->tmp_table)
|
2006-05-03 13:33:42 +02:00
|
|
|
table->table->file->info(HA_STATUS_CONST);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pthread_mutex_lock(&LOCK_open);
|
2006-05-04 14:58:30 +02:00
|
|
|
remove_table_from_cache(thd, table->table->s->db.str,
|
|
|
|
table->table->s->table_name.str, RTFC_NO_FLAG);
|
2006-05-03 13:33:42 +02:00
|
|
|
pthread_mutex_unlock(&LOCK_open);
|
|
|
|
}
|
|
|
|
/* May be something modified consequently we have to invalidate cache */
|
2005-11-02 22:44:58 +01:00
|
|
|
query_cache_invalidate3(thd, table->table, 0);
|
|
|
|
}
|
2002-04-28 23:33:52 +02:00
|
|
|
}
|
2006-05-05 19:08:40 +02:00
|
|
|
ha_autocommit_or_rollback(thd, 0);
|
2000-07-31 21:29:14 +02:00
|
|
|
close_thread_tables(thd);
|
2001-12-06 00:05:30 +01:00
|
|
|
table->table=0; // For query cache
|
2002-12-11 08:17:51 +01:00
|
|
|
if (protocol->write())
|
2000-07-31 21:29:14 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2002-10-02 12:33:08 +02:00
|
|
|
send_eof(thd);
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(FALSE);
|
2006-05-05 19:08:40 +02:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
err:
|
2006-05-05 19:08:40 +02:00
|
|
|
ha_autocommit_or_rollback(thd, 1);
|
2000-09-12 02:02:33 +02:00
|
|
|
close_thread_tables(thd); // Shouldn't be needed
|
2001-12-06 00:05:30 +01:00
|
|
|
if (table)
|
|
|
|
table->table=0;
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
2001-12-06 00:05:30 +01:00
|
|
|
|
2004-10-20 03:04:37 +02:00
|
|
|
bool mysql_backup_table(THD* thd, TABLE_LIST* table_list)
|
2000-09-15 00:34:50 +02:00
|
|
|
{
|
|
|
|
DBUG_ENTER("mysql_backup_table");
|
|
|
|
DBUG_RETURN(mysql_admin_table(thd, table_list, 0,
|
2005-02-24 22:33:42 +01:00
|
|
|
"backup", TL_READ, 0, 0, 0, 0,
|
2004-10-28 18:37:25 +02:00
|
|
|
&handler::backup, 0));
|
2000-09-15 00:34:50 +02:00
|
|
|
}
|
2001-03-11 20:20:15 +01:00
|
|
|
|
2002-08-08 02:12:02 +02:00
|
|
|
|
2004-10-20 03:04:37 +02:00
|
|
|
bool mysql_restore_table(THD* thd, TABLE_LIST* table_list)
|
2000-09-15 00:34:50 +02:00
|
|
|
{
|
|
|
|
DBUG_ENTER("mysql_restore_table");
|
|
|
|
DBUG_RETURN(mysql_admin_table(thd, table_list, 0,
|
2005-02-24 22:33:42 +01:00
|
|
|
"restore", TL_WRITE, 1, 1, 0,
|
2004-04-08 16:56:45 +02:00
|
|
|
&prepare_for_restore,
|
2004-10-28 18:37:25 +02:00
|
|
|
&handler::restore, 0));
|
2000-09-15 00:34:50 +02:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2002-08-08 02:12:02 +02:00
|
|
|
|
2004-10-20 03:04:37 +02:00
|
|
|
bool mysql_repair_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
|
2000-09-12 02:02:33 +02:00
|
|
|
{
|
|
|
|
DBUG_ENTER("mysql_repair_table");
|
|
|
|
DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
|
2005-02-24 22:33:42 +01:00
|
|
|
"repair", TL_WRITE, 1,
|
|
|
|
test(check_opt->sql_flags & TT_USEFRM),
|
|
|
|
HA_OPEN_FOR_REPAIR,
|
2004-04-08 16:56:45 +02:00
|
|
|
&prepare_for_repair,
|
2006-02-17 07:52:32 +01:00
|
|
|
&handler::ha_repair, 0));
|
2000-09-12 02:02:33 +02:00
|
|
|
}
|
|
|
|
|
2002-08-08 02:12:02 +02:00
|
|
|
|
2004-10-20 03:04:37 +02:00
|
|
|
bool mysql_optimize_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
|
2000-09-12 02:02:33 +02:00
|
|
|
{
|
|
|
|
DBUG_ENTER("mysql_optimize_table");
|
|
|
|
DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
|
2005-02-24 22:33:42 +01:00
|
|
|
"optimize", TL_WRITE, 1,0,0,0,
|
2004-10-28 18:37:25 +02:00
|
|
|
&handler::optimize, 0));
|
2000-09-12 02:02:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-07-16 21:30:49 +02:00
|
|
|
/*
|
|
|
|
Assigned specified indexes for a table into key cache
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
mysql_assign_to_keycache()
|
2004-04-08 16:56:45 +02:00
|
|
|
thd Thread object
|
|
|
|
tables Table list (one table only)
|
2003-07-16 21:30:49 +02:00
|
|
|
|
|
|
|
RETURN VALUES
|
2004-10-20 03:04:37 +02:00
|
|
|
FALSE ok
|
|
|
|
TRUE error
|
2003-07-16 21:30:49 +02:00
|
|
|
*/
|
|
|
|
|
2004-10-20 03:04:37 +02:00
|
|
|
bool mysql_assign_to_keycache(THD* thd, TABLE_LIST* tables,
|
2003-11-18 12:47:27 +01:00
|
|
|
LEX_STRING *key_cache_name)
|
2003-07-16 21:30:49 +02:00
|
|
|
{
|
2003-11-18 12:47:27 +01:00
|
|
|
HA_CHECK_OPT check_opt;
|
2003-11-20 21:06:25 +01:00
|
|
|
KEY_CACHE *key_cache;
|
2003-07-16 21:30:49 +02:00
|
|
|
DBUG_ENTER("mysql_assign_to_keycache");
|
2003-11-18 12:47:27 +01:00
|
|
|
|
|
|
|
check_opt.init();
|
|
|
|
pthread_mutex_lock(&LOCK_global_system_variables);
|
|
|
|
if (!(key_cache= get_key_cache(key_cache_name)))
|
|
|
|
{
|
|
|
|
pthread_mutex_unlock(&LOCK_global_system_variables);
|
|
|
|
my_error(ER_UNKNOWN_KEY_CACHE, MYF(0), key_cache_name->str);
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
2003-11-18 12:47:27 +01:00
|
|
|
}
|
|
|
|
pthread_mutex_unlock(&LOCK_global_system_variables);
|
|
|
|
check_opt.key_cache= key_cache;
|
|
|
|
DBUG_RETURN(mysql_admin_table(thd, tables, &check_opt,
|
2005-02-24 22:33:42 +01:00
|
|
|
"assign_to_keycache", TL_READ_NO_INSERT, 0, 0,
|
2004-10-28 18:37:25 +02:00
|
|
|
0, 0, &handler::assign_to_keycache, 0));
|
2003-07-16 21:30:49 +02:00
|
|
|
}
|
|
|
|
|
2003-08-02 11:43:18 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
Reassign all tables assigned to a key cache to another key cache
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
reassign_keycache_tables()
|
2004-04-08 16:56:45 +02:00
|
|
|
thd Thread object
|
|
|
|
src_cache Reference to the key cache to clean up
|
|
|
|
dest_cache New key cache
|
2003-08-02 11:43:18 +02:00
|
|
|
|
2003-11-18 12:47:27 +01:00
|
|
|
NOTES
|
|
|
|
This is called when one sets a key cache size to zero, in which
|
|
|
|
case we have to move the tables associated to this key cache to
|
|
|
|
the "default" one.
|
|
|
|
|
|
|
|
One has to ensure that one never calls this function while
|
|
|
|
some other thread is changing the key cache. This is assured by
|
|
|
|
the caller setting src_cache->in_init before calling this function.
|
|
|
|
|
|
|
|
We don't delete the old key cache as there may still be pointers pointing
|
|
|
|
to it for a while after this function returns.
|
|
|
|
|
|
|
|
RETURN VALUES
|
2003-08-02 11:43:18 +02:00
|
|
|
0 ok
|
|
|
|
*/
|
|
|
|
|
2004-04-08 16:56:45 +02:00
|
|
|
int reassign_keycache_tables(THD *thd, KEY_CACHE *src_cache,
|
|
|
|
KEY_CACHE *dst_cache)
|
2003-08-02 11:43:18 +02:00
|
|
|
{
|
|
|
|
DBUG_ENTER("reassign_keycache_tables");
|
|
|
|
|
2003-11-18 12:47:27 +01:00
|
|
|
DBUG_ASSERT(src_cache != dst_cache);
|
|
|
|
DBUG_ASSERT(src_cache->in_init);
|
2003-11-20 21:06:25 +01:00
|
|
|
src_cache->param_buff_size= 0; // Free key cache
|
2003-11-18 12:47:27 +01:00
|
|
|
ha_resize_key_cache(src_cache);
|
|
|
|
ha_change_key_cache(src_cache, dst_cache);
|
2004-04-08 16:56:45 +02:00
|
|
|
DBUG_RETURN(0);
|
2003-08-02 11:43:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-06-12 13:29:02 +02:00
|
|
|
/*
|
|
|
|
Preload specified indexes for a table into key cache
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
mysql_preload_keys()
|
2004-04-08 16:56:45 +02:00
|
|
|
thd Thread object
|
|
|
|
tables Table list (one table only)
|
2003-06-12 13:29:02 +02:00
|
|
|
|
|
|
|
RETURN VALUES
|
2004-10-20 03:04:37 +02:00
|
|
|
FALSE ok
|
|
|
|
TRUE error
|
2003-06-12 13:29:02 +02:00
|
|
|
*/
|
|
|
|
|
2004-10-20 03:04:37 +02:00
|
|
|
bool mysql_preload_keys(THD* thd, TABLE_LIST* tables)
|
2003-06-12 13:29:02 +02:00
|
|
|
{
|
|
|
|
DBUG_ENTER("mysql_preload_keys");
|
|
|
|
DBUG_RETURN(mysql_admin_table(thd, tables, 0,
|
2005-02-24 22:33:42 +01:00
|
|
|
"preload_keys", TL_READ, 0, 0, 0, 0,
|
2004-10-28 18:37:25 +02:00
|
|
|
&handler::preload_keys, 0));
|
2003-06-12 13:29:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-12-28 10:38:29 +01:00
|
|
|
/*
|
|
|
|
Create a table identical to the specified table
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
mysql_create_like_table()
|
2004-04-08 16:56:45 +02:00
|
|
|
thd Thread object
|
|
|
|
table Table list (one table only)
|
2002-12-28 10:38:29 +01:00
|
|
|
create_info Create info
|
|
|
|
table_ident Src table_ident
|
|
|
|
|
|
|
|
RETURN VALUES
|
2004-10-20 03:04:37 +02:00
|
|
|
FALSE OK
|
|
|
|
TRUE error
|
2002-12-28 10:38:29 +01:00
|
|
|
*/
|
|
|
|
|
2004-10-20 03:04:37 +02:00
|
|
|
bool mysql_create_like_table(THD* thd, TABLE_LIST* table,
|
2006-06-21 16:57:30 +02:00
|
|
|
HA_CREATE_INFO *lex_create_info,
|
2004-10-20 03:04:37 +02:00
|
|
|
Table_ident *table_ident)
|
2002-12-28 10:38:29 +01:00
|
|
|
{
|
2005-11-23 21:45:02 +01:00
|
|
|
TABLE *tmp_table;
|
2006-01-17 08:40:00 +01:00
|
|
|
char src_path[FN_REFLEN], dst_path[FN_REFLEN], tmp_path[FN_REFLEN];
|
2005-12-31 06:01:26 +01:00
|
|
|
uint dst_path_length;
|
2002-12-28 10:38:29 +01:00
|
|
|
char *db= table->db;
|
2005-01-06 12:00:13 +01:00
|
|
|
char *table_name= table->table_name;
|
2005-06-09 17:06:15 +02:00
|
|
|
char *src_db;
|
2002-12-28 10:38:29 +01:00
|
|
|
char *src_table= table_ident->table.str;
|
2004-10-20 03:04:37 +02:00
|
|
|
int err;
|
|
|
|
bool res= TRUE;
|
2005-12-21 19:18:40 +01:00
|
|
|
enum legacy_db_type not_used;
|
2006-06-21 16:57:30 +02:00
|
|
|
HA_CREATE_INFO *create_info;
|
2005-11-03 15:10:11 +01:00
|
|
|
|
2004-04-01 20:13:25 +02:00
|
|
|
TABLE_LIST src_tables_list;
|
2002-12-28 10:38:29 +01:00
|
|
|
DBUG_ENTER("mysql_create_like_table");
|
2006-06-21 16:57:30 +02:00
|
|
|
|
|
|
|
if (!(create_info= copy_create_info(lex_create_info)))
|
|
|
|
{
|
|
|
|
DBUG_RETURN(TRUE);
|
|
|
|
}
|
A fix and a test case for
Bug#19022 "Memory bug when switching db during trigger execution"
Bug#17199 "Problem when view calls function from another database."
Bug#18444 "Fully qualified stored function names don't work correctly in
SELECT statements"
Documentation note: this patch introduces a change in behaviour of prepared
statements.
This patch adds a few new invariants with regard to how THD::db should
be used. These invariants should be preserved in future:
- one should never refer to THD::db by pointer and always make a deep copy
(strmake, strdup)
- one should never compare two databases by pointer, but use strncmp or
my_strncasecmp
- TABLE_LIST object table->db should be always initialized in the parser or
by creator of the object.
For prepared statements it means that if the current database is changed
after a statement is prepared, the database that was current at prepare
remains active. This also means that you can not prepare a statement that
implicitly refers to the current database if the latter is not set.
This is not documented, and therefore needs documentation. This is NOT a
change in behavior for almost all SQL statements except:
- ALTER TABLE t1 RENAME t2
- OPTIMIZE TABLE t1
- ANALYZE TABLE t1
- TRUNCATE TABLE t1 --
until this patch t1 or t2 could be evaluated at the first execution of
prepared statement.
CURRENT_DATABASE() still works OK and is evaluated at every execution
of prepared statement.
Note, that in stored routines this is not an issue as the default
database is the database of the stored procedure and "use" statement
is prohibited in stored routines.
This patch makes obsolete the use of check_db_used (it was never used in the
old code too) and all other places that check for table->db and assign it
from THD::db if it's NULL, except the parser.
How this patch was created: THD::{db,db_length} were replaced with a
LEX_STRING, THD::db. All the places that refer to THD::{db,db_length} were
manually checked and:
- if the place uses thd->db by pointer, it was fixed to make a deep copy
- if a place compared two db pointers, it was fixed to compare them by value
(via strcmp/my_strcasecmp, whatever was approproate)
Then this intermediate patch was used to write a smaller patch that does the
same thing but without a rename.
TODO in 5.1:
- remove check_db_used
- deploy THD::set_db in mysql_change_db
See also comments to individual files.
2006-06-26 22:47:52 +02:00
|
|
|
DBUG_ASSERT(table_ident->db.str); /* Must be set in the parser */
|
|
|
|
src_db= table_ident->db.str;
|
2002-12-28 10:38:29 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
Validate the source table
|
|
|
|
*/
|
|
|
|
if (table_ident->table.length > NAME_LEN ||
|
|
|
|
(table_ident->table.length &&
|
2005-06-09 17:06:15 +02:00
|
|
|
check_table_name(src_table,table_ident->table.length)))
|
2002-12-28 10:38:29 +01:00
|
|
|
{
|
2003-11-18 16:28:00 +01:00
|
|
|
my_error(ER_WRONG_TABLE_NAME, MYF(0), src_table);
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
2002-12-28 10:38:29 +01:00
|
|
|
}
|
2005-06-09 17:06:15 +02:00
|
|
|
if (!src_db || check_db_name(src_db))
|
|
|
|
{
|
|
|
|
my_error(ER_WRONG_DB_NAME, MYF(0), src_db ? src_db : "NULL");
|
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
2004-04-08 16:56:45 +02:00
|
|
|
|
2004-07-16 00:15:55 +02:00
|
|
|
bzero((gptr)&src_tables_list, sizeof(src_tables_list));
|
2005-06-09 17:06:15 +02:00
|
|
|
src_tables_list.db= src_db;
|
2005-06-09 18:15:06 +02:00
|
|
|
src_tables_list.table_name= src_table;
|
2004-04-08 16:56:45 +02:00
|
|
|
|
2004-04-01 20:13:25 +02:00
|
|
|
if (lock_and_wait_for_table_name(thd, &src_tables_list))
|
|
|
|
goto err;
|
2002-12-28 10:38:29 +01:00
|
|
|
|
|
|
|
if ((tmp_table= find_temporary_table(thd, src_db, src_table)))
|
2005-11-23 21:45:02 +01:00
|
|
|
strxmov(src_path, tmp_table->s->path.str, reg_ext, NullS);
|
2002-12-28 10:38:29 +01:00
|
|
|
else
|
|
|
|
{
|
2005-12-31 06:01:26 +01:00
|
|
|
build_table_filename(src_path, sizeof(src_path),
|
|
|
|
src_db, src_table, reg_ext);
|
2005-02-02 19:28:01 +01:00
|
|
|
/* Resolve symlinks (for windows) */
|
2005-11-23 21:45:02 +01:00
|
|
|
unpack_filename(src_path, src_path);
|
2005-04-29 00:30:42 +02:00
|
|
|
if (lower_case_table_names)
|
|
|
|
my_casedn_str(files_charset_info, src_path);
|
2002-12-28 10:38:29 +01:00
|
|
|
if (access(src_path, F_OK))
|
|
|
|
{
|
|
|
|
my_error(ER_BAD_TABLE_ERROR, MYF(0), src_table);
|
2004-04-01 20:13:25 +02:00
|
|
|
goto err;
|
2002-12-28 10:38:29 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-08-03 07:29:48 +02:00
|
|
|
/*
|
|
|
|
create like should be not allowed for Views, Triggers, ...
|
|
|
|
*/
|
2005-11-03 15:10:11 +01:00
|
|
|
if (mysql_frm_type(thd, src_path, ¬_used) != FRMTYPE_TABLE)
|
2005-08-03 07:29:48 +02:00
|
|
|
{
|
2005-08-04 02:06:21 +02:00
|
|
|
my_error(ER_WRONG_OBJECT, MYF(0), src_db, src_table, "BASE TABLE");
|
2005-08-03 07:29:48 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2002-12-28 10:38:29 +01:00
|
|
|
/*
|
|
|
|
Validate the destination table
|
|
|
|
|
2004-04-08 16:56:45 +02:00
|
|
|
skip the destination table name checking as this is already
|
2002-12-28 10:38:29 +01:00
|
|
|
validated.
|
|
|
|
*/
|
|
|
|
if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
|
|
|
|
{
|
|
|
|
if (find_temporary_table(thd, db, table_name))
|
|
|
|
goto table_exists;
|
2006-01-23 13:28:42 +01:00
|
|
|
dst_path_length= build_tmptable_filename(thd, dst_path, sizeof(dst_path));
|
2004-05-25 00:30:09 +02:00
|
|
|
if (lower_case_table_names)
|
|
|
|
my_casedn_str(files_charset_info, dst_path);
|
2002-12-28 10:38:29 +01:00
|
|
|
create_info->table_options|= HA_CREATE_DELAY_KEY_WRITE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2005-12-31 06:01:26 +01:00
|
|
|
dst_path_length= build_table_filename(dst_path, sizeof(dst_path),
|
|
|
|
db, table_name, reg_ext);
|
2002-12-28 10:38:29 +01:00
|
|
|
if (!access(dst_path, F_OK))
|
|
|
|
goto table_exists;
|
|
|
|
}
|
|
|
|
|
2004-04-08 16:56:45 +02:00
|
|
|
/*
|
2002-12-28 10:38:29 +01:00
|
|
|
Create a new table by copying from source table
|
2004-04-08 16:56:45 +02:00
|
|
|
*/
|
2005-07-22 05:08:54 +02:00
|
|
|
if (my_copy(src_path, dst_path, MYF(MY_DONT_OVERWRITE_FILE)))
|
|
|
|
{
|
|
|
|
if (my_errno == ENOENT)
|
|
|
|
my_error(ER_BAD_DB_ERROR,MYF(0),db);
|
|
|
|
else
|
|
|
|
my_error(ER_CANT_CREATE_FILE,MYF(0),dst_path,my_errno);
|
2004-04-01 20:13:25 +02:00
|
|
|
goto err;
|
2005-07-22 05:08:54 +02:00
|
|
|
}
|
2002-12-28 10:38:29 +01:00
|
|
|
|
|
|
|
/*
|
2004-04-08 16:56:45 +02:00
|
|
|
As mysql_truncate don't work on a new table at this stage of
|
|
|
|
creation, instead create the table directly (for both normal
|
2002-12-28 10:38:29 +01:00
|
|
|
and temporary tables).
|
|
|
|
*/
|
2006-01-17 08:40:00 +01:00
|
|
|
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
|
|
|
/*
|
|
|
|
For partitioned tables we need to copy the .par file as well since
|
|
|
|
it is used in open_table_def to even be able to create a new handler.
|
|
|
|
There is no way to find out here if the original table is a
|
|
|
|
partitioned table so we copy the file and ignore any errors.
|
|
|
|
*/
|
|
|
|
fn_format(tmp_path, dst_path, reg_ext, ".par", MYF(MY_REPLACE_EXT));
|
|
|
|
strmov(dst_path, tmp_path);
|
|
|
|
fn_format(tmp_path, src_path, reg_ext, ".par", MYF(MY_REPLACE_EXT));
|
|
|
|
strmov(src_path, tmp_path);
|
|
|
|
my_copy(src_path, dst_path, MYF(MY_DONT_OVERWRITE_FILE));
|
|
|
|
#endif
|
2005-12-31 06:01:26 +01:00
|
|
|
dst_path[dst_path_length - reg_ext_length]= '\0'; // Remove .frm
|
2005-11-23 21:45:02 +01:00
|
|
|
err= ha_create_table(thd, dst_path, db, table_name, create_info, 1);
|
2004-04-08 16:56:45 +02:00
|
|
|
|
2002-12-28 10:38:29 +01:00
|
|
|
if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
|
|
|
|
{
|
|
|
|
if (err || !open_temporary_table(thd, dst_path, db, table_name, 1))
|
|
|
|
{
|
2004-04-08 16:56:45 +02:00
|
|
|
(void) rm_temporary_table(create_info->db_type,
|
|
|
|
dst_path); /* purecov: inspected */
|
2004-04-01 20:13:25 +02:00
|
|
|
goto err; /* purecov: inspected */
|
2002-12-28 10:38:29 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (err)
|
|
|
|
{
|
2004-04-08 16:56:45 +02:00
|
|
|
(void) quick_rm_table(create_info->db_type, db,
|
|
|
|
table_name); /* purecov: inspected */
|
|
|
|
goto err; /* purecov: inspected */
|
2002-12-28 10:38:29 +01:00
|
|
|
}
|
2004-02-17 00:35:17 +01:00
|
|
|
|
2005-12-22 06:39:02 +01:00
|
|
|
/*
|
|
|
|
We have to write the query before we unlock the tables.
|
|
|
|
*/
|
WL#2977 and WL#2712 global and session-level variable to set the binlog format (row/statement),
and new binlog format called "mixed" (which is statement-based except if only row-based is correct,
in this cset it means if UDF or UUID is used; more cases could be added in later 5.1 release):
SET GLOBAL|SESSION BINLOG_FORMAT=row|statement|mixed|default;
the global default is statement unless cluster is enabled (then it's row) as in 5.1-alpha.
It's not possible to use SET on this variable if a session is currently in row-based mode and has open temporary tables (because CREATE
TEMPORARY TABLE was not binlogged so temp table is not known on slave), or if NDB is enabled (because
NDB does not support such change on-the-fly, though it will later), of if in a stored function (see below).
The added tests test the possibility or impossibility to SET, their effects, and the mixed mode,
including in prepared statements and in stored procedures and functions.
Caveats:
a) The mixed mode will not work for stored functions: in mixed mode, a stored function will
always be binlogged as one call and in a statement-based way (e.g. INSERT VALUES(myfunc()) or SELECT myfunc()).
b) for the same reason, changing the thread's binlog format inside a stored function is
refused with an error message.
c) the same problems apply to triggers; implementing b) for triggers will be done later (will ask
Dmitri).
Additionally, as the binlog format is now changeable by each user for his session, I remove the implication
which was done at startup, where row-based automatically set log-bin-trust-routine-creators to 1
(not possible anymore as a user can now switch to stmt-based and do nasty things again), and automatically
set --innodb-locks-unsafe-for-binlog to 1 (was anyway theoretically incorrect as it disabled
phantom protection).
Plus fixes for compiler warnings.
2006-02-25 22:21:03 +01:00
|
|
|
if (thd->current_stmt_binlog_row_based)
|
2005-12-22 06:39:02 +01:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
Since temporary tables are not replicated under row-based
|
|
|
|
replication, CREATE TABLE ... LIKE ... needs special
|
|
|
|
treatement. We have four cases to consider, according to the
|
|
|
|
following decision table:
|
|
|
|
|
|
|
|
==== ========= ========= ==============================
|
|
|
|
Case Target Source Write to binary log
|
|
|
|
==== ========= ========= ==============================
|
|
|
|
1 normal normal Original statement
|
|
|
|
2 normal temporary Generated statement
|
|
|
|
3 temporary normal Nothing
|
|
|
|
4 temporary temporary Nothing
|
|
|
|
==== ========= ========= ==============================
|
|
|
|
|
|
|
|
The variable 'tmp_table' below is used to see if the source
|
|
|
|
table is a temporary table: if it is set, then the source table
|
|
|
|
was a temporary table and we can take apropriate actions.
|
|
|
|
*/
|
|
|
|
if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE))
|
|
|
|
{
|
|
|
|
if (tmp_table) // Case 2
|
|
|
|
{
|
|
|
|
char buf[2048];
|
|
|
|
String query(buf, sizeof(buf), system_charset_info);
|
|
|
|
query.length(0); // Have to zero it since constructor doesn't
|
|
|
|
TABLE *table_ptr;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
/*
|
|
|
|
Let's open and lock the table: it will be closed (and
|
|
|
|
unlocked) by close_thread_tables() at the end of the
|
|
|
|
statement anyway.
|
|
|
|
*/
|
|
|
|
if (!(table_ptr= open_ltable(thd, table, TL_READ_NO_INSERT)))
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
int result= store_create_info(thd, table, &query, create_info);
|
|
|
|
|
|
|
|
DBUG_ASSERT(result == 0); // store_create_info() always return 0
|
|
|
|
write_bin_log(thd, TRUE, query.ptr(), query.length());
|
|
|
|
}
|
|
|
|
else // Case 1
|
|
|
|
write_bin_log(thd, TRUE, thd->query, thd->query_length);
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
Case 3 and 4 does nothing under RBR
|
|
|
|
*/
|
|
|
|
}
|
2005-12-22 11:09:50 +01:00
|
|
|
else
|
2005-12-22 06:39:02 +01:00
|
|
|
write_bin_log(thd, TRUE, thd->query, thd->query_length);
|
|
|
|
|
2004-10-20 03:04:37 +02:00
|
|
|
res= FALSE;
|
2004-04-02 23:44:38 +02:00
|
|
|
goto err;
|
2004-04-08 16:56:45 +02:00
|
|
|
|
2002-12-28 10:38:29 +01:00
|
|
|
table_exists:
|
|
|
|
if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
|
|
|
|
{
|
|
|
|
char warn_buff[MYSQL_ERRMSG_SIZE];
|
2004-04-08 16:56:45 +02:00
|
|
|
my_snprintf(warn_buff, sizeof(warn_buff),
|
|
|
|
ER(ER_TABLE_EXISTS_ERROR), table_name);
|
2004-09-09 05:59:26 +02:00
|
|
|
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
|
2004-04-08 16:56:45 +02:00
|
|
|
ER_TABLE_EXISTS_ERROR,warn_buff);
|
2004-10-20 03:04:37 +02:00
|
|
|
res= FALSE;
|
2002-12-28 10:38:29 +01:00
|
|
|
}
|
2004-04-02 23:44:38 +02:00
|
|
|
else
|
|
|
|
my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name);
|
|
|
|
|
|
|
|
err:
|
|
|
|
pthread_mutex_lock(&LOCK_open);
|
|
|
|
unlock_table_name(thd, &src_tables_list);
|
|
|
|
pthread_mutex_unlock(&LOCK_open);
|
|
|
|
DBUG_RETURN(res);
|
2002-12-28 10:38:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-10-20 03:04:37 +02:00
|
|
|
bool mysql_analyze_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
|
2000-09-12 02:02:33 +02:00
|
|
|
{
|
2001-08-22 00:45:07 +02:00
|
|
|
thr_lock_type lock_type = TL_READ_NO_INSERT;
|
|
|
|
|
2000-09-12 02:02:33 +02:00
|
|
|
DBUG_ENTER("mysql_analyze_table");
|
|
|
|
DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
|
2005-02-24 22:33:42 +01:00
|
|
|
"analyze", lock_type, 1, 0, 0, 0,
|
2004-10-28 18:37:25 +02:00
|
|
|
&handler::analyze, 0));
|
2000-09-12 02:02:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-10-20 03:04:37 +02:00
|
|
|
bool mysql_check_table(THD* thd, TABLE_LIST* tables,HA_CHECK_OPT* check_opt)
|
2000-09-12 02:02:33 +02:00
|
|
|
{
|
2001-08-22 00:45:07 +02:00
|
|
|
thr_lock_type lock_type = TL_READ_NO_INSERT;
|
|
|
|
|
2000-09-12 02:02:33 +02:00
|
|
|
DBUG_ENTER("mysql_check_table");
|
|
|
|
DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
|
2001-08-22 00:45:07 +02:00
|
|
|
"check", lock_type,
|
2005-02-24 22:33:42 +01:00
|
|
|
0, HA_OPEN_FOR_REPAIR, 0, 0,
|
2006-02-17 07:52:32 +01:00
|
|
|
&handler::ha_check, &view_checksum));
|
2000-09-12 02:02:33 +02:00
|
|
|
}
|
|
|
|
|
2004-10-07 09:50:13 +02:00
|
|
|
|
2003-10-13 10:20:19 +02:00
|
|
|
/* table_list should contain just one table */
|
2004-10-07 09:50:13 +02:00
|
|
|
static int
|
|
|
|
mysql_discard_or_import_tablespace(THD *thd,
|
|
|
|
TABLE_LIST *table_list,
|
|
|
|
enum tablespace_op_type tablespace_op)
|
2003-10-13 10:20:19 +02:00
|
|
|
{
|
|
|
|
TABLE *table;
|
|
|
|
my_bool discard;
|
|
|
|
int error;
|
|
|
|
DBUG_ENTER("mysql_discard_or_import_tablespace");
|
|
|
|
|
2004-10-07 09:50:13 +02:00
|
|
|
/*
|
|
|
|
Note that DISCARD/IMPORT TABLESPACE always is the only operation in an
|
|
|
|
ALTER TABLE
|
|
|
|
*/
|
2003-10-13 10:20:19 +02:00
|
|
|
|
|
|
|
thd->proc_info="discard_or_import_tablespace";
|
|
|
|
|
2004-10-07 09:50:13 +02:00
|
|
|
discard= test(tablespace_op == DISCARD_TABLESPACE);
|
2003-10-13 10:20:19 +02:00
|
|
|
|
2004-10-07 09:50:13 +02:00
|
|
|
/*
|
|
|
|
We set this flag so that ha_innobase::open and ::external_lock() do
|
|
|
|
not complain when we lock the table
|
|
|
|
*/
|
|
|
|
thd->tablespace_op= TRUE;
|
2003-10-13 10:20:19 +02:00
|
|
|
if (!(table=open_ltable(thd,table_list,TL_WRITE)))
|
|
|
|
{
|
|
|
|
thd->tablespace_op=FALSE;
|
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
2004-04-08 16:56:45 +02:00
|
|
|
|
2003-10-13 10:20:19 +02:00
|
|
|
error=table->file->discard_or_import_tablespace(discard);
|
|
|
|
|
|
|
|
thd->proc_info="end";
|
|
|
|
|
|
|
|
if (error)
|
|
|
|
goto err;
|
|
|
|
|
2004-10-07 09:50:13 +02:00
|
|
|
/*
|
|
|
|
The 0 in the call below means 'not in a transaction', which means
|
|
|
|
immediate invalidation; that is probably what we wish here
|
|
|
|
*/
|
2003-10-13 10:20:19 +02:00
|
|
|
query_cache_invalidate3(thd, table_list, 0);
|
|
|
|
|
|
|
|
/* The ALTER TABLE is always in its own transaction */
|
|
|
|
error = ha_commit_stmt(thd);
|
|
|
|
if (ha_commit(thd))
|
|
|
|
error=1;
|
|
|
|
if (error)
|
|
|
|
goto err;
|
2005-12-22 06:39:02 +01:00
|
|
|
write_bin_log(thd, FALSE, thd->query, thd->query_length);
|
2006-05-05 19:08:40 +02:00
|
|
|
|
2003-10-13 10:20:19 +02:00
|
|
|
err:
|
2006-05-05 19:08:40 +02:00
|
|
|
ha_autocommit_or_rollback(thd, error);
|
2003-10-13 10:20:19 +02:00
|
|
|
close_thread_tables(thd);
|
2003-10-14 00:52:03 +02:00
|
|
|
thd->tablespace_op=FALSE;
|
2005-08-29 21:00:43 +02:00
|
|
|
|
2004-03-30 01:32:41 +02:00
|
|
|
if (error == 0)
|
|
|
|
{
|
2003-10-13 10:20:19 +02:00
|
|
|
send_ok(thd);
|
2004-03-30 01:32:41 +02:00
|
|
|
DBUG_RETURN(0);
|
2003-10-13 10:20:19 +02:00
|
|
|
}
|
2004-12-27 04:03:11 +01:00
|
|
|
|
2005-08-29 21:00:43 +02:00
|
|
|
table->file->print_error(error, MYF(0));
|
|
|
|
|
2004-12-27 04:03:11 +01:00
|
|
|
DBUG_RETURN(-1);
|
2003-10-13 10:20:19 +02:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2004-04-08 16:56:45 +02:00
|
|
|
|
2005-07-22 22:43:59 +02:00
|
|
|
/*
|
|
|
|
SYNOPSIS
|
2006-01-12 10:05:07 +01:00
|
|
|
compare_tables()
|
|
|
|
table The original table.
|
|
|
|
create_list The fields for the new table.
|
|
|
|
key_info_buffer An array of KEY structs for the new indexes.
|
|
|
|
key_count The number of elements in the array.
|
|
|
|
create_info Create options for the new table.
|
|
|
|
alter_info Alter options.
|
|
|
|
order_num Number of order list elements.
|
|
|
|
index_drop_buffer OUT An array of offsets into table->key_info.
|
|
|
|
index_drop_count OUT The number of elements in the array.
|
|
|
|
index_add_buffer OUT An array of offsets into key_info_buffer.
|
|
|
|
index_add_count OUT The number of elements in the array.
|
2005-07-22 22:43:59 +02:00
|
|
|
|
|
|
|
DESCRIPTION
|
|
|
|
'table' (first argument) contains information of the original
|
|
|
|
table, which includes all corresponding parts that the new
|
|
|
|
table has in arguments create_list, key_list and create_info.
|
|
|
|
|
|
|
|
By comparing the changes between the original and new table
|
|
|
|
we can determine how much it has changed after ALTER TABLE
|
|
|
|
and whether we need to make a copy of the table, or just change
|
|
|
|
the .frm file.
|
|
|
|
|
2006-01-12 10:05:07 +01:00
|
|
|
If there are no data changes, but index changes, 'index_drop_buffer'
|
|
|
|
and/or 'index_add_buffer' are populated with offsets into
|
|
|
|
table->key_info or key_info_buffer respectively for the indexes
|
|
|
|
that need to be dropped and/or (re-)created.
|
|
|
|
|
2005-07-22 22:43:59 +02:00
|
|
|
RETURN VALUES
|
2006-01-12 10:05:07 +01:00
|
|
|
0 No copy needed
|
|
|
|
ALTER_TABLE_DATA_CHANGED Data changes, copy needed
|
|
|
|
ALTER_TABLE_INDEX_CHANGED Index changes, copy might be needed
|
2005-07-22 22:43:59 +02:00
|
|
|
*/
|
|
|
|
|
2006-01-12 10:05:07 +01:00
|
|
|
static uint compare_tables(TABLE *table, List<create_field> *create_list,
|
|
|
|
KEY *key_info_buffer, uint key_count,
|
|
|
|
HA_CREATE_INFO *create_info,
|
|
|
|
ALTER_INFO *alter_info, uint order_num,
|
|
|
|
uint *index_drop_buffer, uint *index_drop_count,
|
2006-05-30 15:07:49 +02:00
|
|
|
uint *index_add_buffer, uint *index_add_count,
|
|
|
|
bool varchar)
|
2005-07-22 22:43:59 +02:00
|
|
|
{
|
|
|
|
Field **f_ptr, *field;
|
|
|
|
uint changes= 0, tmp;
|
|
|
|
List_iterator_fast<create_field> new_field_it(*create_list);
|
|
|
|
create_field *new_field;
|
2006-03-06 10:24:06 +01:00
|
|
|
KEY_PART_INFO *key_part;
|
|
|
|
KEY_PART_INFO *end;
|
2006-01-12 10:05:07 +01:00
|
|
|
DBUG_ENTER("compare_tables");
|
2005-07-22 22:43:59 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
Some very basic checks. If number of fields changes, or the
|
|
|
|
handler, we need to run full ALTER TABLE. In the future
|
|
|
|
new fields can be added and old dropped without copy, but
|
|
|
|
not yet.
|
|
|
|
|
|
|
|
Test also that engine was not given during ALTER TABLE, or
|
|
|
|
we are force to run regular alter table (copy).
|
|
|
|
E.g. ALTER TABLE tbl_name ENGINE=MyISAM.
|
|
|
|
|
|
|
|
For the following ones we also want to run regular alter table:
|
|
|
|
ALTER TABLE tbl_name ORDER BY ..
|
|
|
|
ALTER TABLE tbl_name CONVERT TO CHARACTER SET ..
|
|
|
|
|
|
|
|
At the moment we can't handle altering temporary tables without a copy.
|
|
|
|
We also test if OPTIMIZE TABLE was given and was mapped to alter table.
|
|
|
|
In that case we always do full copy.
|
2006-06-14 13:45:29 +02:00
|
|
|
|
|
|
|
There was a bug prior to mysql-4.0.25. Number of null fields was
|
|
|
|
calculated incorrectly. As a result frm and data files gets out of
|
|
|
|
sync after fast alter table. There is no way to determine by which
|
|
|
|
mysql version (in 4.0 and 4.1 branches) table was created, thus we
|
|
|
|
disable fast alter table for all tables created by mysql versions
|
|
|
|
prior to 5.0 branch.
|
|
|
|
See BUG#6236.
|
2005-07-22 22:43:59 +02:00
|
|
|
*/
|
|
|
|
if (table->s->fields != create_list->elements ||
|
|
|
|
table->s->db_type != create_info->db_type ||
|
|
|
|
table->s->tmp_table ||
|
|
|
|
create_info->used_fields & HA_CREATE_USED_ENGINE ||
|
|
|
|
create_info->used_fields & HA_CREATE_USED_CHARSET ||
|
|
|
|
create_info->used_fields & HA_CREATE_USED_DEFAULT_CHARSET ||
|
2006-03-30 20:55:54 +02:00
|
|
|
(alter_info->flags & (ALTER_RECREATE | ALTER_FOREIGN_KEY)) ||
|
2006-05-30 15:07:49 +02:00
|
|
|
order_num ||
|
2006-06-14 13:45:29 +02:00
|
|
|
!table->s->mysql_version ||
|
2006-05-30 15:07:49 +02:00
|
|
|
(table->s->frm_version < FRM_VER_TRUE_VARCHAR && varchar))
|
2006-01-12 10:05:07 +01:00
|
|
|
DBUG_RETURN(ALTER_TABLE_DATA_CHANGED);
|
2005-07-22 22:43:59 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
Go through fields and check if the original ones are compatible
|
|
|
|
with new table.
|
|
|
|
*/
|
|
|
|
for (f_ptr= table->field, new_field= new_field_it++;
|
|
|
|
(field= *f_ptr); f_ptr++, new_field= new_field_it++)
|
|
|
|
{
|
|
|
|
/* Make sure we have at least the default charset in use. */
|
|
|
|
if (!new_field->charset)
|
|
|
|
new_field->charset= create_info->default_table_charset;
|
2006-01-12 10:05:07 +01:00
|
|
|
|
2005-07-22 22:43:59 +02:00
|
|
|
/* Check that NULL behavior is same for old and new fields */
|
|
|
|
if ((new_field->flags & NOT_NULL_FLAG) !=
|
|
|
|
(uint) (field->flags & NOT_NULL_FLAG))
|
2006-01-12 10:05:07 +01:00
|
|
|
DBUG_RETURN(ALTER_TABLE_DATA_CHANGED);
|
2005-07-22 22:43:59 +02:00
|
|
|
|
|
|
|
/* Don't pack rows in old tables if the user has requested this. */
|
|
|
|
if (create_info->row_type == ROW_TYPE_DYNAMIC ||
|
|
|
|
(new_field->flags & BLOB_FLAG) ||
|
|
|
|
new_field->sql_type == MYSQL_TYPE_VARCHAR &&
|
|
|
|
create_info->row_type != ROW_TYPE_FIXED)
|
|
|
|
create_info->table_options|= HA_OPTION_PACK_RECORD;
|
|
|
|
|
2006-06-14 11:08:36 +02:00
|
|
|
/* Check if field was renamed */
|
2006-06-14 12:01:06 +02:00
|
|
|
field->flags&= ~FIELD_IS_RENAMED;
|
2006-06-14 11:08:36 +02:00
|
|
|
if (my_strcasecmp(system_charset_info,
|
|
|
|
field->field_name,
|
|
|
|
new_field->field_name))
|
|
|
|
field->flags|= FIELD_IS_RENAMED;
|
2006-06-14 12:01:06 +02:00
|
|
|
|
2005-07-22 22:43:59 +02:00
|
|
|
/* Evaluate changes bitmap and send to check_if_incompatible_data() */
|
|
|
|
if (!(tmp= field->is_equal(new_field)))
|
2006-01-12 10:05:07 +01:00
|
|
|
DBUG_RETURN(ALTER_TABLE_DATA_CHANGED);
|
2006-01-27 17:23:14 +01:00
|
|
|
// Clear indexed marker
|
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes
Changes that requires code changes in other code of other storage engines.
(Note that all changes are very straightforward and one should find all issues
by compiling a --debug build and fixing all compiler errors and all
asserts in field.cc while running the test suite),
- New optional handler function introduced: reset()
This is called after every DML statement to make it easy for a handler to
statement specific cleanups.
(The only case it's not called is if force the file to be closed)
- handler::extra(HA_EXTRA_RESET) is removed. Code that was there before
should be moved to handler::reset()
- table->read_set contains a bitmap over all columns that are needed
in the query. read_row() and similar functions only needs to read these
columns
- table->write_set contains a bitmap over all columns that will be updated
in the query. write_row() and update_row() only needs to update these
columns.
The above bitmaps should now be up to date in all context
(including ALTER TABLE, filesort()).
The handler is informed of any changes to the bitmap after
fix_fields() by calling the virtual function
handler::column_bitmaps_signal(). If the handler does caching of
these bitmaps (instead of using table->read_set, table->write_set),
it should redo the caching in this code. as the signal() may be sent
several times, it's probably best to set a variable in the signal
and redo the caching on read_row() / write_row() if the variable was
set.
- Removed the read_set and write_set bitmap objects from the handler class
- Removed all column bit handling functions from the handler class.
(Now one instead uses the normal bitmap functions in my_bitmap.c instead
of handler dedicated bitmap functions)
- field->query_id is removed. One should instead instead check
table->read_set and table->write_set if a field is used in the query.
- handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and
handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now
instead use table->read_set to check for which columns to retrieve.
- If a handler needs to call Field->val() or Field->store() on columns
that are not used in the query, one should install a temporary
all-columns-used map while doing so. For this, we provide the following
functions:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
field->val();
dbug_tmp_restore_column_map(table->read_set, old_map);
and similar for the write map:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
field->val();
dbug_tmp_restore_column_map(table->write_set, old_map);
If this is not done, you will sooner or later hit a DBUG_ASSERT
in the field store() / val() functions.
(For not DBUG binaries, the dbug_tmp_restore_column_map() and
dbug_tmp_restore_column_map() are inline dummy functions and should
be optimized away be the compiler).
- If one needs to temporary set the column map for all binaries (and not
just to avoid the DBUG_ASSERT() in the Field::store() / Field::val()
methods) one should use the functions tmp_use_all_columns() and
tmp_restore_column_map() instead of the above dbug_ variants.
- All 'status' fields in the handler base class (like records,
data_file_length etc) are now stored in a 'stats' struct. This makes
it easier to know what status variables are provided by the base
handler. This requires some trivial variable names in the extra()
function.
- New virtual function handler::records(). This is called to optimize
COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true.
(stats.records is not supposed to be an exact value. It's only has to
be 'reasonable enough' for the optimizer to be able to choose a good
optimization path).
- Non virtual handler::init() function added for caching of virtual
constants from engine.
- Removed has_transactions() virtual method. Now one should instead return
HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support
transactions.
- The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument
that is to be used with 'new handler_name()' to allocate the handler
in the right area. The xxxx_create_handler() function is also
responsible for any initialization of the object before returning.
For example, one should change:
static handler *myisam_create_handler(TABLE_SHARE *table)
{
return new ha_myisam(table);
}
->
static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root)
{
return new (mem_root) ha_myisam(table);
}
- New optional virtual function: use_hidden_primary_key().
This is called in case of an update/delete when
(table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
but we don't have a primary key. This allows the handler to take precisions
in remembering any hidden primary key to able to update/delete any
found row. The default handler marks all columns to be read.
- handler::table_flags() now returns a ulonglong (to allow for more flags).
- New/changed table_flags()
- HA_HAS_RECORDS Set if ::records() is supported
- HA_NO_TRANSACTIONS Set if engine doesn't support transactions
- HA_PRIMARY_KEY_REQUIRED_FOR_DELETE
Set if we should mark all primary key columns for
read when reading rows as part of a DELETE
statement. If there is no primary key,
all columns are marked for read.
- HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some
cases (based on table->read_set)
- HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS
Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION.
- HA_DUPP_POS Renamed to HA_DUPLICATE_POS
- HA_REQUIRES_KEY_COLUMNS_FOR_DELETE
Set this if we should mark ALL key columns for
read when when reading rows as part of a DELETE
statement. In case of an update we will mark
all keys for read for which key part changed
value.
- HA_STATS_RECORDS_IS_EXACT
Set this if stats.records is exact.
(This saves us some extra records() calls
when optimizing COUNT(*))
- Removed table_flags()
- HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if
handler::records() gives an exact count() and
HA_STATS_RECORDS_IS_EXACT if stats.records is exact.
- HA_READ_RND_SAME Removed (no one supported this one)
- Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk()
- Renamed handler::dupp_pos to handler::dup_pos
- Removed not used variable handler::sortkey
Upper level handler changes:
- ha_reset() now does some overall checks and calls ::reset()
- ha_table_flags() added. This is a cached version of table_flags(). The
cache is updated on engine creation time and updated on open.
MySQL level changes (not obvious from the above):
- DBUG_ASSERT() added to check that column usage matches what is set
in the column usage bit maps. (This found a LOT of bugs in current
column marking code).
- In 5.1 before, all used columns was marked in read_set and only updated
columns was marked in write_set. Now we only mark columns for which we
need a value in read_set.
- Column bitmaps are created in open_binary_frm() and open_table_from_share().
(Before this was in table.cc)
- handler::table_flags() calls are replaced with handler::ha_table_flags()
- For calling field->val() you must have the corresponding bit set in
table->read_set. For calling field->store() you must have the
corresponding bit set in table->write_set. (There are asserts in
all store()/val() functions to catch wrong usage)
- thd->set_query_id is renamed to thd->mark_used_columns and instead
of setting this to an integer value, this has now the values:
MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE
Changed also all variables named 'set_query_id' to mark_used_columns.
- In filesort() we now inform the handler of exactly which columns are needed
doing the sort and choosing the rows.
- The TABLE_SHARE object has a 'all_set' column bitmap one can use
when one needs a column bitmap with all columns set.
(This is used for table->use_all_columns() and other places)
- The TABLE object has 3 column bitmaps:
- def_read_set Default bitmap for columns to be read
- def_write_set Default bitmap for columns to be written
- tmp_set Can be used as a temporary bitmap when needed.
The table object has also two pointer to bitmaps read_set and write_set
that the handler should use to find out which columns are used in which way.
- count() optimization now calls handler::records() instead of using
handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true).
- Added extra argument to Item::walk() to indicate if we should also
traverse sub queries.
- Added TABLE parameter to cp_buffer_from_ref()
- Don't close tables created with CREATE ... SELECT but keep them in
the table cache. (Faster usage of newly created tables).
New interfaces:
- table->clear_column_bitmaps() to initialize the bitmaps for tables
at start of new statements.
- table->column_bitmaps_set() to set up new column bitmaps and signal
the handler about this.
- table->column_bitmaps_set_no_signal() for some few cases where we need
to setup new column bitmaps but don't signal the handler (as the handler
has already been signaled about these before). Used for the momement
only in opt_range.cc when doing ROR scans.
- table->use_all_columns() to install a bitmap where all columns are marked
as use in the read and the write set.
- table->default_column_bitmaps() to install the normal read and write
column bitmaps, but not signaling the handler about this.
This is mainly used when creating TABLE instances.
- table->mark_columns_needed_for_delete(),
table->mark_columns_needed_for_delete() and
table->mark_columns_needed_for_insert() to allow us to put additional
columns in column usage maps if handler so requires.
(The handler indicates what it neads in handler->table_flags())
- table->prepare_for_position() to allow us to tell handler that it
needs to read primary key parts to be able to store them in
future table->position() calls.
(This replaces the table->file->ha_retrieve_all_pk function)
- table->mark_auto_increment_column() to tell handler are going to update
columns part of any auto_increment key.
- table->mark_columns_used_by_index() to mark all columns that is part of
an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow
it to quickly know that it only needs to read colums that are part
of the key. (The handler can also use the column map for detecting this,
but simpler/faster handler can just monitor the extra() call).
- table->mark_columns_used_by_index_no_reset() to in addition to other columns,
also mark all columns that is used by the given key.
- table->restore_column_maps_after_mark_index() to restore to default
column maps after a call to table->mark_columns_used_by_index().
- New item function register_field_in_read_map(), for marking used columns
in table->read_map. Used by filesort() to mark all used columns
- Maintain in TABLE->merge_keys set of all keys that are used in query.
(Simplices some optimization loops)
- Maintain Field->part_of_key_not_clustered which is like Field->part_of_key
but the field in the clustered key is not assumed to be part of all index.
(used in opt_range.cc for faster loops)
- dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map()
tmp_use_all_columns() and tmp_restore_column_map() functions to temporally
mark all columns as usable. The 'dbug_' version is primarily intended
inside a handler when it wants to just call Field:store() & Field::val()
functions, but don't need the column maps set for any other usage.
(ie:: bitmap_is_set() is never called)
- We can't use compare_records() to skip updates for handlers that returns
a partial column set and the read_set doesn't cover all columns in the
write set. The reason for this is that if we have a column marked only for
write we can't in the MySQL level know if the value changed or not.
The reason this worked before was that MySQL marked all to be written
columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden
bug'.
- open_table_from_share() does not anymore setup temporary MEM_ROOT
object as a thread specific variable for the handler. Instead we
send the to-be-used MEMROOT to get_new_handler().
(Simpler, faster code)
Bugs fixed:
- Column marking was not done correctly in a lot of cases.
(ALTER TABLE, when using triggers, auto_increment fields etc)
(Could potentially result in wrong values inserted in table handlers
relying on that the old column maps or field->set_query_id was correct)
Especially when it comes to triggers, there may be cases where the
old code would cause lost/wrong values for NDB and/or InnoDB tables.
- Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags:
OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG.
This allowed me to remove some wrong warnings about:
"Some non-transactional changed tables couldn't be rolled back"
- Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset
(thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose
some warnings about
"Some non-transactional changed tables couldn't be rolled back")
- Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table()
which could cause delete_table to report random failures.
- Fixed core dumps for some tests when running with --debug
- Added missing FN_LIBCHAR in mysql_rm_tmp_tables()
(This has probably caused us to not properly remove temporary files after
crash)
- slow_logs was not properly initialized, which could maybe cause
extra/lost entries in slow log.
- If we get an duplicate row on insert, change column map to read and
write all columns while retrying the operation. This is required by
the definition of REPLACE and also ensures that fields that are only
part of UPDATE are properly handled. This fixed a bug in NDB and
REPLACE where REPLACE wrongly copied some column values from the replaced
row.
- For table handler that doesn't support NULL in keys, we would give an error
when creating a primary key with NULL fields, even after the fields has been
automaticly converted to NOT NULL.
- Creating a primary key on a SPATIAL key, would fail if field was not
declared as NOT NULL.
Cleanups:
- Removed not used condition argument to setup_tables
- Removed not needed item function reset_query_id_processor().
- Field->add_index is removed. Now this is instead maintained in
(field->flags & FIELD_IN_ADD_INDEX)
- Field->fieldnr is removed (use field->field_index instead)
- New argument to filesort() to indicate that it should return a set of
row pointers (not used columns). This allowed me to remove some references
to sql_command in filesort and should also enable us to return column
results in some cases where we couldn't before.
- Changed column bitmap handling in opt_range.cc to be aligned with TABLE
bitmap, which allowed me to use bitmap functions instead of looping over
all fields to create some needed bitmaps. (Faster and smaller code)
- Broke up found too long lines
- Moved some variable declaration at start of function for better code
readability.
- Removed some not used arguments from functions.
(setup_fields(), mysql_prepare_insert_check_table())
- setup_fields() now takes an enum instead of an int for marking columns
usage.
- For internal temporary tables, use handler::write_row(),
handler::delete_row() and handler::update_row() instead of
handler::ha_xxxx() for faster execution.
- Changed some constants to enum's and define's.
- Using separate column read and write sets allows for easier checking
of timestamp field was set by statement.
- Remove calls to free_io_cache() as this is now done automaticly in ha_reset()
- Don't build table->normalized_path as this is now identical to table->path
(after bar's fixes to convert filenames)
- Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to
do comparision with the 'convert-dbug-for-diff' tool.
Things left to do in 5.1:
- We wrongly log failed CREATE TABLE ... SELECT in some cases when using
row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result)
Mats has promised to look into this.
- Test that my fix for CREATE TABLE ... SELECT is indeed correct.
(I added several test cases for this, but in this case it's better that
someone else also tests this throughly).
Lars has promosed to do this.
2006-06-04 17:52:22 +02:00
|
|
|
field->flags&= ~FIELD_IN_ADD_INDEX;
|
2005-07-22 22:43:59 +02:00
|
|
|
changes|= tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Go through keys and check if the original ones are compatible
|
|
|
|
with new table.
|
|
|
|
*/
|
2006-01-12 10:05:07 +01:00
|
|
|
KEY *table_key;
|
|
|
|
KEY *table_key_end= table->key_info + table->s->keys;
|
|
|
|
KEY *new_key;
|
|
|
|
KEY *new_key_end= key_info_buffer + key_count;
|
2005-07-22 22:43:59 +02:00
|
|
|
|
2006-01-12 10:05:07 +01:00
|
|
|
DBUG_PRINT("info", ("index count old: %d new: %d",
|
|
|
|
table->s->keys, key_count));
|
|
|
|
/*
|
|
|
|
Step through all keys of the old table and search matching new keys.
|
|
|
|
*/
|
|
|
|
*index_drop_count= 0;
|
|
|
|
*index_add_count= 0;
|
|
|
|
for (table_key= table->key_info; table_key < table_key_end; table_key++)
|
2005-07-22 22:43:59 +02:00
|
|
|
{
|
2006-01-12 10:05:07 +01:00
|
|
|
KEY_PART_INFO *table_part;
|
|
|
|
KEY_PART_INFO *table_part_end= table_key->key_part + table_key->key_parts;
|
|
|
|
KEY_PART_INFO *new_part;
|
|
|
|
|
|
|
|
/* Search a new key with the same name. */
|
|
|
|
for (new_key= key_info_buffer; new_key < new_key_end; new_key++)
|
|
|
|
{
|
|
|
|
if (! strcmp(table_key->name, new_key->name))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (new_key >= new_key_end)
|
|
|
|
{
|
|
|
|
/* Key not found. Add the offset of the key to the drop buffer. */
|
|
|
|
index_drop_buffer[(*index_drop_count)++]= table_key - table->key_info;
|
|
|
|
DBUG_PRINT("info", ("index dropped: '%s'", table_key->name));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check that the key types are compatible between old and new tables. */
|
|
|
|
if ((table_key->algorithm != new_key->algorithm) ||
|
|
|
|
((table_key->flags & HA_KEYFLAG_MASK) !=
|
|
|
|
(new_key->flags & HA_KEYFLAG_MASK)) ||
|
|
|
|
(table_key->key_parts != new_key->key_parts))
|
|
|
|
goto index_changed;
|
2005-07-22 22:43:59 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
Check that the key parts remain compatible between the old and
|
|
|
|
new tables.
|
|
|
|
*/
|
2006-01-12 10:05:07 +01:00
|
|
|
for (table_part= table_key->key_part, new_part= new_key->key_part;
|
|
|
|
table_part < table_part_end;
|
|
|
|
table_part++, new_part++)
|
2005-07-22 22:43:59 +02:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
Key definition has changed if we are using a different field or
|
2006-01-12 10:05:07 +01:00
|
|
|
if the used key part length is different. We know that the fields
|
|
|
|
did not change. Comparing field numbers is sufficient.
|
2005-07-22 22:43:59 +02:00
|
|
|
*/
|
2006-01-12 10:05:07 +01:00
|
|
|
if ((table_part->length != new_part->length) ||
|
|
|
|
(table_part->fieldnr - 1 != new_part->fieldnr))
|
|
|
|
goto index_changed;
|
2005-07-22 22:43:59 +02:00
|
|
|
}
|
2006-01-12 10:05:07 +01:00
|
|
|
continue;
|
|
|
|
|
|
|
|
index_changed:
|
|
|
|
/* Key modified. Add the offset of the key to both buffers. */
|
|
|
|
index_drop_buffer[(*index_drop_count)++]= table_key - table->key_info;
|
|
|
|
index_add_buffer[(*index_add_count)++]= new_key - key_info_buffer;
|
2006-03-06 10:24:06 +01:00
|
|
|
key_part= new_key->key_part;
|
|
|
|
end= key_part + new_key->key_parts;
|
|
|
|
for(; key_part != end; key_part++)
|
|
|
|
{
|
|
|
|
// Mark field to be part of new key
|
|
|
|
field= table->field[key_part->fieldnr];
|
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes
Changes that requires code changes in other code of other storage engines.
(Note that all changes are very straightforward and one should find all issues
by compiling a --debug build and fixing all compiler errors and all
asserts in field.cc while running the test suite),
- New optional handler function introduced: reset()
This is called after every DML statement to make it easy for a handler to
statement specific cleanups.
(The only case it's not called is if force the file to be closed)
- handler::extra(HA_EXTRA_RESET) is removed. Code that was there before
should be moved to handler::reset()
- table->read_set contains a bitmap over all columns that are needed
in the query. read_row() and similar functions only needs to read these
columns
- table->write_set contains a bitmap over all columns that will be updated
in the query. write_row() and update_row() only needs to update these
columns.
The above bitmaps should now be up to date in all context
(including ALTER TABLE, filesort()).
The handler is informed of any changes to the bitmap after
fix_fields() by calling the virtual function
handler::column_bitmaps_signal(). If the handler does caching of
these bitmaps (instead of using table->read_set, table->write_set),
it should redo the caching in this code. as the signal() may be sent
several times, it's probably best to set a variable in the signal
and redo the caching on read_row() / write_row() if the variable was
set.
- Removed the read_set and write_set bitmap objects from the handler class
- Removed all column bit handling functions from the handler class.
(Now one instead uses the normal bitmap functions in my_bitmap.c instead
of handler dedicated bitmap functions)
- field->query_id is removed. One should instead instead check
table->read_set and table->write_set if a field is used in the query.
- handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and
handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now
instead use table->read_set to check for which columns to retrieve.
- If a handler needs to call Field->val() or Field->store() on columns
that are not used in the query, one should install a temporary
all-columns-used map while doing so. For this, we provide the following
functions:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
field->val();
dbug_tmp_restore_column_map(table->read_set, old_map);
and similar for the write map:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
field->val();
dbug_tmp_restore_column_map(table->write_set, old_map);
If this is not done, you will sooner or later hit a DBUG_ASSERT
in the field store() / val() functions.
(For not DBUG binaries, the dbug_tmp_restore_column_map() and
dbug_tmp_restore_column_map() are inline dummy functions and should
be optimized away be the compiler).
- If one needs to temporary set the column map for all binaries (and not
just to avoid the DBUG_ASSERT() in the Field::store() / Field::val()
methods) one should use the functions tmp_use_all_columns() and
tmp_restore_column_map() instead of the above dbug_ variants.
- All 'status' fields in the handler base class (like records,
data_file_length etc) are now stored in a 'stats' struct. This makes
it easier to know what status variables are provided by the base
handler. This requires some trivial variable names in the extra()
function.
- New virtual function handler::records(). This is called to optimize
COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true.
(stats.records is not supposed to be an exact value. It's only has to
be 'reasonable enough' for the optimizer to be able to choose a good
optimization path).
- Non virtual handler::init() function added for caching of virtual
constants from engine.
- Removed has_transactions() virtual method. Now one should instead return
HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support
transactions.
- The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument
that is to be used with 'new handler_name()' to allocate the handler
in the right area. The xxxx_create_handler() function is also
responsible for any initialization of the object before returning.
For example, one should change:
static handler *myisam_create_handler(TABLE_SHARE *table)
{
return new ha_myisam(table);
}
->
static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root)
{
return new (mem_root) ha_myisam(table);
}
- New optional virtual function: use_hidden_primary_key().
This is called in case of an update/delete when
(table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
but we don't have a primary key. This allows the handler to take precisions
in remembering any hidden primary key to able to update/delete any
found row. The default handler marks all columns to be read.
- handler::table_flags() now returns a ulonglong (to allow for more flags).
- New/changed table_flags()
- HA_HAS_RECORDS Set if ::records() is supported
- HA_NO_TRANSACTIONS Set if engine doesn't support transactions
- HA_PRIMARY_KEY_REQUIRED_FOR_DELETE
Set if we should mark all primary key columns for
read when reading rows as part of a DELETE
statement. If there is no primary key,
all columns are marked for read.
- HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some
cases (based on table->read_set)
- HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS
Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION.
- HA_DUPP_POS Renamed to HA_DUPLICATE_POS
- HA_REQUIRES_KEY_COLUMNS_FOR_DELETE
Set this if we should mark ALL key columns for
read when when reading rows as part of a DELETE
statement. In case of an update we will mark
all keys for read for which key part changed
value.
- HA_STATS_RECORDS_IS_EXACT
Set this if stats.records is exact.
(This saves us some extra records() calls
when optimizing COUNT(*))
- Removed table_flags()
- HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if
handler::records() gives an exact count() and
HA_STATS_RECORDS_IS_EXACT if stats.records is exact.
- HA_READ_RND_SAME Removed (no one supported this one)
- Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk()
- Renamed handler::dupp_pos to handler::dup_pos
- Removed not used variable handler::sortkey
Upper level handler changes:
- ha_reset() now does some overall checks and calls ::reset()
- ha_table_flags() added. This is a cached version of table_flags(). The
cache is updated on engine creation time and updated on open.
MySQL level changes (not obvious from the above):
- DBUG_ASSERT() added to check that column usage matches what is set
in the column usage bit maps. (This found a LOT of bugs in current
column marking code).
- In 5.1 before, all used columns was marked in read_set and only updated
columns was marked in write_set. Now we only mark columns for which we
need a value in read_set.
- Column bitmaps are created in open_binary_frm() and open_table_from_share().
(Before this was in table.cc)
- handler::table_flags() calls are replaced with handler::ha_table_flags()
- For calling field->val() you must have the corresponding bit set in
table->read_set. For calling field->store() you must have the
corresponding bit set in table->write_set. (There are asserts in
all store()/val() functions to catch wrong usage)
- thd->set_query_id is renamed to thd->mark_used_columns and instead
of setting this to an integer value, this has now the values:
MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE
Changed also all variables named 'set_query_id' to mark_used_columns.
- In filesort() we now inform the handler of exactly which columns are needed
doing the sort and choosing the rows.
- The TABLE_SHARE object has a 'all_set' column bitmap one can use
when one needs a column bitmap with all columns set.
(This is used for table->use_all_columns() and other places)
- The TABLE object has 3 column bitmaps:
- def_read_set Default bitmap for columns to be read
- def_write_set Default bitmap for columns to be written
- tmp_set Can be used as a temporary bitmap when needed.
The table object has also two pointer to bitmaps read_set and write_set
that the handler should use to find out which columns are used in which way.
- count() optimization now calls handler::records() instead of using
handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true).
- Added extra argument to Item::walk() to indicate if we should also
traverse sub queries.
- Added TABLE parameter to cp_buffer_from_ref()
- Don't close tables created with CREATE ... SELECT but keep them in
the table cache. (Faster usage of newly created tables).
New interfaces:
- table->clear_column_bitmaps() to initialize the bitmaps for tables
at start of new statements.
- table->column_bitmaps_set() to set up new column bitmaps and signal
the handler about this.
- table->column_bitmaps_set_no_signal() for some few cases where we need
to setup new column bitmaps but don't signal the handler (as the handler
has already been signaled about these before). Used for the momement
only in opt_range.cc when doing ROR scans.
- table->use_all_columns() to install a bitmap where all columns are marked
as use in the read and the write set.
- table->default_column_bitmaps() to install the normal read and write
column bitmaps, but not signaling the handler about this.
This is mainly used when creating TABLE instances.
- table->mark_columns_needed_for_delete(),
table->mark_columns_needed_for_delete() and
table->mark_columns_needed_for_insert() to allow us to put additional
columns in column usage maps if handler so requires.
(The handler indicates what it neads in handler->table_flags())
- table->prepare_for_position() to allow us to tell handler that it
needs to read primary key parts to be able to store them in
future table->position() calls.
(This replaces the table->file->ha_retrieve_all_pk function)
- table->mark_auto_increment_column() to tell handler are going to update
columns part of any auto_increment key.
- table->mark_columns_used_by_index() to mark all columns that is part of
an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow
it to quickly know that it only needs to read colums that are part
of the key. (The handler can also use the column map for detecting this,
but simpler/faster handler can just monitor the extra() call).
- table->mark_columns_used_by_index_no_reset() to in addition to other columns,
also mark all columns that is used by the given key.
- table->restore_column_maps_after_mark_index() to restore to default
column maps after a call to table->mark_columns_used_by_index().
- New item function register_field_in_read_map(), for marking used columns
in table->read_map. Used by filesort() to mark all used columns
- Maintain in TABLE->merge_keys set of all keys that are used in query.
(Simplices some optimization loops)
- Maintain Field->part_of_key_not_clustered which is like Field->part_of_key
but the field in the clustered key is not assumed to be part of all index.
(used in opt_range.cc for faster loops)
- dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map()
tmp_use_all_columns() and tmp_restore_column_map() functions to temporally
mark all columns as usable. The 'dbug_' version is primarily intended
inside a handler when it wants to just call Field:store() & Field::val()
functions, but don't need the column maps set for any other usage.
(ie:: bitmap_is_set() is never called)
- We can't use compare_records() to skip updates for handlers that returns
a partial column set and the read_set doesn't cover all columns in the
write set. The reason for this is that if we have a column marked only for
write we can't in the MySQL level know if the value changed or not.
The reason this worked before was that MySQL marked all to be written
columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden
bug'.
- open_table_from_share() does not anymore setup temporary MEM_ROOT
object as a thread specific variable for the handler. Instead we
send the to-be-used MEMROOT to get_new_handler().
(Simpler, faster code)
Bugs fixed:
- Column marking was not done correctly in a lot of cases.
(ALTER TABLE, when using triggers, auto_increment fields etc)
(Could potentially result in wrong values inserted in table handlers
relying on that the old column maps or field->set_query_id was correct)
Especially when it comes to triggers, there may be cases where the
old code would cause lost/wrong values for NDB and/or InnoDB tables.
- Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags:
OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG.
This allowed me to remove some wrong warnings about:
"Some non-transactional changed tables couldn't be rolled back"
- Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset
(thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose
some warnings about
"Some non-transactional changed tables couldn't be rolled back")
- Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table()
which could cause delete_table to report random failures.
- Fixed core dumps for some tests when running with --debug
- Added missing FN_LIBCHAR in mysql_rm_tmp_tables()
(This has probably caused us to not properly remove temporary files after
crash)
- slow_logs was not properly initialized, which could maybe cause
extra/lost entries in slow log.
- If we get an duplicate row on insert, change column map to read and
write all columns while retrying the operation. This is required by
the definition of REPLACE and also ensures that fields that are only
part of UPDATE are properly handled. This fixed a bug in NDB and
REPLACE where REPLACE wrongly copied some column values from the replaced
row.
- For table handler that doesn't support NULL in keys, we would give an error
when creating a primary key with NULL fields, even after the fields has been
automaticly converted to NOT NULL.
- Creating a primary key on a SPATIAL key, would fail if field was not
declared as NOT NULL.
Cleanups:
- Removed not used condition argument to setup_tables
- Removed not needed item function reset_query_id_processor().
- Field->add_index is removed. Now this is instead maintained in
(field->flags & FIELD_IN_ADD_INDEX)
- Field->fieldnr is removed (use field->field_index instead)
- New argument to filesort() to indicate that it should return a set of
row pointers (not used columns). This allowed me to remove some references
to sql_command in filesort and should also enable us to return column
results in some cases where we couldn't before.
- Changed column bitmap handling in opt_range.cc to be aligned with TABLE
bitmap, which allowed me to use bitmap functions instead of looping over
all fields to create some needed bitmaps. (Faster and smaller code)
- Broke up found too long lines
- Moved some variable declaration at start of function for better code
readability.
- Removed some not used arguments from functions.
(setup_fields(), mysql_prepare_insert_check_table())
- setup_fields() now takes an enum instead of an int for marking columns
usage.
- For internal temporary tables, use handler::write_row(),
handler::delete_row() and handler::update_row() instead of
handler::ha_xxxx() for faster execution.
- Changed some constants to enum's and define's.
- Using separate column read and write sets allows for easier checking
of timestamp field was set by statement.
- Remove calls to free_io_cache() as this is now done automaticly in ha_reset()
- Don't build table->normalized_path as this is now identical to table->path
(after bar's fixes to convert filenames)
- Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to
do comparision with the 'convert-dbug-for-diff' tool.
Things left to do in 5.1:
- We wrongly log failed CREATE TABLE ... SELECT in some cases when using
row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result)
Mats has promised to look into this.
- Test that my fix for CREATE TABLE ... SELECT is indeed correct.
(I added several test cases for this, but in this case it's better that
someone else also tests this throughly).
Lars has promosed to do this.
2006-06-04 17:52:22 +02:00
|
|
|
field->flags|= FIELD_IN_ADD_INDEX;
|
2006-03-06 10:24:06 +01:00
|
|
|
}
|
2006-01-12 10:05:07 +01:00
|
|
|
DBUG_PRINT("info", ("index changed: '%s'", table_key->name));
|
2005-07-22 22:43:59 +02:00
|
|
|
}
|
2006-01-12 10:05:07 +01:00
|
|
|
/*end of for (; table_key < table_key_end;) */
|
2005-07-22 22:43:59 +02:00
|
|
|
|
2006-01-12 10:05:07 +01:00
|
|
|
/*
|
|
|
|
Step through all keys of the new table and find matching old keys.
|
|
|
|
*/
|
|
|
|
for (new_key= key_info_buffer; new_key < new_key_end; new_key++)
|
|
|
|
{
|
|
|
|
/* Search an old key with the same name. */
|
|
|
|
for (table_key= table->key_info; table_key < table_key_end; table_key++)
|
|
|
|
{
|
|
|
|
if (! strcmp(table_key->name, new_key->name))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (table_key >= table_key_end)
|
|
|
|
{
|
|
|
|
/* Key not found. Add the offset of the key to the add buffer. */
|
|
|
|
index_add_buffer[(*index_add_count)++]= new_key - key_info_buffer;
|
2006-03-06 10:24:06 +01:00
|
|
|
key_part= new_key->key_part;
|
|
|
|
end= key_part + new_key->key_parts;
|
|
|
|
for(; key_part != end; key_part++)
|
|
|
|
{
|
|
|
|
// Mark field to be part of new key
|
|
|
|
field= table->field[key_part->fieldnr];
|
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes
Changes that requires code changes in other code of other storage engines.
(Note that all changes are very straightforward and one should find all issues
by compiling a --debug build and fixing all compiler errors and all
asserts in field.cc while running the test suite),
- New optional handler function introduced: reset()
This is called after every DML statement to make it easy for a handler to
statement specific cleanups.
(The only case it's not called is if force the file to be closed)
- handler::extra(HA_EXTRA_RESET) is removed. Code that was there before
should be moved to handler::reset()
- table->read_set contains a bitmap over all columns that are needed
in the query. read_row() and similar functions only needs to read these
columns
- table->write_set contains a bitmap over all columns that will be updated
in the query. write_row() and update_row() only needs to update these
columns.
The above bitmaps should now be up to date in all context
(including ALTER TABLE, filesort()).
The handler is informed of any changes to the bitmap after
fix_fields() by calling the virtual function
handler::column_bitmaps_signal(). If the handler does caching of
these bitmaps (instead of using table->read_set, table->write_set),
it should redo the caching in this code. as the signal() may be sent
several times, it's probably best to set a variable in the signal
and redo the caching on read_row() / write_row() if the variable was
set.
- Removed the read_set and write_set bitmap objects from the handler class
- Removed all column bit handling functions from the handler class.
(Now one instead uses the normal bitmap functions in my_bitmap.c instead
of handler dedicated bitmap functions)
- field->query_id is removed. One should instead instead check
table->read_set and table->write_set if a field is used in the query.
- handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and
handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now
instead use table->read_set to check for which columns to retrieve.
- If a handler needs to call Field->val() or Field->store() on columns
that are not used in the query, one should install a temporary
all-columns-used map while doing so. For this, we provide the following
functions:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
field->val();
dbug_tmp_restore_column_map(table->read_set, old_map);
and similar for the write map:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
field->val();
dbug_tmp_restore_column_map(table->write_set, old_map);
If this is not done, you will sooner or later hit a DBUG_ASSERT
in the field store() / val() functions.
(For not DBUG binaries, the dbug_tmp_restore_column_map() and
dbug_tmp_restore_column_map() are inline dummy functions and should
be optimized away be the compiler).
- If one needs to temporary set the column map for all binaries (and not
just to avoid the DBUG_ASSERT() in the Field::store() / Field::val()
methods) one should use the functions tmp_use_all_columns() and
tmp_restore_column_map() instead of the above dbug_ variants.
- All 'status' fields in the handler base class (like records,
data_file_length etc) are now stored in a 'stats' struct. This makes
it easier to know what status variables are provided by the base
handler. This requires some trivial variable names in the extra()
function.
- New virtual function handler::records(). This is called to optimize
COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true.
(stats.records is not supposed to be an exact value. It's only has to
be 'reasonable enough' for the optimizer to be able to choose a good
optimization path).
- Non virtual handler::init() function added for caching of virtual
constants from engine.
- Removed has_transactions() virtual method. Now one should instead return
HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support
transactions.
- The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument
that is to be used with 'new handler_name()' to allocate the handler
in the right area. The xxxx_create_handler() function is also
responsible for any initialization of the object before returning.
For example, one should change:
static handler *myisam_create_handler(TABLE_SHARE *table)
{
return new ha_myisam(table);
}
->
static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root)
{
return new (mem_root) ha_myisam(table);
}
- New optional virtual function: use_hidden_primary_key().
This is called in case of an update/delete when
(table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
but we don't have a primary key. This allows the handler to take precisions
in remembering any hidden primary key to able to update/delete any
found row. The default handler marks all columns to be read.
- handler::table_flags() now returns a ulonglong (to allow for more flags).
- New/changed table_flags()
- HA_HAS_RECORDS Set if ::records() is supported
- HA_NO_TRANSACTIONS Set if engine doesn't support transactions
- HA_PRIMARY_KEY_REQUIRED_FOR_DELETE
Set if we should mark all primary key columns for
read when reading rows as part of a DELETE
statement. If there is no primary key,
all columns are marked for read.
- HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some
cases (based on table->read_set)
- HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS
Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION.
- HA_DUPP_POS Renamed to HA_DUPLICATE_POS
- HA_REQUIRES_KEY_COLUMNS_FOR_DELETE
Set this if we should mark ALL key columns for
read when when reading rows as part of a DELETE
statement. In case of an update we will mark
all keys for read for which key part changed
value.
- HA_STATS_RECORDS_IS_EXACT
Set this if stats.records is exact.
(This saves us some extra records() calls
when optimizing COUNT(*))
- Removed table_flags()
- HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if
handler::records() gives an exact count() and
HA_STATS_RECORDS_IS_EXACT if stats.records is exact.
- HA_READ_RND_SAME Removed (no one supported this one)
- Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk()
- Renamed handler::dupp_pos to handler::dup_pos
- Removed not used variable handler::sortkey
Upper level handler changes:
- ha_reset() now does some overall checks and calls ::reset()
- ha_table_flags() added. This is a cached version of table_flags(). The
cache is updated on engine creation time and updated on open.
MySQL level changes (not obvious from the above):
- DBUG_ASSERT() added to check that column usage matches what is set
in the column usage bit maps. (This found a LOT of bugs in current
column marking code).
- In 5.1 before, all used columns was marked in read_set and only updated
columns was marked in write_set. Now we only mark columns for which we
need a value in read_set.
- Column bitmaps are created in open_binary_frm() and open_table_from_share().
(Before this was in table.cc)
- handler::table_flags() calls are replaced with handler::ha_table_flags()
- For calling field->val() you must have the corresponding bit set in
table->read_set. For calling field->store() you must have the
corresponding bit set in table->write_set. (There are asserts in
all store()/val() functions to catch wrong usage)
- thd->set_query_id is renamed to thd->mark_used_columns and instead
of setting this to an integer value, this has now the values:
MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE
Changed also all variables named 'set_query_id' to mark_used_columns.
- In filesort() we now inform the handler of exactly which columns are needed
doing the sort and choosing the rows.
- The TABLE_SHARE object has a 'all_set' column bitmap one can use
when one needs a column bitmap with all columns set.
(This is used for table->use_all_columns() and other places)
- The TABLE object has 3 column bitmaps:
- def_read_set Default bitmap for columns to be read
- def_write_set Default bitmap for columns to be written
- tmp_set Can be used as a temporary bitmap when needed.
The table object has also two pointer to bitmaps read_set and write_set
that the handler should use to find out which columns are used in which way.
- count() optimization now calls handler::records() instead of using
handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true).
- Added extra argument to Item::walk() to indicate if we should also
traverse sub queries.
- Added TABLE parameter to cp_buffer_from_ref()
- Don't close tables created with CREATE ... SELECT but keep them in
the table cache. (Faster usage of newly created tables).
New interfaces:
- table->clear_column_bitmaps() to initialize the bitmaps for tables
at start of new statements.
- table->column_bitmaps_set() to set up new column bitmaps and signal
the handler about this.
- table->column_bitmaps_set_no_signal() for some few cases where we need
to setup new column bitmaps but don't signal the handler (as the handler
has already been signaled about these before). Used for the momement
only in opt_range.cc when doing ROR scans.
- table->use_all_columns() to install a bitmap where all columns are marked
as use in the read and the write set.
- table->default_column_bitmaps() to install the normal read and write
column bitmaps, but not signaling the handler about this.
This is mainly used when creating TABLE instances.
- table->mark_columns_needed_for_delete(),
table->mark_columns_needed_for_delete() and
table->mark_columns_needed_for_insert() to allow us to put additional
columns in column usage maps if handler so requires.
(The handler indicates what it neads in handler->table_flags())
- table->prepare_for_position() to allow us to tell handler that it
needs to read primary key parts to be able to store them in
future table->position() calls.
(This replaces the table->file->ha_retrieve_all_pk function)
- table->mark_auto_increment_column() to tell handler are going to update
columns part of any auto_increment key.
- table->mark_columns_used_by_index() to mark all columns that is part of
an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow
it to quickly know that it only needs to read colums that are part
of the key. (The handler can also use the column map for detecting this,
but simpler/faster handler can just monitor the extra() call).
- table->mark_columns_used_by_index_no_reset() to in addition to other columns,
also mark all columns that is used by the given key.
- table->restore_column_maps_after_mark_index() to restore to default
column maps after a call to table->mark_columns_used_by_index().
- New item function register_field_in_read_map(), for marking used columns
in table->read_map. Used by filesort() to mark all used columns
- Maintain in TABLE->merge_keys set of all keys that are used in query.
(Simplices some optimization loops)
- Maintain Field->part_of_key_not_clustered which is like Field->part_of_key
but the field in the clustered key is not assumed to be part of all index.
(used in opt_range.cc for faster loops)
- dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map()
tmp_use_all_columns() and tmp_restore_column_map() functions to temporally
mark all columns as usable. The 'dbug_' version is primarily intended
inside a handler when it wants to just call Field:store() & Field::val()
functions, but don't need the column maps set for any other usage.
(ie:: bitmap_is_set() is never called)
- We can't use compare_records() to skip updates for handlers that returns
a partial column set and the read_set doesn't cover all columns in the
write set. The reason for this is that if we have a column marked only for
write we can't in the MySQL level know if the value changed or not.
The reason this worked before was that MySQL marked all to be written
columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden
bug'.
- open_table_from_share() does not anymore setup temporary MEM_ROOT
object as a thread specific variable for the handler. Instead we
send the to-be-used MEMROOT to get_new_handler().
(Simpler, faster code)
Bugs fixed:
- Column marking was not done correctly in a lot of cases.
(ALTER TABLE, when using triggers, auto_increment fields etc)
(Could potentially result in wrong values inserted in table handlers
relying on that the old column maps or field->set_query_id was correct)
Especially when it comes to triggers, there may be cases where the
old code would cause lost/wrong values for NDB and/or InnoDB tables.
- Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags:
OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG.
This allowed me to remove some wrong warnings about:
"Some non-transactional changed tables couldn't be rolled back"
- Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset
(thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose
some warnings about
"Some non-transactional changed tables couldn't be rolled back")
- Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table()
which could cause delete_table to report random failures.
- Fixed core dumps for some tests when running with --debug
- Added missing FN_LIBCHAR in mysql_rm_tmp_tables()
(This has probably caused us to not properly remove temporary files after
crash)
- slow_logs was not properly initialized, which could maybe cause
extra/lost entries in slow log.
- If we get an duplicate row on insert, change column map to read and
write all columns while retrying the operation. This is required by
the definition of REPLACE and also ensures that fields that are only
part of UPDATE are properly handled. This fixed a bug in NDB and
REPLACE where REPLACE wrongly copied some column values from the replaced
row.
- For table handler that doesn't support NULL in keys, we would give an error
when creating a primary key with NULL fields, even after the fields has been
automaticly converted to NOT NULL.
- Creating a primary key on a SPATIAL key, would fail if field was not
declared as NOT NULL.
Cleanups:
- Removed not used condition argument to setup_tables
- Removed not needed item function reset_query_id_processor().
- Field->add_index is removed. Now this is instead maintained in
(field->flags & FIELD_IN_ADD_INDEX)
- Field->fieldnr is removed (use field->field_index instead)
- New argument to filesort() to indicate that it should return a set of
row pointers (not used columns). This allowed me to remove some references
to sql_command in filesort and should also enable us to return column
results in some cases where we couldn't before.
- Changed column bitmap handling in opt_range.cc to be aligned with TABLE
bitmap, which allowed me to use bitmap functions instead of looping over
all fields to create some needed bitmaps. (Faster and smaller code)
- Broke up found too long lines
- Moved some variable declaration at start of function for better code
readability.
- Removed some not used arguments from functions.
(setup_fields(), mysql_prepare_insert_check_table())
- setup_fields() now takes an enum instead of an int for marking columns
usage.
- For internal temporary tables, use handler::write_row(),
handler::delete_row() and handler::update_row() instead of
handler::ha_xxxx() for faster execution.
- Changed some constants to enum's and define's.
- Using separate column read and write sets allows for easier checking
of timestamp field was set by statement.
- Remove calls to free_io_cache() as this is now done automaticly in ha_reset()
- Don't build table->normalized_path as this is now identical to table->path
(after bar's fixes to convert filenames)
- Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to
do comparision with the 'convert-dbug-for-diff' tool.
Things left to do in 5.1:
- We wrongly log failed CREATE TABLE ... SELECT in some cases when using
row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result)
Mats has promised to look into this.
- Test that my fix for CREATE TABLE ... SELECT is indeed correct.
(I added several test cases for this, but in this case it's better that
someone else also tests this throughly).
Lars has promosed to do this.
2006-06-04 17:52:22 +02:00
|
|
|
field->flags|= FIELD_IN_ADD_INDEX;
|
2006-03-06 10:24:06 +01:00
|
|
|
}
|
2006-01-31 15:53:35 +01:00
|
|
|
DBUG_PRINT("info", ("index added: '%s'", new_key->name));
|
2006-01-12 10:05:07 +01:00
|
|
|
}
|
|
|
|
}
|
2006-01-27 17:23:14 +01:00
|
|
|
|
|
|
|
/* Check if changes are compatible with current handler without a copy */
|
|
|
|
if (table->file->check_if_incompatible_data(create_info, changes))
|
|
|
|
DBUG_RETURN(ALTER_TABLE_DATA_CHANGED);
|
|
|
|
|
2006-01-12 10:05:07 +01:00
|
|
|
if (*index_drop_count || *index_add_count)
|
|
|
|
DBUG_RETURN(ALTER_TABLE_INDEX_CHANGED);
|
|
|
|
|
|
|
|
DBUG_RETURN(0); // Tables are compatible
|
2005-07-22 22:43:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-04-08 16:56:45 +02:00
|
|
|
/*
|
|
|
|
Alter table
|
|
|
|
*/
|
2004-03-30 19:22:14 +02:00
|
|
|
|
2004-10-20 03:04:37 +02:00
|
|
|
bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
|
2006-06-15 20:03:17 +02:00
|
|
|
HA_CREATE_INFO *lex_create_info,
|
2004-10-20 03:04:37 +02:00
|
|
|
TABLE_LIST *table_list,
|
|
|
|
List<create_field> &fields, List<Key> &keys,
|
2006-07-01 23:51:10 +02:00
|
|
|
uint order_num, ORDER *order, bool ignore,
|
2004-10-20 03:04:37 +02:00
|
|
|
ALTER_INFO *alter_info, bool do_send_ok)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-10-21 18:10:58 +02:00
|
|
|
TABLE *table,*new_table=0;
|
2000-07-31 21:29:14 +02:00
|
|
|
int error;
|
2003-12-30 12:14:21 +01:00
|
|
|
char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN];
|
|
|
|
char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias;
|
2001-06-01 03:27:59 +02:00
|
|
|
char index_file[FN_REFLEN], data_file[FN_REFLEN];
|
2006-04-11 14:06:32 +02:00
|
|
|
char path[FN_REFLEN];
|
2006-01-12 10:05:07 +01:00
|
|
|
char reg_path[FN_REFLEN+1];
|
2000-08-07 23:28:56 +02:00
|
|
|
ha_rows copied,deleted;
|
2004-04-02 08:12:53 +02:00
|
|
|
uint db_create_options, used_fields;
|
2005-12-21 19:18:40 +01:00
|
|
|
handlerton *old_db_type, *new_db_type;
|
2006-06-15 20:03:17 +02:00
|
|
|
HA_CREATE_INFO *create_info;
|
2005-07-22 22:43:59 +02:00
|
|
|
uint need_copy_table= 0;
|
2006-05-16 15:37:23 +02:00
|
|
|
bool no_table_reopen= FALSE, varchar= FALSE;
|
2005-11-07 16:25:06 +01:00
|
|
|
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
2006-01-17 08:40:00 +01:00
|
|
|
uint fast_alter_partition= 0;
|
2005-07-27 22:56:28 +02:00
|
|
|
bool partition_changed= FALSE;
|
|
|
|
#endif
|
2006-01-12 10:05:07 +01:00
|
|
|
List<create_field> prepared_create_list;
|
|
|
|
List<Key> prepared_key_list;
|
|
|
|
bool need_lock_for_indexes= TRUE;
|
|
|
|
uint db_options= 0;
|
|
|
|
uint key_count;
|
|
|
|
KEY *key_info_buffer;
|
|
|
|
uint index_drop_count;
|
|
|
|
uint *index_drop_buffer;
|
|
|
|
uint index_add_count;
|
|
|
|
uint *index_add_buffer;
|
2006-01-17 12:53:49 +01:00
|
|
|
bool committed= 0;
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_ENTER("mysql_alter_table");
|
|
|
|
|
2006-03-29 13:27:36 +02:00
|
|
|
LINT_INIT(index_add_count);
|
|
|
|
LINT_INIT(index_drop_count);
|
|
|
|
LINT_INIT(index_add_buffer);
|
|
|
|
LINT_INIT(index_drop_buffer);
|
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
thd->proc_info="init";
|
2006-06-15 20:03:17 +02:00
|
|
|
if (!(create_info= copy_create_info(lex_create_info)))
|
|
|
|
{
|
|
|
|
DBUG_RETURN(TRUE);
|
|
|
|
}
|
2005-01-06 12:00:13 +01:00
|
|
|
table_name=table_list->table_name;
|
2003-12-30 12:14:21 +01:00
|
|
|
alias= (lower_case_table_names == 2) ? table_list->alias : table_name;
|
2000-07-31 21:29:14 +02:00
|
|
|
db=table_list->db;
|
2004-02-11 00:06:46 +01:00
|
|
|
if (!new_db || !my_strcasecmp(table_alias_charset, new_db, db))
|
2004-02-09 12:29:31 +01:00
|
|
|
new_db= db;
|
2006-01-12 10:05:07 +01:00
|
|
|
build_table_filename(reg_path, sizeof(reg_path), db, table_name, reg_ext);
|
2006-04-11 14:06:32 +02:00
|
|
|
build_table_filename(path, sizeof(path), db, table_name, "");
|
2006-01-12 10:05:07 +01:00
|
|
|
|
2001-09-30 04:47:35 +02:00
|
|
|
used_fields=create_info->used_fields;
|
2006-01-12 10:05:07 +01:00
|
|
|
|
2005-11-15 21:57:02 +01:00
|
|
|
mysql_ha_flush(thd, table_list, MYSQL_HA_CLOSE_FINAL, FALSE);
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2003-10-14 00:52:03 +02:00
|
|
|
/* DISCARD/IMPORT TABLESPACE is always alone in an ALTER TABLE */
|
2004-05-21 16:57:03 +02:00
|
|
|
if (alter_info->tablespace_op != NO_TABLESPACE_OP)
|
2003-10-13 10:20:19 +02:00
|
|
|
DBUG_RETURN(mysql_discard_or_import_tablespace(thd,table_list,
|
2004-05-21 16:57:03 +02:00
|
|
|
alter_info->tablespace_op));
|
2000-07-31 21:29:14 +02:00
|
|
|
if (!(table=open_ltable(thd,table_list,TL_WRITE_ALLOW_READ)))
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes
Changes that requires code changes in other code of other storage engines.
(Note that all changes are very straightforward and one should find all issues
by compiling a --debug build and fixing all compiler errors and all
asserts in field.cc while running the test suite),
- New optional handler function introduced: reset()
This is called after every DML statement to make it easy for a handler to
statement specific cleanups.
(The only case it's not called is if force the file to be closed)
- handler::extra(HA_EXTRA_RESET) is removed. Code that was there before
should be moved to handler::reset()
- table->read_set contains a bitmap over all columns that are needed
in the query. read_row() and similar functions only needs to read these
columns
- table->write_set contains a bitmap over all columns that will be updated
in the query. write_row() and update_row() only needs to update these
columns.
The above bitmaps should now be up to date in all context
(including ALTER TABLE, filesort()).
The handler is informed of any changes to the bitmap after
fix_fields() by calling the virtual function
handler::column_bitmaps_signal(). If the handler does caching of
these bitmaps (instead of using table->read_set, table->write_set),
it should redo the caching in this code. as the signal() may be sent
several times, it's probably best to set a variable in the signal
and redo the caching on read_row() / write_row() if the variable was
set.
- Removed the read_set and write_set bitmap objects from the handler class
- Removed all column bit handling functions from the handler class.
(Now one instead uses the normal bitmap functions in my_bitmap.c instead
of handler dedicated bitmap functions)
- field->query_id is removed. One should instead instead check
table->read_set and table->write_set if a field is used in the query.
- handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and
handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now
instead use table->read_set to check for which columns to retrieve.
- If a handler needs to call Field->val() or Field->store() on columns
that are not used in the query, one should install a temporary
all-columns-used map while doing so. For this, we provide the following
functions:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
field->val();
dbug_tmp_restore_column_map(table->read_set, old_map);
and similar for the write map:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
field->val();
dbug_tmp_restore_column_map(table->write_set, old_map);
If this is not done, you will sooner or later hit a DBUG_ASSERT
in the field store() / val() functions.
(For not DBUG binaries, the dbug_tmp_restore_column_map() and
dbug_tmp_restore_column_map() are inline dummy functions and should
be optimized away be the compiler).
- If one needs to temporary set the column map for all binaries (and not
just to avoid the DBUG_ASSERT() in the Field::store() / Field::val()
methods) one should use the functions tmp_use_all_columns() and
tmp_restore_column_map() instead of the above dbug_ variants.
- All 'status' fields in the handler base class (like records,
data_file_length etc) are now stored in a 'stats' struct. This makes
it easier to know what status variables are provided by the base
handler. This requires some trivial variable names in the extra()
function.
- New virtual function handler::records(). This is called to optimize
COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true.
(stats.records is not supposed to be an exact value. It's only has to
be 'reasonable enough' for the optimizer to be able to choose a good
optimization path).
- Non virtual handler::init() function added for caching of virtual
constants from engine.
- Removed has_transactions() virtual method. Now one should instead return
HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support
transactions.
- The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument
that is to be used with 'new handler_name()' to allocate the handler
in the right area. The xxxx_create_handler() function is also
responsible for any initialization of the object before returning.
For example, one should change:
static handler *myisam_create_handler(TABLE_SHARE *table)
{
return new ha_myisam(table);
}
->
static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root)
{
return new (mem_root) ha_myisam(table);
}
- New optional virtual function: use_hidden_primary_key().
This is called in case of an update/delete when
(table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
but we don't have a primary key. This allows the handler to take precisions
in remembering any hidden primary key to able to update/delete any
found row. The default handler marks all columns to be read.
- handler::table_flags() now returns a ulonglong (to allow for more flags).
- New/changed table_flags()
- HA_HAS_RECORDS Set if ::records() is supported
- HA_NO_TRANSACTIONS Set if engine doesn't support transactions
- HA_PRIMARY_KEY_REQUIRED_FOR_DELETE
Set if we should mark all primary key columns for
read when reading rows as part of a DELETE
statement. If there is no primary key,
all columns are marked for read.
- HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some
cases (based on table->read_set)
- HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS
Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION.
- HA_DUPP_POS Renamed to HA_DUPLICATE_POS
- HA_REQUIRES_KEY_COLUMNS_FOR_DELETE
Set this if we should mark ALL key columns for
read when when reading rows as part of a DELETE
statement. In case of an update we will mark
all keys for read for which key part changed
value.
- HA_STATS_RECORDS_IS_EXACT
Set this if stats.records is exact.
(This saves us some extra records() calls
when optimizing COUNT(*))
- Removed table_flags()
- HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if
handler::records() gives an exact count() and
HA_STATS_RECORDS_IS_EXACT if stats.records is exact.
- HA_READ_RND_SAME Removed (no one supported this one)
- Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk()
- Renamed handler::dupp_pos to handler::dup_pos
- Removed not used variable handler::sortkey
Upper level handler changes:
- ha_reset() now does some overall checks and calls ::reset()
- ha_table_flags() added. This is a cached version of table_flags(). The
cache is updated on engine creation time and updated on open.
MySQL level changes (not obvious from the above):
- DBUG_ASSERT() added to check that column usage matches what is set
in the column usage bit maps. (This found a LOT of bugs in current
column marking code).
- In 5.1 before, all used columns was marked in read_set and only updated
columns was marked in write_set. Now we only mark columns for which we
need a value in read_set.
- Column bitmaps are created in open_binary_frm() and open_table_from_share().
(Before this was in table.cc)
- handler::table_flags() calls are replaced with handler::ha_table_flags()
- For calling field->val() you must have the corresponding bit set in
table->read_set. For calling field->store() you must have the
corresponding bit set in table->write_set. (There are asserts in
all store()/val() functions to catch wrong usage)
- thd->set_query_id is renamed to thd->mark_used_columns and instead
of setting this to an integer value, this has now the values:
MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE
Changed also all variables named 'set_query_id' to mark_used_columns.
- In filesort() we now inform the handler of exactly which columns are needed
doing the sort and choosing the rows.
- The TABLE_SHARE object has a 'all_set' column bitmap one can use
when one needs a column bitmap with all columns set.
(This is used for table->use_all_columns() and other places)
- The TABLE object has 3 column bitmaps:
- def_read_set Default bitmap for columns to be read
- def_write_set Default bitmap for columns to be written
- tmp_set Can be used as a temporary bitmap when needed.
The table object has also two pointer to bitmaps read_set and write_set
that the handler should use to find out which columns are used in which way.
- count() optimization now calls handler::records() instead of using
handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true).
- Added extra argument to Item::walk() to indicate if we should also
traverse sub queries.
- Added TABLE parameter to cp_buffer_from_ref()
- Don't close tables created with CREATE ... SELECT but keep them in
the table cache. (Faster usage of newly created tables).
New interfaces:
- table->clear_column_bitmaps() to initialize the bitmaps for tables
at start of new statements.
- table->column_bitmaps_set() to set up new column bitmaps and signal
the handler about this.
- table->column_bitmaps_set_no_signal() for some few cases where we need
to setup new column bitmaps but don't signal the handler (as the handler
has already been signaled about these before). Used for the momement
only in opt_range.cc when doing ROR scans.
- table->use_all_columns() to install a bitmap where all columns are marked
as use in the read and the write set.
- table->default_column_bitmaps() to install the normal read and write
column bitmaps, but not signaling the handler about this.
This is mainly used when creating TABLE instances.
- table->mark_columns_needed_for_delete(),
table->mark_columns_needed_for_delete() and
table->mark_columns_needed_for_insert() to allow us to put additional
columns in column usage maps if handler so requires.
(The handler indicates what it neads in handler->table_flags())
- table->prepare_for_position() to allow us to tell handler that it
needs to read primary key parts to be able to store them in
future table->position() calls.
(This replaces the table->file->ha_retrieve_all_pk function)
- table->mark_auto_increment_column() to tell handler are going to update
columns part of any auto_increment key.
- table->mark_columns_used_by_index() to mark all columns that is part of
an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow
it to quickly know that it only needs to read colums that are part
of the key. (The handler can also use the column map for detecting this,
but simpler/faster handler can just monitor the extra() call).
- table->mark_columns_used_by_index_no_reset() to in addition to other columns,
also mark all columns that is used by the given key.
- table->restore_column_maps_after_mark_index() to restore to default
column maps after a call to table->mark_columns_used_by_index().
- New item function register_field_in_read_map(), for marking used columns
in table->read_map. Used by filesort() to mark all used columns
- Maintain in TABLE->merge_keys set of all keys that are used in query.
(Simplices some optimization loops)
- Maintain Field->part_of_key_not_clustered which is like Field->part_of_key
but the field in the clustered key is not assumed to be part of all index.
(used in opt_range.cc for faster loops)
- dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map()
tmp_use_all_columns() and tmp_restore_column_map() functions to temporally
mark all columns as usable. The 'dbug_' version is primarily intended
inside a handler when it wants to just call Field:store() & Field::val()
functions, but don't need the column maps set for any other usage.
(ie:: bitmap_is_set() is never called)
- We can't use compare_records() to skip updates for handlers that returns
a partial column set and the read_set doesn't cover all columns in the
write set. The reason for this is that if we have a column marked only for
write we can't in the MySQL level know if the value changed or not.
The reason this worked before was that MySQL marked all to be written
columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden
bug'.
- open_table_from_share() does not anymore setup temporary MEM_ROOT
object as a thread specific variable for the handler. Instead we
send the to-be-used MEMROOT to get_new_handler().
(Simpler, faster code)
Bugs fixed:
- Column marking was not done correctly in a lot of cases.
(ALTER TABLE, when using triggers, auto_increment fields etc)
(Could potentially result in wrong values inserted in table handlers
relying on that the old column maps or field->set_query_id was correct)
Especially when it comes to triggers, there may be cases where the
old code would cause lost/wrong values for NDB and/or InnoDB tables.
- Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags:
OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG.
This allowed me to remove some wrong warnings about:
"Some non-transactional changed tables couldn't be rolled back"
- Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset
(thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose
some warnings about
"Some non-transactional changed tables couldn't be rolled back")
- Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table()
which could cause delete_table to report random failures.
- Fixed core dumps for some tests when running with --debug
- Added missing FN_LIBCHAR in mysql_rm_tmp_tables()
(This has probably caused us to not properly remove temporary files after
crash)
- slow_logs was not properly initialized, which could maybe cause
extra/lost entries in slow log.
- If we get an duplicate row on insert, change column map to read and
write all columns while retrying the operation. This is required by
the definition of REPLACE and also ensures that fields that are only
part of UPDATE are properly handled. This fixed a bug in NDB and
REPLACE where REPLACE wrongly copied some column values from the replaced
row.
- For table handler that doesn't support NULL in keys, we would give an error
when creating a primary key with NULL fields, even after the fields has been
automaticly converted to NOT NULL.
- Creating a primary key on a SPATIAL key, would fail if field was not
declared as NOT NULL.
Cleanups:
- Removed not used condition argument to setup_tables
- Removed not needed item function reset_query_id_processor().
- Field->add_index is removed. Now this is instead maintained in
(field->flags & FIELD_IN_ADD_INDEX)
- Field->fieldnr is removed (use field->field_index instead)
- New argument to filesort() to indicate that it should return a set of
row pointers (not used columns). This allowed me to remove some references
to sql_command in filesort and should also enable us to return column
results in some cases where we couldn't before.
- Changed column bitmap handling in opt_range.cc to be aligned with TABLE
bitmap, which allowed me to use bitmap functions instead of looping over
all fields to create some needed bitmaps. (Faster and smaller code)
- Broke up found too long lines
- Moved some variable declaration at start of function for better code
readability.
- Removed some not used arguments from functions.
(setup_fields(), mysql_prepare_insert_check_table())
- setup_fields() now takes an enum instead of an int for marking columns
usage.
- For internal temporary tables, use handler::write_row(),
handler::delete_row() and handler::update_row() instead of
handler::ha_xxxx() for faster execution.
- Changed some constants to enum's and define's.
- Using separate column read and write sets allows for easier checking
of timestamp field was set by statement.
- Remove calls to free_io_cache() as this is now done automaticly in ha_reset()
- Don't build table->normalized_path as this is now identical to table->path
(after bar's fixes to convert filenames)
- Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to
do comparision with the 'convert-dbug-for-diff' tool.
Things left to do in 5.1:
- We wrongly log failed CREATE TABLE ... SELECT in some cases when using
row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result)
Mats has promised to look into this.
- Test that my fix for CREATE TABLE ... SELECT is indeed correct.
(I added several test cases for this, but in this case it's better that
someone else also tests this throughly).
Lars has promosed to do this.
2006-06-04 17:52:22 +02:00
|
|
|
table->use_all_columns();
|
2000-07-31 21:29:14 +02:00
|
|
|
|
|
|
|
/* Check that we are not trying to rename to an existing table */
|
|
|
|
if (new_name)
|
|
|
|
{
|
|
|
|
strmov(new_name_buff,new_name);
|
2004-02-06 14:40:44 +01:00
|
|
|
strmov(new_alias= new_alias_buff, new_name);
|
2001-08-10 16:37:37 +02:00
|
|
|
if (lower_case_table_names)
|
2003-12-30 12:14:21 +01:00
|
|
|
{
|
|
|
|
if (lower_case_table_names != 2)
|
|
|
|
{
|
2004-05-22 21:41:58 +02:00
|
|
|
my_casedn_str(files_charset_info, new_name_buff);
|
2003-12-30 12:14:21 +01:00
|
|
|
new_alias= new_name; // Create lower case table name
|
|
|
|
}
|
2004-05-22 21:41:58 +02:00
|
|
|
my_casedn_str(files_charset_info, new_name);
|
2003-12-30 12:14:21 +01:00
|
|
|
}
|
2004-02-06 12:28:57 +01:00
|
|
|
if (new_db == db &&
|
2004-02-11 00:06:46 +01:00
|
|
|
!my_strcasecmp(table_alias_charset, new_name_buff, table_name))
|
2004-02-06 12:28:57 +01:00
|
|
|
{
|
|
|
|
/*
|
2004-04-08 16:56:45 +02:00
|
|
|
Source and destination table names are equal: make later check
|
|
|
|
easier.
|
2004-02-06 12:28:57 +01:00
|
|
|
*/
|
2004-02-06 13:23:41 +01:00
|
|
|
new_alias= new_name= table_name;
|
2004-02-06 12:28:57 +01:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
else
|
|
|
|
{
|
2005-11-23 21:45:02 +01:00
|
|
|
if (table->s->tmp_table != NO_TMP_TABLE)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
|
|
|
if (find_temporary_table(thd,new_db,new_name_buff))
|
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name_buff);
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2004-09-07 08:55:34 +02:00
|
|
|
char dir_buff[FN_REFLEN];
|
2005-11-23 21:45:02 +01:00
|
|
|
strxnmov(dir_buff, sizeof(dir_buff)-1,
|
|
|
|
mysql_real_data_home, new_db, NullS);
|
2004-09-07 08:55:34 +02:00
|
|
|
if (!access(fn_format(new_name_buff,new_name_buff,dir_buff,reg_ext,0),
|
2000-07-31 21:29:14 +02:00
|
|
|
F_OK))
|
|
|
|
{
|
|
|
|
/* Table will be closed in do_command() */
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias);
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2004-06-23 15:44:34 +02:00
|
|
|
{
|
|
|
|
new_alias= (lower_case_table_names == 2) ? alias : table_name;
|
|
|
|
new_name= table_name;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2005-01-06 12:00:13 +01:00
|
|
|
old_db_type= table->s->db_type;
|
2006-05-28 14:51:01 +02:00
|
|
|
if (!create_info->db_type)
|
2006-06-14 04:46:38 +02:00
|
|
|
{
|
2006-06-16 21:04:35 +02:00
|
|
|
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
2006-06-15 21:56:47 +02:00
|
|
|
if (table->part_info &&
|
|
|
|
create_info->used_fields & HA_CREATE_USED_ENGINE)
|
2006-06-14 04:46:38 +02:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
This case happens when the user specified
|
|
|
|
ENGINE = x where x is a non-existing storage engine
|
2006-06-15 21:56:47 +02:00
|
|
|
We set create_info->db_type to default_engine_type
|
|
|
|
to ensure we don't change underlying engine type
|
|
|
|
due to a erroneously given engine name.
|
2006-06-14 04:46:38 +02:00
|
|
|
*/
|
2006-06-15 21:56:47 +02:00
|
|
|
create_info->db_type= table->part_info->default_engine_type;
|
2006-06-14 04:46:38 +02:00
|
|
|
}
|
2006-06-15 21:56:47 +02:00
|
|
|
else
|
2006-06-16 21:04:35 +02:00
|
|
|
#endif
|
2006-06-15 21:56:47 +02:00
|
|
|
create_info->db_type= old_db_type;
|
2006-06-14 04:46:38 +02:00
|
|
|
}
|
2005-08-19 16:26:05 +02:00
|
|
|
|
2005-11-07 16:25:06 +01:00
|
|
|
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
2006-01-17 08:40:00 +01:00
|
|
|
if (prep_alter_part_table(thd, table, alter_info, create_info, old_db_type,
|
|
|
|
&partition_changed, &fast_alter_partition))
|
2005-07-18 13:31:02 +02:00
|
|
|
{
|
2006-01-17 08:40:00 +01:00
|
|
|
DBUG_RETURN(TRUE);
|
2005-07-18 13:31:02 +02:00
|
|
|
}
|
|
|
|
#endif
|
2006-02-17 17:12:35 +01:00
|
|
|
if (check_engine(thd, new_name, create_info))
|
2005-06-17 23:14:44 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
|
|
|
new_db_type= create_info->db_type;
|
2001-09-30 04:47:35 +02:00
|
|
|
if (create_info->row_type == ROW_TYPE_NOT_USED)
|
2005-01-06 12:00:13 +01:00
|
|
|
create_info->row_type= table->s->row_type;
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2006-01-17 09:25:12 +01:00
|
|
|
DBUG_PRINT("info", ("old type: %s new type: %s",
|
|
|
|
ha_resolve_storage_engine_name(old_db_type),
|
|
|
|
ha_resolve_storage_engine_name(new_db_type)));
|
2005-10-11 23:58:22 +02:00
|
|
|
if (ha_check_storage_engine_flag(old_db_type, HTON_ALTER_NOT_SUPPORTED) ||
|
2006-05-09 22:31:46 +02:00
|
|
|
ha_check_storage_engine_flag(new_db_type, HTON_ALTER_NOT_SUPPORTED) ||
|
|
|
|
(old_db_type != new_db_type &&
|
|
|
|
ha_check_storage_engine_flag(new_db_type, HTON_ALTER_CANNOT_CREATE)))
|
2005-10-01 01:26:48 +02:00
|
|
|
{
|
|
|
|
DBUG_PRINT("info", ("doesn't support alter"));
|
|
|
|
my_error(ER_ILLEGAL_HA, MYF(0), table_name);
|
|
|
|
DBUG_RETURN(TRUE);
|
|
|
|
}
|
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
thd->proc_info="setup";
|
2004-10-21 18:10:58 +02:00
|
|
|
if (!(alter_info->flags & ~(ALTER_RENAME | ALTER_KEYS_ONOFF)) &&
|
2005-01-06 12:00:13 +01:00
|
|
|
!table->s->tmp_table) // no need to touch frm
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
|
|
|
error=0;
|
2002-06-03 23:40:27 +02:00
|
|
|
if (new_name != table_name || new_db != db)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2001-05-11 22:26:12 +02:00
|
|
|
thd->proc_info="rename";
|
|
|
|
VOID(pthread_mutex_lock(&LOCK_open));
|
|
|
|
/* Then do a 'simple' rename of the table */
|
|
|
|
error=0;
|
|
|
|
if (!access(new_name_buff,F_OK))
|
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name);
|
2004-04-08 16:56:45 +02:00
|
|
|
error= -1;
|
2001-05-11 22:26:12 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2004-04-08 16:56:45 +02:00
|
|
|
*fn_ext(new_name)=0;
|
2005-11-23 21:45:02 +01:00
|
|
|
table->s->version= 0; // Force removal of table def
|
2004-04-08 16:56:45 +02:00
|
|
|
close_cached_table(thd, table);
|
|
|
|
if (mysql_rename_table(old_db_type,db,table_name,new_db,new_alias))
|
2001-05-11 22:26:12 +02:00
|
|
|
error= -1;
|
2006-02-24 21:50:36 +01:00
|
|
|
else if (Table_triggers_list::change_table_name(thd, db, table_name,
|
|
|
|
new_db, new_alias))
|
|
|
|
{
|
|
|
|
VOID(mysql_rename_table(old_db_type, new_db, new_alias, db,
|
|
|
|
table_name));
|
|
|
|
error= -1;
|
|
|
|
}
|
2001-05-11 22:26:12 +02:00
|
|
|
}
|
|
|
|
VOID(pthread_mutex_unlock(&LOCK_open));
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2003-10-13 10:20:19 +02:00
|
|
|
|
2001-05-11 22:26:12 +02:00
|
|
|
if (!error)
|
2001-03-21 21:34:16 +01:00
|
|
|
{
|
2004-05-21 16:57:03 +02:00
|
|
|
switch (alter_info->keys_onoff) {
|
2002-06-03 23:40:27 +02:00
|
|
|
case LEAVE_AS_IS:
|
2004-10-21 18:10:58 +02:00
|
|
|
break;
|
2002-06-03 23:40:27 +02:00
|
|
|
case ENABLE:
|
2004-10-21 18:10:58 +02:00
|
|
|
VOID(pthread_mutex_lock(&LOCK_open));
|
|
|
|
wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
|
|
|
|
VOID(pthread_mutex_unlock(&LOCK_open));
|
|
|
|
error= table->file->enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
|
|
|
|
/* COND_refresh will be signaled in close_thread_tables() */
|
|
|
|
break;
|
2002-06-03 23:40:27 +02:00
|
|
|
case DISABLE:
|
2004-10-21 18:10:58 +02:00
|
|
|
VOID(pthread_mutex_lock(&LOCK_open));
|
|
|
|
wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
|
|
|
|
VOID(pthread_mutex_unlock(&LOCK_open));
|
|
|
|
error=table->file->disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
|
|
|
|
/* COND_refresh will be signaled in close_thread_tables() */
|
|
|
|
break;
|
2001-05-11 22:26:12 +02:00
|
|
|
}
|
2001-03-21 21:34:16 +01:00
|
|
|
}
|
2004-10-21 18:10:58 +02:00
|
|
|
|
2004-04-08 16:56:45 +02:00
|
|
|
if (error == HA_ERR_WRONG_COMMAND)
|
2004-04-07 16:04:28 +02:00
|
|
|
{
|
|
|
|
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
|
2004-04-08 16:56:45 +02:00
|
|
|
ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
|
2005-01-06 12:00:13 +01:00
|
|
|
table->alias);
|
2004-04-07 16:04:28 +02:00
|
|
|
error=0;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
if (!error)
|
|
|
|
{
|
2005-12-22 06:39:02 +01:00
|
|
|
write_bin_log(thd, TRUE, thd->query, thd->query_length);
|
2004-06-10 16:41:24 +02:00
|
|
|
if (do_send_ok)
|
|
|
|
send_ok(thd);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2004-08-07 18:26:59 +02:00
|
|
|
else if (error > 0)
|
2004-04-06 21:35:26 +02:00
|
|
|
{
|
|
|
|
table->file->print_error(error, MYF(0));
|
2004-04-08 16:56:45 +02:00
|
|
|
error= -1;
|
2004-04-06 21:35:26 +02:00
|
|
|
}
|
2003-01-24 00:54:39 +01:00
|
|
|
table_list->table=0; // For query cache
|
|
|
|
query_cache_invalidate3(thd, table_list, 0);
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Full alter table */
|
2003-08-11 15:18:34 +02:00
|
|
|
|
2004-10-21 18:10:58 +02:00
|
|
|
/* Let new create options override the old ones */
|
2003-08-11 15:18:34 +02:00
|
|
|
if (!(used_fields & HA_CREATE_USED_MIN_ROWS))
|
2005-01-06 12:00:13 +01:00
|
|
|
create_info->min_rows= table->s->min_rows;
|
2003-08-11 15:18:34 +02:00
|
|
|
if (!(used_fields & HA_CREATE_USED_MAX_ROWS))
|
2005-01-06 12:00:13 +01:00
|
|
|
create_info->max_rows= table->s->max_rows;
|
2003-08-11 15:18:34 +02:00
|
|
|
if (!(used_fields & HA_CREATE_USED_AVG_ROW_LENGTH))
|
2005-01-06 12:00:13 +01:00
|
|
|
create_info->avg_row_length= table->s->avg_row_length;
|
2003-11-18 12:47:27 +01:00
|
|
|
if (!(used_fields & HA_CREATE_USED_DEFAULT_CHARSET))
|
2005-01-06 12:00:13 +01:00
|
|
|
create_info->default_table_charset= table->s->table_charset;
|
2006-05-03 14:59:17 +02:00
|
|
|
if (!(used_fields & HA_CREATE_USED_KEY_BLOCK_SIZE))
|
|
|
|
create_info->key_block_size= table->s->key_block_size;
|
2003-08-11 15:18:34 +02:00
|
|
|
|
2005-01-06 12:00:13 +01:00
|
|
|
restore_record(table, s->default_values); // Empty record for DEFAULT
|
2004-05-21 16:57:03 +02:00
|
|
|
List_iterator<Alter_drop> drop_it(alter_info->drop_list);
|
2000-07-31 21:29:14 +02:00
|
|
|
List_iterator<create_field> def_it(fields);
|
2004-05-21 16:57:03 +02:00
|
|
|
List_iterator<Alter_column> alter_it(alter_info->alter_list);
|
2000-07-31 21:29:14 +02:00
|
|
|
List<create_field> create_list; // Add new fields here
|
|
|
|
List<Key> key_list; // Add new keys here
|
2003-08-11 15:18:34 +02:00
|
|
|
create_field *def;
|
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
/*
|
2002-06-02 20:22:20 +02:00
|
|
|
First collect all fields from table which isn't in drop_list
|
2000-07-31 21:29:14 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
Field **f_ptr,*field;
|
|
|
|
for (f_ptr=table->field ; (field= *f_ptr) ; f_ptr++)
|
|
|
|
{
|
2006-05-16 15:37:23 +02:00
|
|
|
if (field->type() == MYSQL_TYPE_STRING)
|
|
|
|
varchar= TRUE;
|
2005-08-30 21:24:37 +02:00
|
|
|
/* Check if field should be dropped */
|
2000-07-31 21:29:14 +02:00
|
|
|
Alter_drop *drop;
|
|
|
|
drop_it.rewind();
|
|
|
|
while ((drop=drop_it++))
|
|
|
|
{
|
|
|
|
if (drop->type == Alter_drop::COLUMN &&
|
2002-03-12 18:37:58 +01:00
|
|
|
!my_strcasecmp(system_charset_info,field->field_name, drop->name))
|
2001-06-11 14:01:28 +02:00
|
|
|
{
|
|
|
|
/* Reset auto_increment value if it was dropped */
|
|
|
|
if (MTYP_TYPENR(field->unireg_check) == Field::NEXT_NUMBER &&
|
2001-09-30 04:47:35 +02:00
|
|
|
!(used_fields & HA_CREATE_USED_AUTO))
|
2001-06-11 14:01:28 +02:00
|
|
|
{
|
|
|
|
create_info->auto_increment_value=0;
|
|
|
|
create_info->used_fields|=HA_CREATE_USED_AUTO;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2001-06-11 14:01:28 +02:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
if (drop)
|
|
|
|
{
|
|
|
|
drop_it.remove();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
/* Check if field is changed */
|
|
|
|
def_it.rewind();
|
|
|
|
while ((def=def_it++))
|
|
|
|
{
|
2004-01-28 18:35:52 +01:00
|
|
|
if (def->change &&
|
2004-04-08 16:56:45 +02:00
|
|
|
!my_strcasecmp(system_charset_info,field->field_name, def->change))
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (def)
|
|
|
|
{ // Field is changed
|
|
|
|
def->field=field;
|
2001-10-29 09:49:35 +01:00
|
|
|
if (!def->after)
|
|
|
|
{
|
|
|
|
create_list.push_back(def);
|
|
|
|
def_it.remove();
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes
Changes that requires code changes in other code of other storage engines.
(Note that all changes are very straightforward and one should find all issues
by compiling a --debug build and fixing all compiler errors and all
asserts in field.cc while running the test suite),
- New optional handler function introduced: reset()
This is called after every DML statement to make it easy for a handler to
statement specific cleanups.
(The only case it's not called is if force the file to be closed)
- handler::extra(HA_EXTRA_RESET) is removed. Code that was there before
should be moved to handler::reset()
- table->read_set contains a bitmap over all columns that are needed
in the query. read_row() and similar functions only needs to read these
columns
- table->write_set contains a bitmap over all columns that will be updated
in the query. write_row() and update_row() only needs to update these
columns.
The above bitmaps should now be up to date in all context
(including ALTER TABLE, filesort()).
The handler is informed of any changes to the bitmap after
fix_fields() by calling the virtual function
handler::column_bitmaps_signal(). If the handler does caching of
these bitmaps (instead of using table->read_set, table->write_set),
it should redo the caching in this code. as the signal() may be sent
several times, it's probably best to set a variable in the signal
and redo the caching on read_row() / write_row() if the variable was
set.
- Removed the read_set and write_set bitmap objects from the handler class
- Removed all column bit handling functions from the handler class.
(Now one instead uses the normal bitmap functions in my_bitmap.c instead
of handler dedicated bitmap functions)
- field->query_id is removed. One should instead instead check
table->read_set and table->write_set if a field is used in the query.
- handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and
handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now
instead use table->read_set to check for which columns to retrieve.
- If a handler needs to call Field->val() or Field->store() on columns
that are not used in the query, one should install a temporary
all-columns-used map while doing so. For this, we provide the following
functions:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
field->val();
dbug_tmp_restore_column_map(table->read_set, old_map);
and similar for the write map:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
field->val();
dbug_tmp_restore_column_map(table->write_set, old_map);
If this is not done, you will sooner or later hit a DBUG_ASSERT
in the field store() / val() functions.
(For not DBUG binaries, the dbug_tmp_restore_column_map() and
dbug_tmp_restore_column_map() are inline dummy functions and should
be optimized away be the compiler).
- If one needs to temporary set the column map for all binaries (and not
just to avoid the DBUG_ASSERT() in the Field::store() / Field::val()
methods) one should use the functions tmp_use_all_columns() and
tmp_restore_column_map() instead of the above dbug_ variants.
- All 'status' fields in the handler base class (like records,
data_file_length etc) are now stored in a 'stats' struct. This makes
it easier to know what status variables are provided by the base
handler. This requires some trivial variable names in the extra()
function.
- New virtual function handler::records(). This is called to optimize
COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true.
(stats.records is not supposed to be an exact value. It's only has to
be 'reasonable enough' for the optimizer to be able to choose a good
optimization path).
- Non virtual handler::init() function added for caching of virtual
constants from engine.
- Removed has_transactions() virtual method. Now one should instead return
HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support
transactions.
- The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument
that is to be used with 'new handler_name()' to allocate the handler
in the right area. The xxxx_create_handler() function is also
responsible for any initialization of the object before returning.
For example, one should change:
static handler *myisam_create_handler(TABLE_SHARE *table)
{
return new ha_myisam(table);
}
->
static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root)
{
return new (mem_root) ha_myisam(table);
}
- New optional virtual function: use_hidden_primary_key().
This is called in case of an update/delete when
(table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
but we don't have a primary key. This allows the handler to take precisions
in remembering any hidden primary key to able to update/delete any
found row. The default handler marks all columns to be read.
- handler::table_flags() now returns a ulonglong (to allow for more flags).
- New/changed table_flags()
- HA_HAS_RECORDS Set if ::records() is supported
- HA_NO_TRANSACTIONS Set if engine doesn't support transactions
- HA_PRIMARY_KEY_REQUIRED_FOR_DELETE
Set if we should mark all primary key columns for
read when reading rows as part of a DELETE
statement. If there is no primary key,
all columns are marked for read.
- HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some
cases (based on table->read_set)
- HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS
Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION.
- HA_DUPP_POS Renamed to HA_DUPLICATE_POS
- HA_REQUIRES_KEY_COLUMNS_FOR_DELETE
Set this if we should mark ALL key columns for
read when when reading rows as part of a DELETE
statement. In case of an update we will mark
all keys for read for which key part changed
value.
- HA_STATS_RECORDS_IS_EXACT
Set this if stats.records is exact.
(This saves us some extra records() calls
when optimizing COUNT(*))
- Removed table_flags()
- HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if
handler::records() gives an exact count() and
HA_STATS_RECORDS_IS_EXACT if stats.records is exact.
- HA_READ_RND_SAME Removed (no one supported this one)
- Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk()
- Renamed handler::dupp_pos to handler::dup_pos
- Removed not used variable handler::sortkey
Upper level handler changes:
- ha_reset() now does some overall checks and calls ::reset()
- ha_table_flags() added. This is a cached version of table_flags(). The
cache is updated on engine creation time and updated on open.
MySQL level changes (not obvious from the above):
- DBUG_ASSERT() added to check that column usage matches what is set
in the column usage bit maps. (This found a LOT of bugs in current
column marking code).
- In 5.1 before, all used columns was marked in read_set and only updated
columns was marked in write_set. Now we only mark columns for which we
need a value in read_set.
- Column bitmaps are created in open_binary_frm() and open_table_from_share().
(Before this was in table.cc)
- handler::table_flags() calls are replaced with handler::ha_table_flags()
- For calling field->val() you must have the corresponding bit set in
table->read_set. For calling field->store() you must have the
corresponding bit set in table->write_set. (There are asserts in
all store()/val() functions to catch wrong usage)
- thd->set_query_id is renamed to thd->mark_used_columns and instead
of setting this to an integer value, this has now the values:
MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE
Changed also all variables named 'set_query_id' to mark_used_columns.
- In filesort() we now inform the handler of exactly which columns are needed
doing the sort and choosing the rows.
- The TABLE_SHARE object has a 'all_set' column bitmap one can use
when one needs a column bitmap with all columns set.
(This is used for table->use_all_columns() and other places)
- The TABLE object has 3 column bitmaps:
- def_read_set Default bitmap for columns to be read
- def_write_set Default bitmap for columns to be written
- tmp_set Can be used as a temporary bitmap when needed.
The table object has also two pointer to bitmaps read_set and write_set
that the handler should use to find out which columns are used in which way.
- count() optimization now calls handler::records() instead of using
handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true).
- Added extra argument to Item::walk() to indicate if we should also
traverse sub queries.
- Added TABLE parameter to cp_buffer_from_ref()
- Don't close tables created with CREATE ... SELECT but keep them in
the table cache. (Faster usage of newly created tables).
New interfaces:
- table->clear_column_bitmaps() to initialize the bitmaps for tables
at start of new statements.
- table->column_bitmaps_set() to set up new column bitmaps and signal
the handler about this.
- table->column_bitmaps_set_no_signal() for some few cases where we need
to setup new column bitmaps but don't signal the handler (as the handler
has already been signaled about these before). Used for the momement
only in opt_range.cc when doing ROR scans.
- table->use_all_columns() to install a bitmap where all columns are marked
as use in the read and the write set.
- table->default_column_bitmaps() to install the normal read and write
column bitmaps, but not signaling the handler about this.
This is mainly used when creating TABLE instances.
- table->mark_columns_needed_for_delete(),
table->mark_columns_needed_for_delete() and
table->mark_columns_needed_for_insert() to allow us to put additional
columns in column usage maps if handler so requires.
(The handler indicates what it neads in handler->table_flags())
- table->prepare_for_position() to allow us to tell handler that it
needs to read primary key parts to be able to store them in
future table->position() calls.
(This replaces the table->file->ha_retrieve_all_pk function)
- table->mark_auto_increment_column() to tell handler are going to update
columns part of any auto_increment key.
- table->mark_columns_used_by_index() to mark all columns that is part of
an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow
it to quickly know that it only needs to read colums that are part
of the key. (The handler can also use the column map for detecting this,
but simpler/faster handler can just monitor the extra() call).
- table->mark_columns_used_by_index_no_reset() to in addition to other columns,
also mark all columns that is used by the given key.
- table->restore_column_maps_after_mark_index() to restore to default
column maps after a call to table->mark_columns_used_by_index().
- New item function register_field_in_read_map(), for marking used columns
in table->read_map. Used by filesort() to mark all used columns
- Maintain in TABLE->merge_keys set of all keys that are used in query.
(Simplices some optimization loops)
- Maintain Field->part_of_key_not_clustered which is like Field->part_of_key
but the field in the clustered key is not assumed to be part of all index.
(used in opt_range.cc for faster loops)
- dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map()
tmp_use_all_columns() and tmp_restore_column_map() functions to temporally
mark all columns as usable. The 'dbug_' version is primarily intended
inside a handler when it wants to just call Field:store() & Field::val()
functions, but don't need the column maps set for any other usage.
(ie:: bitmap_is_set() is never called)
- We can't use compare_records() to skip updates for handlers that returns
a partial column set and the read_set doesn't cover all columns in the
write set. The reason for this is that if we have a column marked only for
write we can't in the MySQL level know if the value changed or not.
The reason this worked before was that MySQL marked all to be written
columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden
bug'.
- open_table_from_share() does not anymore setup temporary MEM_ROOT
object as a thread specific variable for the handler. Instead we
send the to-be-used MEMROOT to get_new_handler().
(Simpler, faster code)
Bugs fixed:
- Column marking was not done correctly in a lot of cases.
(ALTER TABLE, when using triggers, auto_increment fields etc)
(Could potentially result in wrong values inserted in table handlers
relying on that the old column maps or field->set_query_id was correct)
Especially when it comes to triggers, there may be cases where the
old code would cause lost/wrong values for NDB and/or InnoDB tables.
- Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags:
OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG.
This allowed me to remove some wrong warnings about:
"Some non-transactional changed tables couldn't be rolled back"
- Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset
(thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose
some warnings about
"Some non-transactional changed tables couldn't be rolled back")
- Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table()
which could cause delete_table to report random failures.
- Fixed core dumps for some tests when running with --debug
- Added missing FN_LIBCHAR in mysql_rm_tmp_tables()
(This has probably caused us to not properly remove temporary files after
crash)
- slow_logs was not properly initialized, which could maybe cause
extra/lost entries in slow log.
- If we get an duplicate row on insert, change column map to read and
write all columns while retrying the operation. This is required by
the definition of REPLACE and also ensures that fields that are only
part of UPDATE are properly handled. This fixed a bug in NDB and
REPLACE where REPLACE wrongly copied some column values from the replaced
row.
- For table handler that doesn't support NULL in keys, we would give an error
when creating a primary key with NULL fields, even after the fields has been
automaticly converted to NOT NULL.
- Creating a primary key on a SPATIAL key, would fail if field was not
declared as NOT NULL.
Cleanups:
- Removed not used condition argument to setup_tables
- Removed not needed item function reset_query_id_processor().
- Field->add_index is removed. Now this is instead maintained in
(field->flags & FIELD_IN_ADD_INDEX)
- Field->fieldnr is removed (use field->field_index instead)
- New argument to filesort() to indicate that it should return a set of
row pointers (not used columns). This allowed me to remove some references
to sql_command in filesort and should also enable us to return column
results in some cases where we couldn't before.
- Changed column bitmap handling in opt_range.cc to be aligned with TABLE
bitmap, which allowed me to use bitmap functions instead of looping over
all fields to create some needed bitmaps. (Faster and smaller code)
- Broke up found too long lines
- Moved some variable declaration at start of function for better code
readability.
- Removed some not used arguments from functions.
(setup_fields(), mysql_prepare_insert_check_table())
- setup_fields() now takes an enum instead of an int for marking columns
usage.
- For internal temporary tables, use handler::write_row(),
handler::delete_row() and handler::update_row() instead of
handler::ha_xxxx() for faster execution.
- Changed some constants to enum's and define's.
- Using separate column read and write sets allows for easier checking
of timestamp field was set by statement.
- Remove calls to free_io_cache() as this is now done automaticly in ha_reset()
- Don't build table->normalized_path as this is now identical to table->path
(after bar's fixes to convert filenames)
- Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to
do comparision with the 'convert-dbug-for-diff' tool.
Things left to do in 5.1:
- We wrongly log failed CREATE TABLE ... SELECT in some cases when using
row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result)
Mats has promised to look into this.
- Test that my fix for CREATE TABLE ... SELECT is indeed correct.
(I added several test cases for this, but in this case it's better that
someone else also tests this throughly).
Lars has promosed to do this.
2006-06-04 17:52:22 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
This field was not dropped and not changed, add it to the list
|
|
|
|
for the new table.
|
|
|
|
*/
|
2000-10-14 02:16:35 +02:00
|
|
|
create_list.push_back(def=new create_field(field,field));
|
2000-07-31 21:29:14 +02:00
|
|
|
alter_it.rewind(); // Change default if ALTER
|
|
|
|
Alter_column *alter;
|
|
|
|
while ((alter=alter_it++))
|
|
|
|
{
|
2002-03-12 18:37:58 +01:00
|
|
|
if (!my_strcasecmp(system_charset_info,field->field_name, alter->name))
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (alter)
|
|
|
|
{
|
2004-04-08 16:56:45 +02:00
|
|
|
if (def->sql_type == FIELD_TYPE_BLOB)
|
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_BLOB_CANT_HAVE_DEFAULT, MYF(0), def->change);
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
2004-04-08 16:56:45 +02:00
|
|
|
}
|
2005-11-24 17:24:45 +01:00
|
|
|
if ((def->def=alter->def)) // Use new default
|
|
|
|
def->flags&= ~NO_DEFAULT_VALUE_FLAG;
|
|
|
|
else
|
|
|
|
def->flags|= NO_DEFAULT_VALUE_FLAG;
|
2000-07-31 21:29:14 +02:00
|
|
|
alter_it.remove();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
def_it.rewind();
|
|
|
|
List_iterator<create_field> find_it(create_list);
|
|
|
|
while ((def=def_it++)) // Add new columns
|
|
|
|
{
|
2001-10-29 09:49:35 +01:00
|
|
|
if (def->change && ! def->field)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_BAD_FIELD_ERROR, MYF(0), def->change, table_name);
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
if (!def->after)
|
|
|
|
create_list.push_back(def);
|
|
|
|
else if (def->after == first_keyword)
|
|
|
|
create_list.push_front(def);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
create_field *find;
|
|
|
|
find_it.rewind();
|
|
|
|
while ((find=find_it++)) // Add new columns
|
|
|
|
{
|
2002-03-12 18:37:58 +01:00
|
|
|
if (!my_strcasecmp(system_charset_info,def->after, find->field_name))
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!find)
|
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_BAD_FIELD_ERROR, MYF(0), def->after, table_name);
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
find_it.after(def); // Put element after this
|
|
|
|
}
|
|
|
|
}
|
2004-05-21 16:57:03 +02:00
|
|
|
if (alter_info->alter_list.elements)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_BAD_FIELD_ERROR, MYF(0),
|
|
|
|
alter_info->alter_list.head()->name, table_name);
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
if (!create_list.elements)
|
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_CANT_REMOVE_ALL_FIELDS, ER(ER_CANT_REMOVE_ALL_FIELDS),
|
|
|
|
MYF(0));
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2002-06-02 20:22:20 +02:00
|
|
|
Collect all keys which isn't in drop list. Add only those
|
|
|
|
for which some fields exists.
|
2000-07-31 21:29:14 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
List_iterator<Key> key_it(keys);
|
|
|
|
List_iterator<create_field> field_it(create_list);
|
|
|
|
List<key_part_spec> key_parts;
|
|
|
|
|
|
|
|
KEY *key_info=table->key_info;
|
2005-01-06 12:00:13 +01:00
|
|
|
for (uint i=0 ; i < table->s->keys ; i++,key_info++)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-01-13 13:18:37 +01:00
|
|
|
char *key_name= key_info->name;
|
2000-07-31 21:29:14 +02:00
|
|
|
Alter_drop *drop;
|
|
|
|
drop_it.rewind();
|
|
|
|
while ((drop=drop_it++))
|
|
|
|
{
|
|
|
|
if (drop->type == Alter_drop::KEY &&
|
2002-03-12 18:37:58 +01:00
|
|
|
!my_strcasecmp(system_charset_info,key_name, drop->name))
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (drop)
|
|
|
|
{
|
|
|
|
drop_it.remove();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
KEY_PART_INFO *key_part= key_info->key_part;
|
|
|
|
key_parts.empty();
|
|
|
|
for (uint j=0 ; j < key_info->key_parts ; j++,key_part++)
|
|
|
|
{
|
|
|
|
if (!key_part->field)
|
|
|
|
continue; // Wrong field (from UNIREG)
|
|
|
|
const char *key_part_name=key_part->field->field_name;
|
|
|
|
create_field *cfield;
|
|
|
|
field_it.rewind();
|
|
|
|
while ((cfield=field_it++))
|
|
|
|
{
|
|
|
|
if (cfield->change)
|
|
|
|
{
|
2002-06-04 07:23:57 +02:00
|
|
|
if (!my_strcasecmp(system_charset_info, key_part_name,
|
|
|
|
cfield->change))
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
2002-03-12 18:37:58 +01:00
|
|
|
else if (!my_strcasecmp(system_charset_info,
|
2004-04-08 16:56:45 +02:00
|
|
|
key_part_name, cfield->field_name))
|
2002-06-04 07:23:57 +02:00
|
|
|
break;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
if (!cfield)
|
|
|
|
continue; // Field is removed
|
|
|
|
uint key_part_length=key_part->length;
|
|
|
|
if (cfield->field) // Not new field
|
2005-05-26 03:11:47 +02:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
If the field can't have only a part used in a key according to its
|
|
|
|
new type, or should not be used partially according to its
|
|
|
|
previous type, or the field length is less than the key part
|
|
|
|
length, unset the key part length.
|
|
|
|
|
2005-06-02 19:00:36 +02:00
|
|
|
We also unset the key part length if it is the same as the
|
|
|
|
old field's length, so the whole new field will be used.
|
|
|
|
|
2005-05-26 03:11:47 +02:00
|
|
|
BLOBs may have cfield->length == 0, which is why we test it before
|
|
|
|
checking whether cfield->length < key_part_length (in chars).
|
|
|
|
*/
|
|
|
|
if (!Field::type_can_have_key_part(cfield->field->type()) ||
|
|
|
|
!Field::type_can_have_key_part(cfield->sql_type) ||
|
2005-07-01 12:07:06 +02:00
|
|
|
(cfield->field->field_length == key_part_length &&
|
|
|
|
!f_is_blob(key_part->key_type)) ||
|
2005-05-26 03:11:47 +02:00
|
|
|
(cfield->length && (cfield->length < key_part_length /
|
|
|
|
key_part->field->charset()->mbmaxlen)))
|
|
|
|
key_part_length= 0; // Use whole field
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2003-09-15 11:45:42 +02:00
|
|
|
key_part_length /= key_part->field->charset()->mbmaxlen;
|
2000-07-31 21:29:14 +02:00
|
|
|
key_parts.push_back(new key_part_spec(cfield->field_name,
|
|
|
|
key_part_length));
|
|
|
|
}
|
|
|
|
if (key_parts.elements)
|
2006-05-03 14:59:17 +02:00
|
|
|
{
|
|
|
|
KEY_CREATE_INFO key_create_info;
|
|
|
|
bzero((char*) &key_create_info, sizeof(key_create_info));
|
|
|
|
|
|
|
|
key_create_info.algorithm= key_info->algorithm;
|
|
|
|
if (key_info->flags & HA_USES_BLOCK_SIZE)
|
|
|
|
key_create_info.block_size= key_info->block_size;
|
|
|
|
if (key_info->flags & HA_USES_PARSER)
|
|
|
|
key_create_info.parser_name= *key_info->parser_name;
|
|
|
|
|
2002-02-22 12:24:42 +01:00
|
|
|
key_list.push_back(new Key(key_info->flags & HA_SPATIAL ? Key::SPATIAL :
|
2004-04-08 16:56:45 +02:00
|
|
|
(key_info->flags & HA_NOSAME ?
|
2002-03-12 18:37:58 +01:00
|
|
|
(!my_strcasecmp(system_charset_info,
|
2004-04-08 16:56:45 +02:00
|
|
|
key_name, primary_key_name) ?
|
|
|
|
Key::PRIMARY : Key::UNIQUE) :
|
2002-06-02 20:22:20 +02:00
|
|
|
(key_info->flags & HA_FULLTEXT ?
|
|
|
|
Key::FULLTEXT : Key::MULTIPLE)),
|
|
|
|
key_name,
|
2006-05-03 14:59:17 +02:00
|
|
|
&key_create_info,
|
2004-05-11 23:29:52 +02:00
|
|
|
test(key_info->flags & HA_GENERATED_KEY),
|
2006-05-03 14:59:17 +02:00
|
|
|
key_parts));
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
{
|
|
|
|
Key *key;
|
|
|
|
while ((key=key_it++)) // Add new keys
|
2002-06-02 20:22:20 +02:00
|
|
|
{
|
|
|
|
if (key->type != Key::FOREIGN_KEY)
|
|
|
|
key_list.push_back(key);
|
2004-03-13 22:31:30 +01:00
|
|
|
if (key->name &&
|
|
|
|
!my_strcasecmp(system_charset_info,key->name,primary_key_name))
|
|
|
|
{
|
|
|
|
my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name);
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
2004-03-13 22:31:30 +01:00
|
|
|
}
|
2002-06-02 20:22:20 +02:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
2004-05-21 16:57:03 +02:00
|
|
|
if (alter_info->drop_list.elements)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0),
|
|
|
|
alter_info->drop_list.head()->name);
|
2000-07-31 21:29:14 +02:00
|
|
|
goto err;
|
|
|
|
}
|
2004-05-21 16:57:03 +02:00
|
|
|
if (alter_info->alter_list.elements)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0),
|
|
|
|
alter_info->alter_list.head()->name);
|
2000-07-31 21:29:14 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2005-01-06 12:00:13 +01:00
|
|
|
db_create_options= table->s->db_create_options & ~(HA_OPTION_PACK_RECORD);
|
2004-04-08 16:56:45 +02:00
|
|
|
my_snprintf(tmp_name, sizeof(tmp_name), "%s-%lx_%lx", tmp_file_prefix,
|
|
|
|
current_pid, thd->thread_id);
|
2004-05-07 05:34:44 +02:00
|
|
|
/* Safety fix for innodb */
|
|
|
|
if (lower_case_table_names)
|
2004-05-22 21:41:58 +02:00
|
|
|
my_casedn_str(files_charset_info, tmp_name);
|
2005-04-07 11:16:41 +02:00
|
|
|
if (new_db_type != old_db_type && !table->file->can_switch_engines()) {
|
|
|
|
my_error(ER_ROW_IS_REFERENCED, MYF(0));
|
|
|
|
goto err;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
create_info->db_type=new_db_type;
|
|
|
|
if (!create_info->comment)
|
2005-01-06 12:00:13 +01:00
|
|
|
create_info->comment= table->s->comment;
|
2001-09-30 04:47:35 +02:00
|
|
|
|
|
|
|
table->file->update_create_info(create_info);
|
|
|
|
if ((create_info->table_options &
|
|
|
|
(HA_OPTION_PACK_KEYS | HA_OPTION_NO_PACK_KEYS)) ||
|
|
|
|
(used_fields & HA_CREATE_USED_PACK_KEYS))
|
2000-07-31 21:29:14 +02:00
|
|
|
db_create_options&= ~(HA_OPTION_PACK_KEYS | HA_OPTION_NO_PACK_KEYS);
|
|
|
|
if (create_info->table_options &
|
|
|
|
(HA_OPTION_CHECKSUM | HA_OPTION_NO_CHECKSUM))
|
|
|
|
db_create_options&= ~(HA_OPTION_CHECKSUM | HA_OPTION_NO_CHECKSUM);
|
|
|
|
if (create_info->table_options &
|
|
|
|
(HA_OPTION_DELAY_KEY_WRITE | HA_OPTION_NO_DELAY_KEY_WRITE))
|
|
|
|
db_create_options&= ~(HA_OPTION_DELAY_KEY_WRITE |
|
|
|
|
HA_OPTION_NO_DELAY_KEY_WRITE);
|
|
|
|
create_info->table_options|= db_create_options;
|
|
|
|
|
2005-01-06 12:00:13 +01:00
|
|
|
if (table->s->tmp_table)
|
2000-07-31 21:29:14 +02:00
|
|
|
create_info->options|=HA_LEX_CREATE_TMP_TABLE;
|
|
|
|
|
2005-07-22 22:43:59 +02:00
|
|
|
set_table_default_charset(thd, create_info, db);
|
|
|
|
|
2006-01-12 10:05:07 +01:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
For some purposes we need prepared table structures and translated
|
|
|
|
key descriptions with proper default key name assignment.
|
|
|
|
|
|
|
|
Unfortunately, mysql_prepare_table() modifies the field and key
|
|
|
|
lists. mysql_create_table() needs the unmodified lists. Hence, we
|
|
|
|
need to copy the lists and all their elements. The lists contain
|
|
|
|
pointers to the elements only.
|
|
|
|
|
|
|
|
We cannot copy conditionally because the partition code always
|
|
|
|
needs prepared lists and compare_tables() needs them and is almost
|
|
|
|
always called.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Copy fields. */
|
|
|
|
List_iterator<create_field> prep_field_it(create_list);
|
|
|
|
create_field *prep_field;
|
|
|
|
while ((prep_field= prep_field_it++))
|
|
|
|
prepared_create_list.push_back(new create_field(*prep_field));
|
|
|
|
|
|
|
|
/* Copy keys and key parts. */
|
|
|
|
List_iterator<Key> prep_key_it(key_list);
|
|
|
|
Key *prep_key;
|
|
|
|
while ((prep_key= prep_key_it++))
|
|
|
|
{
|
|
|
|
List<key_part_spec> prep_columns;
|
|
|
|
List_iterator<key_part_spec> prep_col_it(prep_key->columns);
|
|
|
|
key_part_spec *prep_col;
|
|
|
|
|
|
|
|
while ((prep_col= prep_col_it++))
|
|
|
|
prep_columns.push_back(new key_part_spec(*prep_col));
|
|
|
|
prepared_key_list.push_back(new Key(prep_key->type, prep_key->name,
|
2006-05-03 18:40:52 +02:00
|
|
|
&prep_key->key_create_info,
|
2006-05-03 14:59:17 +02:00
|
|
|
prep_key->generated, prep_columns));
|
2006-01-12 10:05:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Create the prepared information. */
|
|
|
|
if (mysql_prepare_table(thd, create_info, &prepared_create_list,
|
|
|
|
&prepared_key_list,
|
|
|
|
(table->s->tmp_table != NO_TMP_TABLE), &db_options,
|
|
|
|
table->file, &key_info_buffer, &key_count, 0))
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (thd->variables.old_alter_table
|
|
|
|
|| (table->s->db_type != create_info->db_type)
|
2005-11-07 16:25:06 +01:00
|
|
|
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
2006-01-12 10:05:07 +01:00
|
|
|
|| partition_changed
|
2005-07-27 22:56:28 +02:00
|
|
|
#endif
|
2006-01-12 10:05:07 +01:00
|
|
|
)
|
2005-07-22 22:43:59 +02:00
|
|
|
need_copy_table= 1;
|
|
|
|
else
|
2006-01-12 10:05:07 +01:00
|
|
|
{
|
|
|
|
/* Try to optimize ALTER TABLE. Allocate result buffers. */
|
|
|
|
if (! (index_drop_buffer=
|
|
|
|
(uint*) thd->alloc(sizeof(uint) * table->s->keys)) ||
|
|
|
|
! (index_add_buffer=
|
|
|
|
(uint*) thd->alloc(sizeof(uint) * prepared_key_list.elements)))
|
|
|
|
goto err;
|
|
|
|
/* Check how much the tables differ. */
|
|
|
|
need_copy_table= compare_tables(table, &prepared_create_list,
|
|
|
|
key_info_buffer, key_count,
|
|
|
|
create_info, alter_info, order_num,
|
|
|
|
index_drop_buffer, &index_drop_count,
|
2006-05-30 15:07:49 +02:00
|
|
|
index_add_buffer, &index_add_count,
|
|
|
|
varchar);
|
2006-01-12 10:05:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
If there are index changes only, try to do them online. "Index
|
|
|
|
changes only" means also that the handler for the table does not
|
|
|
|
change. The table is open and locked. The handler can be accessed.
|
|
|
|
*/
|
|
|
|
if (need_copy_table == ALTER_TABLE_INDEX_CHANGED)
|
|
|
|
{
|
|
|
|
int pk_changed= 0;
|
2006-01-17 08:40:00 +01:00
|
|
|
ulong alter_flags= 0;
|
2006-01-12 10:05:07 +01:00
|
|
|
ulong needed_online_flags= 0;
|
|
|
|
ulong needed_fast_flags= 0;
|
|
|
|
KEY *key;
|
|
|
|
uint *idx_p;
|
|
|
|
uint *idx_end_p;
|
|
|
|
|
2006-01-17 08:40:00 +01:00
|
|
|
if (table->s->db_type->alter_table_flags)
|
|
|
|
alter_flags= table->s->db_type->alter_table_flags(alter_info->flags);
|
|
|
|
DBUG_PRINT("info", ("alter_flags: %lu", alter_flags));
|
2006-01-12 10:05:07 +01:00
|
|
|
/* Check dropped indexes. */
|
|
|
|
for (idx_p= index_drop_buffer, idx_end_p= idx_p + index_drop_count;
|
|
|
|
idx_p < idx_end_p;
|
|
|
|
idx_p++)
|
|
|
|
{
|
|
|
|
key= table->key_info + *idx_p;
|
|
|
|
DBUG_PRINT("info", ("index dropped: '%s'", key->name));
|
|
|
|
if (key->flags & HA_NOSAME)
|
|
|
|
{
|
|
|
|
/* Unique key. Check for "PRIMARY". */
|
|
|
|
if (! my_strcasecmp(system_charset_info,
|
|
|
|
key->name, primary_key_name))
|
|
|
|
{
|
|
|
|
/* Primary key. */
|
|
|
|
needed_online_flags|= HA_ONLINE_DROP_PK_INDEX;
|
|
|
|
needed_fast_flags|= HA_ONLINE_DROP_PK_INDEX_NO_WRITES;
|
|
|
|
pk_changed++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Non-primary unique key. */
|
|
|
|
needed_online_flags|= HA_ONLINE_DROP_UNIQUE_INDEX;
|
|
|
|
needed_fast_flags|= HA_ONLINE_DROP_UNIQUE_INDEX_NO_WRITES;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Non-unique key. */
|
|
|
|
needed_online_flags|= HA_ONLINE_DROP_INDEX;
|
|
|
|
needed_fast_flags|= HA_ONLINE_DROP_INDEX_NO_WRITES;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check added indexes. */
|
|
|
|
for (idx_p= index_add_buffer, idx_end_p= idx_p + index_add_count;
|
|
|
|
idx_p < idx_end_p;
|
|
|
|
idx_p++)
|
|
|
|
{
|
|
|
|
key= key_info_buffer + *idx_p;
|
|
|
|
DBUG_PRINT("info", ("index added: '%s'", key->name));
|
|
|
|
if (key->flags & HA_NOSAME)
|
|
|
|
{
|
|
|
|
/* Unique key. Check for "PRIMARY". */
|
|
|
|
if (! my_strcasecmp(system_charset_info,
|
|
|
|
key->name, primary_key_name))
|
|
|
|
{
|
|
|
|
/* Primary key. */
|
|
|
|
needed_online_flags|= HA_ONLINE_ADD_PK_INDEX;
|
|
|
|
needed_fast_flags|= HA_ONLINE_ADD_PK_INDEX_NO_WRITES;
|
|
|
|
pk_changed++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Non-primary unique key. */
|
|
|
|
needed_online_flags|= HA_ONLINE_ADD_UNIQUE_INDEX;
|
|
|
|
needed_fast_flags|= HA_ONLINE_ADD_UNIQUE_INDEX_NO_WRITES;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Non-unique key. */
|
|
|
|
needed_online_flags|= HA_ONLINE_ADD_INDEX;
|
|
|
|
needed_fast_flags|= HA_ONLINE_ADD_INDEX_NO_WRITES;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Online or fast add/drop index is possible only if
|
|
|
|
the primary key is not added and dropped in the same statement.
|
|
|
|
Otherwise we have to recreate the table.
|
|
|
|
need_copy_table is no-zero at this place.
|
|
|
|
*/
|
|
|
|
if ( pk_changed < 2 )
|
|
|
|
{
|
|
|
|
if ((alter_flags & needed_online_flags) == needed_online_flags)
|
|
|
|
{
|
|
|
|
/* All required online flags are present. */
|
|
|
|
need_copy_table= 0;
|
|
|
|
need_lock_for_indexes= FALSE;
|
|
|
|
}
|
|
|
|
else if ((alter_flags & needed_fast_flags) == needed_fast_flags)
|
|
|
|
{
|
|
|
|
/* All required fast flags are present. */
|
|
|
|
need_copy_table= 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DBUG_PRINT("info", ("need_copy_table: %u need_lock: %d",
|
|
|
|
need_copy_table, need_lock_for_indexes));
|
|
|
|
}
|
2005-07-22 22:43:59 +02:00
|
|
|
|
2004-10-21 18:10:58 +02:00
|
|
|
/*
|
|
|
|
better have a negative test here, instead of positive, like
|
2005-05-14 17:31:22 +02:00
|
|
|
alter_info->flags & ALTER_ADD_COLUMN|ALTER_ADD_INDEX|...
|
2004-10-21 18:10:58 +02:00
|
|
|
so that ALTER TABLE won't break when somebody will add new flag
|
|
|
|
*/
|
2005-07-22 22:43:59 +02:00
|
|
|
if (!need_copy_table)
|
|
|
|
create_info->frm_only= 1;
|
2004-10-21 18:10:58 +02:00
|
|
|
|
2005-11-07 16:25:06 +01:00
|
|
|
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
2006-01-17 08:40:00 +01:00
|
|
|
if (fast_alter_partition)
|
2005-08-19 16:26:05 +02:00
|
|
|
{
|
2006-01-17 08:40:00 +01:00
|
|
|
DBUG_RETURN(fast_alter_partition_table(thd, table, alter_info,
|
|
|
|
create_info, table_list,
|
|
|
|
&create_list, &key_list,
|
|
|
|
db, table_name,
|
|
|
|
fast_alter_partition));
|
2005-08-19 16:26:05 +02:00
|
|
|
}
|
2005-08-20 22:56:54 +02:00
|
|
|
#endif
|
2005-08-19 16:26:05 +02:00
|
|
|
|
2001-06-01 03:27:59 +02:00
|
|
|
/*
|
|
|
|
Handling of symlinked tables:
|
|
|
|
If no rename:
|
|
|
|
Create new data file and index file on the same disk as the
|
|
|
|
old data and index files.
|
|
|
|
Copy data.
|
|
|
|
Rename new data file over old data file and new index file over
|
|
|
|
old index file.
|
|
|
|
Symlinks are not changed.
|
|
|
|
|
|
|
|
If rename:
|
|
|
|
Create new data file and index file on the same disk as the
|
|
|
|
old data and index files. Create also symlinks to point at
|
|
|
|
the new tables.
|
|
|
|
Copy data.
|
|
|
|
At end, rename temporary tables and symlinks to temporary table
|
|
|
|
to final table name.
|
|
|
|
Remove old table and old symlinks
|
|
|
|
|
|
|
|
If rename is made to another database:
|
|
|
|
Create new tables in new database.
|
|
|
|
Copy data.
|
|
|
|
Remove old table and symlinks.
|
|
|
|
*/
|
|
|
|
if (!strcmp(db, new_db)) // Ignore symlink if db changed
|
|
|
|
{
|
|
|
|
if (create_info->index_file_name)
|
|
|
|
{
|
|
|
|
/* Fix index_file_name to have 'tmp_name' as basename */
|
|
|
|
strmov(index_file, tmp_name);
|
|
|
|
create_info->index_file_name=fn_same(index_file,
|
|
|
|
create_info->index_file_name,
|
|
|
|
1);
|
|
|
|
}
|
|
|
|
if (create_info->data_file_name)
|
|
|
|
{
|
|
|
|
/* Fix data_file_name to have 'tmp_name' as basename */
|
|
|
|
strmov(data_file, tmp_name);
|
|
|
|
create_info->data_file_name=fn_same(data_file,
|
|
|
|
create_info->data_file_name,
|
|
|
|
1);
|
|
|
|
}
|
|
|
|
}
|
2001-06-05 02:38:10 +02:00
|
|
|
else
|
|
|
|
create_info->data_file_name=create_info->index_file_name=0;
|
2004-10-29 18:26:52 +02:00
|
|
|
|
2006-01-12 10:05:07 +01:00
|
|
|
/*
|
|
|
|
Create a table with a temporary name.
|
|
|
|
With create_info->frm_only == 1 this creates a .frm file only.
|
|
|
|
We don't log the statement, it will be logged later.
|
|
|
|
*/
|
|
|
|
tmp_disable_binlog(thd);
|
|
|
|
error= mysql_create_table(thd, new_db, tmp_name,
|
2006-06-21 16:57:30 +02:00
|
|
|
create_info,create_list,key_list,1,0,0);
|
2006-01-12 10:05:07 +01:00
|
|
|
reenable_binlog(thd);
|
|
|
|
if (error)
|
|
|
|
DBUG_RETURN(error);
|
|
|
|
|
|
|
|
/* Open the table if we need to copy the data. */
|
2004-10-21 18:10:58 +02:00
|
|
|
if (need_copy_table)
|
2001-05-09 22:02:36 +02:00
|
|
|
{
|
2005-01-06 12:00:13 +01:00
|
|
|
if (table->s->tmp_table)
|
2004-10-21 18:10:58 +02:00
|
|
|
{
|
|
|
|
TABLE_LIST tbl;
|
|
|
|
bzero((void*) &tbl, sizeof(tbl));
|
|
|
|
tbl.db= new_db;
|
2005-01-06 12:00:13 +01:00
|
|
|
tbl.table_name= tbl.alias= tmp_name;
|
2005-11-23 21:45:02 +01:00
|
|
|
/* Table is in thd->temporary_tables */
|
2005-09-13 14:36:43 +02:00
|
|
|
new_table= open_table(thd, &tbl, thd->mem_root, (bool*) 0,
|
|
|
|
MYSQL_LOCK_IGNORE_FLUSH);
|
2004-10-21 18:10:58 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char path[FN_REFLEN];
|
2005-11-23 21:45:02 +01:00
|
|
|
/* table is a normal table: Create temporary table in same directory */
|
2005-12-31 06:01:26 +01:00
|
|
|
build_table_filename(path, sizeof(path), new_db, tmp_name, "");
|
2004-10-21 18:10:58 +02:00
|
|
|
new_table=open_temporary_table(thd, path, new_db, tmp_name,0);
|
|
|
|
}
|
|
|
|
if (!new_table)
|
2006-04-13 16:19:19 +02:00
|
|
|
goto err1;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
2006-01-12 10:05:07 +01:00
|
|
|
/* Copy the data if necessary. */
|
2003-10-11 22:26:39 +02:00
|
|
|
thd->count_cuted_fields= CHECK_FIELD_WARN; // calc cuted fields
|
2000-07-31 21:29:14 +02:00
|
|
|
thd->cuted_fields=0L;
|
|
|
|
thd->proc_info="copy to tmp table";
|
2001-01-28 20:35:50 +01:00
|
|
|
copied=deleted=0;
|
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes
Changes that requires code changes in other code of other storage engines.
(Note that all changes are very straightforward and one should find all issues
by compiling a --debug build and fixing all compiler errors and all
asserts in field.cc while running the test suite),
- New optional handler function introduced: reset()
This is called after every DML statement to make it easy for a handler to
statement specific cleanups.
(The only case it's not called is if force the file to be closed)
- handler::extra(HA_EXTRA_RESET) is removed. Code that was there before
should be moved to handler::reset()
- table->read_set contains a bitmap over all columns that are needed
in the query. read_row() and similar functions only needs to read these
columns
- table->write_set contains a bitmap over all columns that will be updated
in the query. write_row() and update_row() only needs to update these
columns.
The above bitmaps should now be up to date in all context
(including ALTER TABLE, filesort()).
The handler is informed of any changes to the bitmap after
fix_fields() by calling the virtual function
handler::column_bitmaps_signal(). If the handler does caching of
these bitmaps (instead of using table->read_set, table->write_set),
it should redo the caching in this code. as the signal() may be sent
several times, it's probably best to set a variable in the signal
and redo the caching on read_row() / write_row() if the variable was
set.
- Removed the read_set and write_set bitmap objects from the handler class
- Removed all column bit handling functions from the handler class.
(Now one instead uses the normal bitmap functions in my_bitmap.c instead
of handler dedicated bitmap functions)
- field->query_id is removed. One should instead instead check
table->read_set and table->write_set if a field is used in the query.
- handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and
handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now
instead use table->read_set to check for which columns to retrieve.
- If a handler needs to call Field->val() or Field->store() on columns
that are not used in the query, one should install a temporary
all-columns-used map while doing so. For this, we provide the following
functions:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
field->val();
dbug_tmp_restore_column_map(table->read_set, old_map);
and similar for the write map:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
field->val();
dbug_tmp_restore_column_map(table->write_set, old_map);
If this is not done, you will sooner or later hit a DBUG_ASSERT
in the field store() / val() functions.
(For not DBUG binaries, the dbug_tmp_restore_column_map() and
dbug_tmp_restore_column_map() are inline dummy functions and should
be optimized away be the compiler).
- If one needs to temporary set the column map for all binaries (and not
just to avoid the DBUG_ASSERT() in the Field::store() / Field::val()
methods) one should use the functions tmp_use_all_columns() and
tmp_restore_column_map() instead of the above dbug_ variants.
- All 'status' fields in the handler base class (like records,
data_file_length etc) are now stored in a 'stats' struct. This makes
it easier to know what status variables are provided by the base
handler. This requires some trivial variable names in the extra()
function.
- New virtual function handler::records(). This is called to optimize
COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true.
(stats.records is not supposed to be an exact value. It's only has to
be 'reasonable enough' for the optimizer to be able to choose a good
optimization path).
- Non virtual handler::init() function added for caching of virtual
constants from engine.
- Removed has_transactions() virtual method. Now one should instead return
HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support
transactions.
- The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument
that is to be used with 'new handler_name()' to allocate the handler
in the right area. The xxxx_create_handler() function is also
responsible for any initialization of the object before returning.
For example, one should change:
static handler *myisam_create_handler(TABLE_SHARE *table)
{
return new ha_myisam(table);
}
->
static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root)
{
return new (mem_root) ha_myisam(table);
}
- New optional virtual function: use_hidden_primary_key().
This is called in case of an update/delete when
(table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
but we don't have a primary key. This allows the handler to take precisions
in remembering any hidden primary key to able to update/delete any
found row. The default handler marks all columns to be read.
- handler::table_flags() now returns a ulonglong (to allow for more flags).
- New/changed table_flags()
- HA_HAS_RECORDS Set if ::records() is supported
- HA_NO_TRANSACTIONS Set if engine doesn't support transactions
- HA_PRIMARY_KEY_REQUIRED_FOR_DELETE
Set if we should mark all primary key columns for
read when reading rows as part of a DELETE
statement. If there is no primary key,
all columns are marked for read.
- HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some
cases (based on table->read_set)
- HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS
Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION.
- HA_DUPP_POS Renamed to HA_DUPLICATE_POS
- HA_REQUIRES_KEY_COLUMNS_FOR_DELETE
Set this if we should mark ALL key columns for
read when when reading rows as part of a DELETE
statement. In case of an update we will mark
all keys for read for which key part changed
value.
- HA_STATS_RECORDS_IS_EXACT
Set this if stats.records is exact.
(This saves us some extra records() calls
when optimizing COUNT(*))
- Removed table_flags()
- HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if
handler::records() gives an exact count() and
HA_STATS_RECORDS_IS_EXACT if stats.records is exact.
- HA_READ_RND_SAME Removed (no one supported this one)
- Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk()
- Renamed handler::dupp_pos to handler::dup_pos
- Removed not used variable handler::sortkey
Upper level handler changes:
- ha_reset() now does some overall checks and calls ::reset()
- ha_table_flags() added. This is a cached version of table_flags(). The
cache is updated on engine creation time and updated on open.
MySQL level changes (not obvious from the above):
- DBUG_ASSERT() added to check that column usage matches what is set
in the column usage bit maps. (This found a LOT of bugs in current
column marking code).
- In 5.1 before, all used columns was marked in read_set and only updated
columns was marked in write_set. Now we only mark columns for which we
need a value in read_set.
- Column bitmaps are created in open_binary_frm() and open_table_from_share().
(Before this was in table.cc)
- handler::table_flags() calls are replaced with handler::ha_table_flags()
- For calling field->val() you must have the corresponding bit set in
table->read_set. For calling field->store() you must have the
corresponding bit set in table->write_set. (There are asserts in
all store()/val() functions to catch wrong usage)
- thd->set_query_id is renamed to thd->mark_used_columns and instead
of setting this to an integer value, this has now the values:
MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE
Changed also all variables named 'set_query_id' to mark_used_columns.
- In filesort() we now inform the handler of exactly which columns are needed
doing the sort and choosing the rows.
- The TABLE_SHARE object has a 'all_set' column bitmap one can use
when one needs a column bitmap with all columns set.
(This is used for table->use_all_columns() and other places)
- The TABLE object has 3 column bitmaps:
- def_read_set Default bitmap for columns to be read
- def_write_set Default bitmap for columns to be written
- tmp_set Can be used as a temporary bitmap when needed.
The table object has also two pointer to bitmaps read_set and write_set
that the handler should use to find out which columns are used in which way.
- count() optimization now calls handler::records() instead of using
handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true).
- Added extra argument to Item::walk() to indicate if we should also
traverse sub queries.
- Added TABLE parameter to cp_buffer_from_ref()
- Don't close tables created with CREATE ... SELECT but keep them in
the table cache. (Faster usage of newly created tables).
New interfaces:
- table->clear_column_bitmaps() to initialize the bitmaps for tables
at start of new statements.
- table->column_bitmaps_set() to set up new column bitmaps and signal
the handler about this.
- table->column_bitmaps_set_no_signal() for some few cases where we need
to setup new column bitmaps but don't signal the handler (as the handler
has already been signaled about these before). Used for the momement
only in opt_range.cc when doing ROR scans.
- table->use_all_columns() to install a bitmap where all columns are marked
as use in the read and the write set.
- table->default_column_bitmaps() to install the normal read and write
column bitmaps, but not signaling the handler about this.
This is mainly used when creating TABLE instances.
- table->mark_columns_needed_for_delete(),
table->mark_columns_needed_for_delete() and
table->mark_columns_needed_for_insert() to allow us to put additional
columns in column usage maps if handler so requires.
(The handler indicates what it neads in handler->table_flags())
- table->prepare_for_position() to allow us to tell handler that it
needs to read primary key parts to be able to store them in
future table->position() calls.
(This replaces the table->file->ha_retrieve_all_pk function)
- table->mark_auto_increment_column() to tell handler are going to update
columns part of any auto_increment key.
- table->mark_columns_used_by_index() to mark all columns that is part of
an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow
it to quickly know that it only needs to read colums that are part
of the key. (The handler can also use the column map for detecting this,
but simpler/faster handler can just monitor the extra() call).
- table->mark_columns_used_by_index_no_reset() to in addition to other columns,
also mark all columns that is used by the given key.
- table->restore_column_maps_after_mark_index() to restore to default
column maps after a call to table->mark_columns_used_by_index().
- New item function register_field_in_read_map(), for marking used columns
in table->read_map. Used by filesort() to mark all used columns
- Maintain in TABLE->merge_keys set of all keys that are used in query.
(Simplices some optimization loops)
- Maintain Field->part_of_key_not_clustered which is like Field->part_of_key
but the field in the clustered key is not assumed to be part of all index.
(used in opt_range.cc for faster loops)
- dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map()
tmp_use_all_columns() and tmp_restore_column_map() functions to temporally
mark all columns as usable. The 'dbug_' version is primarily intended
inside a handler when it wants to just call Field:store() & Field::val()
functions, but don't need the column maps set for any other usage.
(ie:: bitmap_is_set() is never called)
- We can't use compare_records() to skip updates for handlers that returns
a partial column set and the read_set doesn't cover all columns in the
write set. The reason for this is that if we have a column marked only for
write we can't in the MySQL level know if the value changed or not.
The reason this worked before was that MySQL marked all to be written
columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden
bug'.
- open_table_from_share() does not anymore setup temporary MEM_ROOT
object as a thread specific variable for the handler. Instead we
send the to-be-used MEMROOT to get_new_handler().
(Simpler, faster code)
Bugs fixed:
- Column marking was not done correctly in a lot of cases.
(ALTER TABLE, when using triggers, auto_increment fields etc)
(Could potentially result in wrong values inserted in table handlers
relying on that the old column maps or field->set_query_id was correct)
Especially when it comes to triggers, there may be cases where the
old code would cause lost/wrong values for NDB and/or InnoDB tables.
- Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags:
OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG.
This allowed me to remove some wrong warnings about:
"Some non-transactional changed tables couldn't be rolled back"
- Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset
(thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose
some warnings about
"Some non-transactional changed tables couldn't be rolled back")
- Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table()
which could cause delete_table to report random failures.
- Fixed core dumps for some tests when running with --debug
- Added missing FN_LIBCHAR in mysql_rm_tmp_tables()
(This has probably caused us to not properly remove temporary files after
crash)
- slow_logs was not properly initialized, which could maybe cause
extra/lost entries in slow log.
- If we get an duplicate row on insert, change column map to read and
write all columns while retrying the operation. This is required by
the definition of REPLACE and also ensures that fields that are only
part of UPDATE are properly handled. This fixed a bug in NDB and
REPLACE where REPLACE wrongly copied some column values from the replaced
row.
- For table handler that doesn't support NULL in keys, we would give an error
when creating a primary key with NULL fields, even after the fields has been
automaticly converted to NOT NULL.
- Creating a primary key on a SPATIAL key, would fail if field was not
declared as NOT NULL.
Cleanups:
- Removed not used condition argument to setup_tables
- Removed not needed item function reset_query_id_processor().
- Field->add_index is removed. Now this is instead maintained in
(field->flags & FIELD_IN_ADD_INDEX)
- Field->fieldnr is removed (use field->field_index instead)
- New argument to filesort() to indicate that it should return a set of
row pointers (not used columns). This allowed me to remove some references
to sql_command in filesort and should also enable us to return column
results in some cases where we couldn't before.
- Changed column bitmap handling in opt_range.cc to be aligned with TABLE
bitmap, which allowed me to use bitmap functions instead of looping over
all fields to create some needed bitmaps. (Faster and smaller code)
- Broke up found too long lines
- Moved some variable declaration at start of function for better code
readability.
- Removed some not used arguments from functions.
(setup_fields(), mysql_prepare_insert_check_table())
- setup_fields() now takes an enum instead of an int for marking columns
usage.
- For internal temporary tables, use handler::write_row(),
handler::delete_row() and handler::update_row() instead of
handler::ha_xxxx() for faster execution.
- Changed some constants to enum's and define's.
- Using separate column read and write sets allows for easier checking
of timestamp field was set by statement.
- Remove calls to free_io_cache() as this is now done automaticly in ha_reset()
- Don't build table->normalized_path as this is now identical to table->path
(after bar's fixes to convert filenames)
- Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to
do comparision with the 'convert-dbug-for-diff' tool.
Things left to do in 5.1:
- We wrongly log failed CREATE TABLE ... SELECT in some cases when using
row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result)
Mats has promised to look into this.
- Test that my fix for CREATE TABLE ... SELECT is indeed correct.
(I added several test cases for this, but in this case it's better that
someone else also tests this throughly).
Lars has promosed to do this.
2006-06-04 17:52:22 +02:00
|
|
|
if (new_table && !(new_table->file->ha_table_flags() & HA_NO_COPY_ON_ALTER))
|
2004-10-21 18:10:58 +02:00
|
|
|
{
|
2006-01-12 10:05:07 +01:00
|
|
|
/* We don't want update TIMESTAMP fields during ALTER TABLE. */
|
2004-11-03 19:07:17 +01:00
|
|
|
new_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
|
2004-10-21 18:10:58 +02:00
|
|
|
new_table->next_number_field=new_table->found_next_number_field;
|
2006-07-01 23:51:10 +02:00
|
|
|
error=copy_data_between_tables(table, new_table, create_list, ignore,
|
2003-01-25 01:25:52 +01:00
|
|
|
order_num, order, &copied, &deleted);
|
2004-10-21 18:10:58 +02:00
|
|
|
}
|
2003-10-11 22:26:39 +02:00
|
|
|
thd->count_cuted_fields= CHECK_FIELD_IGNORE;
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2006-01-12 10:05:07 +01:00
|
|
|
/* If we did not need to copy, we might still need to add/drop indexes. */
|
|
|
|
if (! new_table)
|
|
|
|
{
|
|
|
|
uint *key_numbers;
|
|
|
|
uint *keyno_p;
|
|
|
|
KEY *key_info;
|
|
|
|
KEY *key;
|
|
|
|
uint *idx_p;
|
|
|
|
uint *idx_end_p;
|
|
|
|
KEY_PART_INFO *key_part;
|
|
|
|
KEY_PART_INFO *part_end;
|
|
|
|
DBUG_PRINT("info", ("No new_table, checking add/drop index"));
|
|
|
|
|
2006-04-11 14:06:32 +02:00
|
|
|
table->file->prepare_for_alter();
|
2006-01-12 10:05:07 +01:00
|
|
|
if (index_add_count)
|
|
|
|
{
|
|
|
|
#ifdef XXX_TO_BE_DONE_LATER_BY_WL3020_AND_WL1892
|
|
|
|
if (! need_lock_for_indexes)
|
|
|
|
{
|
|
|
|
/* Downgrade the write lock. */
|
|
|
|
mysql_lock_downgrade_write(thd, table, TL_WRITE_ALLOW_WRITE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create a new .frm file for crash recovery. */
|
|
|
|
/* TODO: Must set INDEX_TO_BE_ADDED flags in the frm file. */
|
|
|
|
VOID(pthread_mutex_lock(&LOCK_open));
|
|
|
|
error= (mysql_create_frm(thd, reg_path, db, table_name,
|
|
|
|
create_info, prepared_create_list, key_count,
|
|
|
|
key_info_buffer, table->file) ||
|
2006-04-20 03:43:30 +02:00
|
|
|
table->file->create_handler_files(reg_path, NULL, CHF_INDEX_FLAG,
|
2006-04-20 03:22:35 +02:00
|
|
|
create_info));
|
2006-01-12 10:05:07 +01:00
|
|
|
VOID(pthread_mutex_unlock(&LOCK_open));
|
|
|
|
if (error)
|
2006-04-13 16:19:19 +02:00
|
|
|
goto err1;
|
2006-01-12 10:05:07 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
/* The add_index() method takes an array of KEY structs. */
|
|
|
|
key_info= (KEY*) thd->alloc(sizeof(KEY) * index_add_count);
|
|
|
|
key= key_info;
|
|
|
|
for (idx_p= index_add_buffer, idx_end_p= idx_p + index_add_count;
|
|
|
|
idx_p < idx_end_p;
|
|
|
|
idx_p++, key++)
|
|
|
|
{
|
|
|
|
/* Copy the KEY struct. */
|
|
|
|
*key= key_info_buffer[*idx_p];
|
|
|
|
/* Fix the key parts. */
|
|
|
|
part_end= key->key_part + key->key_parts;
|
|
|
|
for (key_part= key->key_part; key_part < part_end; key_part++)
|
|
|
|
key_part->field= table->field[key_part->fieldnr];
|
|
|
|
}
|
|
|
|
/* Add the indexes. */
|
|
|
|
if ((error= table->file->add_index(table, key_info, index_add_count)))
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
Exchange the key_info for the error message. If we exchange
|
|
|
|
key number by key name in the message later, we need correct info.
|
|
|
|
*/
|
|
|
|
KEY *save_key_info= table->key_info;
|
|
|
|
table->key_info= key_info;
|
|
|
|
table->file->print_error(error, MYF(0));
|
|
|
|
table->key_info= save_key_info;
|
2006-04-13 16:19:19 +02:00
|
|
|
goto err1;
|
2006-01-12 10:05:07 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
/*end of if (index_add_count)*/
|
|
|
|
|
|
|
|
if (index_drop_count)
|
|
|
|
{
|
|
|
|
#ifdef XXX_TO_BE_DONE_LATER_BY_WL3020_AND_WL1892
|
|
|
|
/* Create a new .frm file for crash recovery. */
|
|
|
|
/* TODO: Must set INDEX_IS_ADDED in the frm file. */
|
|
|
|
/* TODO: Must set INDEX_TO_BE_DROPPED in the frm file. */
|
|
|
|
VOID(pthread_mutex_lock(&LOCK_open));
|
|
|
|
error= (mysql_create_frm(thd, reg_path, db, table_name,
|
|
|
|
create_info, prepared_create_list, key_count,
|
|
|
|
key_info_buffer, table->file) ||
|
2006-04-20 03:43:30 +02:00
|
|
|
table->file->create_handler_files(reg_path, NULL, CHF_INDEX_FLAG,
|
2006-04-20 03:22:35 +02:00
|
|
|
create_info));
|
2006-01-12 10:05:07 +01:00
|
|
|
VOID(pthread_mutex_unlock(&LOCK_open));
|
|
|
|
if (error)
|
2006-04-13 16:19:19 +02:00
|
|
|
goto err1;
|
2006-01-12 10:05:07 +01:00
|
|
|
|
|
|
|
if (! need_lock_for_indexes)
|
|
|
|
{
|
|
|
|
LOCK_PARAM_TYPE lpt;
|
|
|
|
|
|
|
|
lpt.thd= thd;
|
|
|
|
lpt.table= table;
|
|
|
|
lpt.db= db;
|
|
|
|
lpt.table_name= table_name;
|
|
|
|
lpt.create_info= create_info;
|
|
|
|
lpt.create_list= &create_list;
|
|
|
|
lpt.key_count= key_count;
|
|
|
|
lpt.key_info_buffer= key_info_buffer;
|
|
|
|
abort_and_upgrade_lock(lpt);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* The prepare_drop_index() method takes an array of key numbers. */
|
|
|
|
key_numbers= (uint*) thd->alloc(sizeof(uint) * index_drop_count);
|
|
|
|
keyno_p= key_numbers;
|
|
|
|
/* Get the number of each key. */
|
|
|
|
for (idx_p= index_drop_buffer, idx_end_p= idx_p + index_drop_count;
|
|
|
|
idx_p < idx_end_p;
|
|
|
|
idx_p++, keyno_p++)
|
|
|
|
*keyno_p= *idx_p;
|
|
|
|
/*
|
|
|
|
Tell the handler to prepare for drop indexes.
|
|
|
|
This re-numbers the indexes to get rid of gaps.
|
|
|
|
*/
|
|
|
|
if ((error= table->file->prepare_drop_index(table, key_numbers,
|
|
|
|
index_drop_count)))
|
|
|
|
{
|
|
|
|
table->file->print_error(error, MYF(0));
|
2006-04-13 16:19:19 +02:00
|
|
|
goto err1;
|
2006-01-12 10:05:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef XXX_TO_BE_DONE_LATER_BY_WL3020
|
|
|
|
if (! need_lock_for_indexes)
|
|
|
|
{
|
|
|
|
/* Downgrade the lock again. */
|
|
|
|
if (table->reginfo.lock_type == TL_WRITE_ALLOW_READ)
|
|
|
|
{
|
|
|
|
LOCK_PARAM_TYPE lpt;
|
|
|
|
|
|
|
|
lpt.thd= thd;
|
|
|
|
lpt.table= table;
|
|
|
|
lpt.db= db;
|
|
|
|
lpt.table_name= table_name;
|
|
|
|
lpt.create_info= create_info;
|
|
|
|
lpt.create_list= &create_list;
|
|
|
|
lpt.key_count= key_count;
|
|
|
|
lpt.key_info_buffer= key_info_buffer;
|
|
|
|
close_open_tables_and_downgrade(lpt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Tell the handler to finally drop the indexes. */
|
|
|
|
if ((error= table->file->final_drop_index(table)))
|
|
|
|
{
|
|
|
|
table->file->print_error(error, MYF(0));
|
2006-04-13 16:19:19 +02:00
|
|
|
goto err1;
|
2006-01-12 10:05:07 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
/*end of if (index_drop_count)*/
|
|
|
|
|
2006-04-11 14:06:32 +02:00
|
|
|
/*
|
|
|
|
The final .frm file is already created as a temporary file
|
|
|
|
and will be renamed to the original table name later.
|
|
|
|
*/
|
2006-01-12 10:05:07 +01:00
|
|
|
|
2006-04-11 14:06:32 +02:00
|
|
|
/* Need to commit before a table is unlocked (NDB requirement). */
|
|
|
|
DBUG_PRINT("info", ("Committing before unlocking table"));
|
|
|
|
if (ha_commit_stmt(thd) || ha_commit(thd))
|
2006-04-13 16:19:19 +02:00
|
|
|
goto err1;
|
2006-04-11 14:06:32 +02:00
|
|
|
committed= 1;
|
2006-01-12 10:05:07 +01:00
|
|
|
}
|
|
|
|
/*end of if (! new_table) for add/drop index*/
|
|
|
|
|
2005-11-23 21:45:02 +01:00
|
|
|
if (table->s->tmp_table != NO_TMP_TABLE)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
|
|
|
/* We changed a temporary table */
|
|
|
|
if (error)
|
2006-04-13 16:19:19 +02:00
|
|
|
goto err1;
|
2001-11-07 22:18:12 +01:00
|
|
|
/* Close lock if this is a transactional table */
|
|
|
|
if (thd->lock)
|
|
|
|
{
|
|
|
|
mysql_unlock_tables(thd, thd->lock);
|
|
|
|
thd->lock=0;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
/* Remove link to old table and rename the new one */
|
2005-11-23 21:45:02 +01:00
|
|
|
close_temporary_table(thd, table, 1, 1);
|
2005-01-21 12:14:31 +01:00
|
|
|
/* Should pass the 'new_name' as we store table name in the cache */
|
|
|
|
if (rename_temporary_table(thd, new_table, new_db, new_name))
|
2006-04-13 16:19:19 +02:00
|
|
|
goto err1;
|
2005-12-22 06:39:02 +01:00
|
|
|
/* We don't replicate alter table statement on temporary tables */
|
WL#2977 and WL#2712 global and session-level variable to set the binlog format (row/statement),
and new binlog format called "mixed" (which is statement-based except if only row-based is correct,
in this cset it means if UDF or UUID is used; more cases could be added in later 5.1 release):
SET GLOBAL|SESSION BINLOG_FORMAT=row|statement|mixed|default;
the global default is statement unless cluster is enabled (then it's row) as in 5.1-alpha.
It's not possible to use SET on this variable if a session is currently in row-based mode and has open temporary tables (because CREATE
TEMPORARY TABLE was not binlogged so temp table is not known on slave), or if NDB is enabled (because
NDB does not support such change on-the-fly, though it will later), of if in a stored function (see below).
The added tests test the possibility or impossibility to SET, their effects, and the mixed mode,
including in prepared statements and in stored procedures and functions.
Caveats:
a) The mixed mode will not work for stored functions: in mixed mode, a stored function will
always be binlogged as one call and in a statement-based way (e.g. INSERT VALUES(myfunc()) or SELECT myfunc()).
b) for the same reason, changing the thread's binlog format inside a stored function is
refused with an error message.
c) the same problems apply to triggers; implementing b) for triggers will be done later (will ask
Dmitri).
Additionally, as the binlog format is now changeable by each user for his session, I remove the implication
which was done at startup, where row-based automatically set log-bin-trust-routine-creators to 1
(not possible anymore as a user can now switch to stmt-based and do nasty things again), and automatically
set --innodb-locks-unsafe-for-binlog to 1 (was anyway theoretically incorrect as it disabled
phantom protection).
Plus fixes for compiler warnings.
2006-02-25 22:21:03 +01:00
|
|
|
if (!thd->current_stmt_binlog_row_based)
|
2005-12-22 06:39:02 +01:00
|
|
|
write_bin_log(thd, TRUE, thd->query, thd->query_length);
|
2000-07-31 21:29:14 +02:00
|
|
|
goto end_temporary;
|
|
|
|
}
|
|
|
|
|
2004-10-21 18:10:58 +02:00
|
|
|
if (new_table)
|
|
|
|
{
|
2005-11-23 21:45:02 +01:00
|
|
|
/* close temporary table that will be the new table */
|
|
|
|
intern_close_table(new_table);
|
2004-10-21 18:10:58 +02:00
|
|
|
my_free((gptr) new_table,MYF(0));
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
VOID(pthread_mutex_lock(&LOCK_open));
|
|
|
|
if (error)
|
|
|
|
{
|
|
|
|
VOID(quick_rm_table(new_db_type,new_db,tmp_name));
|
|
|
|
VOID(pthread_mutex_unlock(&LOCK_open));
|
|
|
|
goto err;
|
|
|
|
}
|
2004-03-10 12:46:11 +01:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
/*
|
2002-06-02 20:22:20 +02:00
|
|
|
Data is copied. Now we rename the old table to a temp name,
|
|
|
|
rename the new one to the old name, remove all entries from the old table
|
2004-10-21 18:10:58 +02:00
|
|
|
from the cache, free all locks, close the old table and remove it.
|
2000-07-31 21:29:14 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
thd->proc_info="rename result table";
|
2004-04-08 16:56:45 +02:00
|
|
|
my_snprintf(old_name, sizeof(old_name), "%s2-%lx-%lx", tmp_file_prefix,
|
|
|
|
current_pid, thd->thread_id);
|
2004-05-25 00:30:09 +02:00
|
|
|
if (lower_case_table_names)
|
|
|
|
my_casedn_str(files_charset_info, old_name);
|
2002-06-03 23:40:27 +02:00
|
|
|
if (new_name != table_name || new_db != db)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
|
|
|
if (!access(new_name_buff,F_OK))
|
|
|
|
{
|
|
|
|
error=1;
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name_buff);
|
2000-07-31 21:29:14 +02:00
|
|
|
VOID(quick_rm_table(new_db_type,new_db,tmp_name));
|
|
|
|
VOID(pthread_mutex_unlock(&LOCK_open));
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-04-16 03:17:32 +02:00
|
|
|
#if !defined( __WIN__)
|
2002-02-07 20:34:35 +01:00
|
|
|
if (table->file->has_transactions())
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
/*
|
2002-02-11 11:56:48 +01:00
|
|
|
Win32 and InnoDB can't drop a table that is in use, so we must
|
2002-06-03 23:40:27 +02:00
|
|
|
close the original table at before doing the rename
|
2002-02-07 20:34:35 +01:00
|
|
|
*/
|
2005-11-23 21:45:02 +01:00
|
|
|
table->s->version= 0; // Force removal of table def
|
2004-09-07 14:29:46 +02:00
|
|
|
close_cached_table(thd, table);
|
2002-02-07 20:34:35 +01:00
|
|
|
table=0; // Marker that table is closed
|
2006-03-24 20:46:33 +01:00
|
|
|
no_table_reopen= TRUE;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2006-04-16 03:17:32 +02:00
|
|
|
#if !defined( __WIN__)
|
2002-02-07 20:34:35 +01:00
|
|
|
else
|
|
|
|
table->file->extra(HA_EXTRA_FORCE_REOPEN); // Don't use this file anymore
|
2000-07-31 21:29:14 +02:00
|
|
|
#endif
|
|
|
|
|
2002-02-07 20:34:35 +01:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
error=0;
|
2004-10-21 18:10:58 +02:00
|
|
|
if (!need_copy_table)
|
2005-12-21 19:18:40 +01:00
|
|
|
new_db_type=old_db_type= NULL; // this type cannot happen in regular ALTER
|
2000-07-31 21:29:14 +02:00
|
|
|
if (mysql_rename_table(old_db_type,db,table_name,db,old_name))
|
|
|
|
{
|
|
|
|
error=1;
|
|
|
|
VOID(quick_rm_table(new_db_type,new_db,tmp_name));
|
|
|
|
}
|
|
|
|
else if (mysql_rename_table(new_db_type,new_db,tmp_name,new_db,
|
2006-02-24 21:50:36 +01:00
|
|
|
new_alias) ||
|
|
|
|
(new_name != table_name || new_db != db) && // we also do rename
|
|
|
|
Table_triggers_list::change_table_name(thd, db, table_name,
|
|
|
|
new_db, new_alias))
|
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
{ // Try to get everything back
|
|
|
|
error=1;
|
2003-12-30 12:14:21 +01:00
|
|
|
VOID(quick_rm_table(new_db_type,new_db,new_alias));
|
2000-07-31 21:29:14 +02:00
|
|
|
VOID(quick_rm_table(new_db_type,new_db,tmp_name));
|
2003-12-30 12:14:21 +01:00
|
|
|
VOID(mysql_rename_table(old_db_type,db,old_name,db,alias));
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
if (error)
|
|
|
|
{
|
2001-12-06 00:05:30 +01:00
|
|
|
/*
|
|
|
|
This shouldn't happen. We solve this the safe way by
|
|
|
|
closing the locked table.
|
|
|
|
*/
|
2003-06-01 11:32:53 +02:00
|
|
|
if (table)
|
2005-11-23 21:45:02 +01:00
|
|
|
{
|
|
|
|
table->s->version= 0; // Force removal of table def
|
2003-06-01 11:32:53 +02:00
|
|
|
close_cached_table(thd,table);
|
2005-11-23 21:45:02 +01:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
VOID(pthread_mutex_unlock(&LOCK_open));
|
|
|
|
goto err;
|
|
|
|
}
|
2006-01-12 10:05:07 +01:00
|
|
|
if (! need_copy_table)
|
|
|
|
{
|
|
|
|
if (! table)
|
|
|
|
{
|
|
|
|
VOID(pthread_mutex_unlock(&LOCK_open));
|
|
|
|
if (! (table= open_ltable(thd, table_list, TL_WRITE_ALLOW_READ)))
|
|
|
|
goto err;
|
|
|
|
VOID(pthread_mutex_lock(&LOCK_open));
|
|
|
|
}
|
|
|
|
/* Tell the handler that a new frm file is in place. */
|
2006-04-22 10:38:19 +02:00
|
|
|
if (table->file->create_handler_files(path, NULL, CHF_INDEX_FLAG,
|
2006-04-20 03:22:35 +02:00
|
|
|
create_info))
|
2006-01-12 10:05:07 +01:00
|
|
|
{
|
|
|
|
VOID(pthread_mutex_unlock(&LOCK_open));
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
2006-03-24 20:46:33 +01:00
|
|
|
if (thd->lock || new_name != table_name || no_table_reopen) // True if WIN32
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2001-12-06 00:05:30 +01:00
|
|
|
/*
|
2005-11-23 21:45:02 +01:00
|
|
|
Not table locking or alter table with rename.
|
|
|
|
Free locks and remove old table
|
2001-12-06 00:05:30 +01:00
|
|
|
*/
|
2003-06-01 11:32:53 +02:00
|
|
|
if (table)
|
2005-11-23 21:45:02 +01:00
|
|
|
{
|
|
|
|
table->s->version= 0; // Force removal of table def
|
2003-06-01 11:32:53 +02:00
|
|
|
close_cached_table(thd,table);
|
2005-11-23 21:45:02 +01:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
VOID(quick_rm_table(old_db_type,db,old_name));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2001-12-06 00:05:30 +01:00
|
|
|
/*
|
|
|
|
Using LOCK TABLES without rename.
|
|
|
|
This code is never executed on WIN32!
|
|
|
|
Remove old renamed table, reopen table and get new locks
|
|
|
|
*/
|
2000-07-31 21:29:14 +02:00
|
|
|
if (table)
|
|
|
|
{
|
|
|
|
VOID(table->file->extra(HA_EXTRA_FORCE_REOPEN)); // Use new file
|
2005-07-27 12:05:30 +02:00
|
|
|
/* Mark in-use copies old */
|
2005-07-20 21:19:01 +02:00
|
|
|
remove_table_from_cache(thd,db,table_name,RTFC_NO_FLAG);
|
2005-07-27 12:05:30 +02:00
|
|
|
/* end threads waiting on lock */
|
2006-01-17 08:40:00 +01:00
|
|
|
mysql_lock_abort(thd,table, TRUE);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
VOID(quick_rm_table(old_db_type,db,old_name));
|
|
|
|
if (close_data_tables(thd,db,table_name) ||
|
|
|
|
reopen_tables(thd,1,0))
|
|
|
|
{ // This shouldn't happen
|
2003-06-01 11:32:53 +02:00
|
|
|
if (table)
|
2005-11-23 21:45:02 +01:00
|
|
|
{
|
|
|
|
table->s->version= 0; // Force removal of table def
|
2003-06-01 11:32:53 +02:00
|
|
|
close_cached_table(thd,table); // Remove lock for table
|
2005-11-23 21:45:02 +01:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
VOID(pthread_mutex_unlock(&LOCK_open));
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
2006-01-12 10:05:07 +01:00
|
|
|
VOID(pthread_mutex_unlock(&LOCK_open));
|
2006-07-04 10:02:11 +02:00
|
|
|
broadcast_refresh();
|
2006-01-12 10:05:07 +01:00
|
|
|
/*
|
|
|
|
The ALTER TABLE is always in its own transaction.
|
|
|
|
Commit must not be called while LOCK_open is locked. It could call
|
|
|
|
wait_if_global_read_lock(), which could create a deadlock if called
|
|
|
|
with LOCK_open.
|
|
|
|
*/
|
2006-01-17 12:53:49 +01:00
|
|
|
if (!committed)
|
|
|
|
{
|
|
|
|
error = ha_commit_stmt(thd);
|
|
|
|
if (ha_commit(thd))
|
|
|
|
error=1;
|
|
|
|
if (error)
|
|
|
|
goto err;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
thd->proc_info="end";
|
2005-12-22 06:39:02 +01:00
|
|
|
|
2006-02-06 11:47:12 +01:00
|
|
|
ha_binlog_log_query(thd, create_info->db_type, LOGCOM_ALTER_TABLE,
|
|
|
|
thd->query, thd->query_length,
|
|
|
|
db, table_name);
|
|
|
|
|
WL#2977 and WL#2712 global and session-level variable to set the binlog format (row/statement),
and new binlog format called "mixed" (which is statement-based except if only row-based is correct,
in this cset it means if UDF or UUID is used; more cases could be added in later 5.1 release):
SET GLOBAL|SESSION BINLOG_FORMAT=row|statement|mixed|default;
the global default is statement unless cluster is enabled (then it's row) as in 5.1-alpha.
It's not possible to use SET on this variable if a session is currently in row-based mode and has open temporary tables (because CREATE
TEMPORARY TABLE was not binlogged so temp table is not known on slave), or if NDB is enabled (because
NDB does not support such change on-the-fly, though it will later), of if in a stored function (see below).
The added tests test the possibility or impossibility to SET, their effects, and the mixed mode,
including in prepared statements and in stored procedures and functions.
Caveats:
a) The mixed mode will not work for stored functions: in mixed mode, a stored function will
always be binlogged as one call and in a statement-based way (e.g. INSERT VALUES(myfunc()) or SELECT myfunc()).
b) for the same reason, changing the thread's binlog format inside a stored function is
refused with an error message.
c) the same problems apply to triggers; implementing b) for triggers will be done later (will ask
Dmitri).
Additionally, as the binlog format is now changeable by each user for his session, I remove the implication
which was done at startup, where row-based automatically set log-bin-trust-routine-creators to 1
(not possible anymore as a user can now switch to stmt-based and do nasty things again), and automatically
set --innodb-locks-unsafe-for-binlog to 1 (was anyway theoretically incorrect as it disabled
phantom protection).
Plus fixes for compiler warnings.
2006-02-25 22:21:03 +01:00
|
|
|
DBUG_ASSERT(!(mysql_bin_log.is_open() && thd->current_stmt_binlog_row_based &&
|
2005-12-22 06:39:02 +01:00
|
|
|
(create_info->options & HA_LEX_CREATE_TMP_TABLE)));
|
|
|
|
write_bin_log(thd, TRUE, thd->query, thd->query_length);
|
2005-08-19 16:26:05 +02:00
|
|
|
/*
|
|
|
|
TODO RONM: This problem needs to handled for Berkeley DB partitions
|
|
|
|
as well
|
|
|
|
*/
|
2005-11-07 16:25:06 +01:00
|
|
|
if (ha_check_storage_engine_flag(old_db_type,HTON_FLUSH_AFTER_RENAME))
|
2002-08-28 15:41:23 +02:00
|
|
|
{
|
2002-09-03 14:44:25 +02:00
|
|
|
/*
|
|
|
|
For the alter table to be properly flushed to the logs, we
|
|
|
|
have to open the new table. If not, we get a problem on server
|
|
|
|
shutdown.
|
|
|
|
*/
|
2002-10-24 00:29:29 +02:00
|
|
|
char path[FN_REFLEN];
|
2005-12-31 06:01:26 +01:00
|
|
|
build_table_filename(path, sizeof(path), new_db, table_name, "");
|
2002-10-24 00:29:29 +02:00
|
|
|
table=open_temporary_table(thd, path, new_db, tmp_name,0);
|
|
|
|
if (table)
|
2002-09-03 14:44:25 +02:00
|
|
|
{
|
2002-10-24 00:29:29 +02:00
|
|
|
intern_close_table(table);
|
|
|
|
my_free((char*) table, MYF(0));
|
2002-09-03 14:44:25 +02:00
|
|
|
}
|
2002-10-24 00:29:29 +02:00
|
|
|
else
|
2005-11-07 16:25:06 +01:00
|
|
|
sql_print_warning("Could not open table %s.%s after rename\n",
|
2004-09-04 20:17:09 +02:00
|
|
|
new_db,table_name);
|
2005-11-07 16:25:06 +01:00
|
|
|
ha_flush_logs(old_db_type);
|
2002-08-28 15:41:23 +02:00
|
|
|
}
|
2001-12-06 00:05:30 +01:00
|
|
|
table_list->table=0; // For query cache
|
2002-03-22 21:55:08 +01:00
|
|
|
query_cache_invalidate3(thd, table_list, 0);
|
2000-07-31 21:29:14 +02:00
|
|
|
|
|
|
|
end_temporary:
|
2004-04-08 16:56:45 +02:00
|
|
|
my_snprintf(tmp_name, sizeof(tmp_name), ER(ER_INSERT_INFO),
|
|
|
|
(ulong) (copied + deleted), (ulong) deleted,
|
|
|
|
(ulong) thd->cuted_fields);
|
2004-06-10 16:41:24 +02:00
|
|
|
if (do_send_ok)
|
|
|
|
send_ok(thd,copied+deleted,0L,tmp_name);
|
2000-07-31 21:29:14 +02:00
|
|
|
thd->some_tables_deleted=0;
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(FALSE);
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2006-04-13 16:19:19 +02:00
|
|
|
err1:
|
|
|
|
if (new_table)
|
|
|
|
{
|
|
|
|
/* close_temporary_table() frees the new_table pointer. */
|
|
|
|
close_temporary_table(thd, new_table, 1, 1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
VOID(quick_rm_table(new_db_type,new_db,tmp_name));
|
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
err:
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2005-07-22 22:43:59 +02:00
|
|
|
/* mysql_alter_table */
|
2000-07-31 21:29:14 +02:00
|
|
|
|
|
|
|
static int
|
2000-11-11 19:27:34 +01:00
|
|
|
copy_data_between_tables(TABLE *from,TABLE *to,
|
2004-04-08 16:56:45 +02:00
|
|
|
List<create_field> &create,
|
2004-12-31 11:04:35 +01:00
|
|
|
bool ignore,
|
2004-04-08 16:56:45 +02:00
|
|
|
uint order_num, ORDER *order,
|
2000-11-11 19:27:34 +01:00
|
|
|
ha_rows *copied,
|
2004-04-08 16:56:45 +02:00
|
|
|
ha_rows *deleted)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
|
|
|
int error;
|
|
|
|
Copy_field *copy,*copy_end;
|
|
|
|
ulong found_count,delete_count;
|
|
|
|
THD *thd= current_thd;
|
2000-11-11 19:27:34 +01:00
|
|
|
uint length;
|
|
|
|
SORT_FIELD *sortorder;
|
|
|
|
READ_RECORD info;
|
|
|
|
TABLE_LIST tables;
|
|
|
|
List<Item> fields;
|
|
|
|
List<Item> all_fields;
|
2001-04-25 21:44:27 +02:00
|
|
|
ha_rows examined_rows;
|
2004-08-24 17:00:45 +02:00
|
|
|
bool auto_increment_field_copied= 0;
|
2004-10-07 11:02:39 +02:00
|
|
|
ulong save_sql_mode;
|
WL#3146 "less locking in auto_increment":
this is a cleanup patch for our current auto_increment handling:
new names for auto_increment variables in THD, new methods to manipulate them
(see sql_class.h), some move into handler::, causing less backup/restore
work when executing substatements.
This makes the logic hopefully clearer, less work is is needed in
mysql_insert().
By cleaning up, using different variables for different purposes (instead
of one for 3 things...), we fix those bugs, which someone may want to fix
in 5.0 too:
BUG#20339 "stored procedure using LAST_INSERT_ID() does not replicate
statement-based"
BUG#20341 "stored function inserting into one auto_increment puts bad
data in slave"
BUG#19243 "wrong LAST_INSERT_ID() after ON DUPLICATE KEY UPDATE"
(now if a row is updated, LAST_INSERT_ID() will return its id)
and re-fixes:
BUG#6880 "LAST_INSERT_ID() value changes during multi-row INSERT"
(already fixed differently by Ramil in 4.1)
Test of documented behaviour of mysql_insert_id() (there was no test).
The behaviour changes introduced are:
- LAST_INSERT_ID() now returns "the first autogenerated auto_increment value
successfully inserted", instead of "the first autogenerated auto_increment
value if any row was successfully inserted", see auto_increment.test.
Same for mysql_insert_id(), see mysql_client_test.c.
- LAST_INSERT_ID() returns the id of the updated row if ON DUPLICATE KEY
UPDATE, see auto_increment.test. Same for mysql_insert_id(), see
mysql_client_test.c.
- LAST_INSERT_ID() does not change if no autogenerated value was successfully
inserted (it used to then be 0), see auto_increment.test.
- if in INSERT SELECT no autogenerated value was successfully inserted,
mysql_insert_id() now returns the id of the last inserted row (it already
did this for INSERT VALUES), see mysql_client_test.c.
- if INSERT SELECT uses LAST_INSERT_ID(X), mysql_insert_id() now returns X
(it already did this for INSERT VALUES), see mysql_client_test.c.
- NDB now behaves like other engines wrt SET INSERT_ID: with INSERT IGNORE,
the id passed in SET INSERT_ID is re-used until a row succeeds; SET INSERT_ID
influences not only the first row now.
Additionally, when unlocking a table we check that the thread is not keeping
a next_insert_id (as the table is unlocked that id is potentially out-of-date);
forgetting about this next_insert_id is done in a new
handler::ha_release_auto_increment().
Finally we prepare for engines capable of reserving finite-length intervals
of auto_increment values: we store such intervals in THD. The next step
(to be done by the replication team in 5.1) is to read those intervals from
THD and actually store them in the statement-based binary log. NDB
will be a good engine to test that.
2006-07-09 17:52:19 +02:00
|
|
|
ulonglong prev_insert_id;
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_ENTER("copy_data_between_tables");
|
|
|
|
|
2005-01-10 11:17:01 +01:00
|
|
|
/*
|
|
|
|
Turn off recovery logging since rollback of an alter table is to
|
|
|
|
delete the new table so there is no need to log the changes to it.
|
|
|
|
|
|
|
|
This needs to be done before external_lock
|
|
|
|
*/
|
2005-01-10 11:33:08 +01:00
|
|
|
error= ha_enable_transaction(thd, FALSE);
|
2005-01-10 11:17:01 +01:00
|
|
|
if (error)
|
|
|
|
DBUG_RETURN(-1);
|
2005-01-10 11:33:08 +01:00
|
|
|
|
2005-01-06 12:00:13 +01:00
|
|
|
if (!(copy= new Copy_field[to->s->fields]))
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(-1); /* purecov: inspected */
|
|
|
|
|
2006-01-26 09:25:37 +01:00
|
|
|
if (to->file->ha_external_lock(thd, F_WRLCK))
|
2004-09-18 20:33:39 +02:00
|
|
|
DBUG_RETURN(-1);
|
2005-04-01 14:04:50 +02:00
|
|
|
|
|
|
|
/* We can abort alter table for any table type */
|
|
|
|
thd->no_trans_update= 0;
|
|
|
|
thd->abort_on_warning= !ignore && test(thd->variables.sql_mode &
|
|
|
|
(MODE_STRICT_TRANS_TABLES |
|
|
|
|
MODE_STRICT_ALL_TABLES));
|
|
|
|
|
2000-08-15 19:09:37 +02:00
|
|
|
from->file->info(HA_STATUS_VARIABLE);
|
2006-06-04 20:05:22 +02:00
|
|
|
to->file->ha_start_bulk_insert(from->file->stats.records);
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2004-10-07 11:51:32 +02:00
|
|
|
save_sql_mode= thd->variables.sql_mode;
|
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
List_iterator<create_field> it(create);
|
|
|
|
create_field *def;
|
|
|
|
copy_end=copy;
|
|
|
|
for (Field **ptr=to->field ; *ptr ; ptr++)
|
|
|
|
{
|
|
|
|
def=it++;
|
|
|
|
if (def->field)
|
2004-07-30 14:17:12 +02:00
|
|
|
{
|
|
|
|
if (*ptr == to->next_number_field)
|
2004-10-07 11:02:39 +02:00
|
|
|
{
|
2004-08-24 17:00:45 +02:00
|
|
|
auto_increment_field_copied= TRUE;
|
2004-10-07 11:02:39 +02:00
|
|
|
/*
|
|
|
|
If we are going to copy contents of one auto_increment column to
|
|
|
|
another auto_increment column it is sensible to preserve zeroes.
|
|
|
|
This condition also covers case when we are don't actually alter
|
|
|
|
auto_increment column.
|
|
|
|
*/
|
|
|
|
if (def->field == from->found_next_number_field)
|
|
|
|
thd->variables.sql_mode|= MODE_NO_AUTO_VALUE_ON_ZERO;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
(copy_end++)->set(*ptr,def->field,0);
|
2004-07-30 14:17:12 +02:00
|
|
|
}
|
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
2000-11-26 07:29:01 +01:00
|
|
|
found_count=delete_count=0;
|
|
|
|
|
2001-02-21 13:16:00 +01:00
|
|
|
if (order)
|
|
|
|
{
|
2003-04-24 13:33:33 +02:00
|
|
|
from->sort.io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE),
|
2004-04-08 16:56:45 +02:00
|
|
|
MYF(MY_FAE | MY_ZEROFILL));
|
2000-11-11 19:27:34 +01:00
|
|
|
bzero((char*) &tables,sizeof(tables));
|
2005-01-06 12:00:13 +01:00
|
|
|
tables.table= from;
|
2005-11-23 21:45:02 +01:00
|
|
|
tables.alias= tables.table_name= from->s->table_name.str;
|
|
|
|
tables.db= from->s->db.str;
|
2000-11-11 19:27:34 +01:00
|
|
|
error=1;
|
|
|
|
|
2003-08-26 17:41:40 +02:00
|
|
|
if (thd->lex->select_lex.setup_ref_array(thd, order_num) ||
|
2003-05-05 20:54:37 +02:00
|
|
|
setup_order(thd, thd->lex->select_lex.ref_pointer_array,
|
2003-01-25 01:25:52 +01:00
|
|
|
&tables, fields, all_fields, order) ||
|
2004-04-08 16:56:45 +02:00
|
|
|
!(sortorder=make_unireg_sortorder(order, &length)) ||
|
|
|
|
(from->sort.found_records = filesort(thd, from, sortorder, length,
|
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes
Changes that requires code changes in other code of other storage engines.
(Note that all changes are very straightforward and one should find all issues
by compiling a --debug build and fixing all compiler errors and all
asserts in field.cc while running the test suite),
- New optional handler function introduced: reset()
This is called after every DML statement to make it easy for a handler to
statement specific cleanups.
(The only case it's not called is if force the file to be closed)
- handler::extra(HA_EXTRA_RESET) is removed. Code that was there before
should be moved to handler::reset()
- table->read_set contains a bitmap over all columns that are needed
in the query. read_row() and similar functions only needs to read these
columns
- table->write_set contains a bitmap over all columns that will be updated
in the query. write_row() and update_row() only needs to update these
columns.
The above bitmaps should now be up to date in all context
(including ALTER TABLE, filesort()).
The handler is informed of any changes to the bitmap after
fix_fields() by calling the virtual function
handler::column_bitmaps_signal(). If the handler does caching of
these bitmaps (instead of using table->read_set, table->write_set),
it should redo the caching in this code. as the signal() may be sent
several times, it's probably best to set a variable in the signal
and redo the caching on read_row() / write_row() if the variable was
set.
- Removed the read_set and write_set bitmap objects from the handler class
- Removed all column bit handling functions from the handler class.
(Now one instead uses the normal bitmap functions in my_bitmap.c instead
of handler dedicated bitmap functions)
- field->query_id is removed. One should instead instead check
table->read_set and table->write_set if a field is used in the query.
- handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and
handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now
instead use table->read_set to check for which columns to retrieve.
- If a handler needs to call Field->val() or Field->store() on columns
that are not used in the query, one should install a temporary
all-columns-used map while doing so. For this, we provide the following
functions:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
field->val();
dbug_tmp_restore_column_map(table->read_set, old_map);
and similar for the write map:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
field->val();
dbug_tmp_restore_column_map(table->write_set, old_map);
If this is not done, you will sooner or later hit a DBUG_ASSERT
in the field store() / val() functions.
(For not DBUG binaries, the dbug_tmp_restore_column_map() and
dbug_tmp_restore_column_map() are inline dummy functions and should
be optimized away be the compiler).
- If one needs to temporary set the column map for all binaries (and not
just to avoid the DBUG_ASSERT() in the Field::store() / Field::val()
methods) one should use the functions tmp_use_all_columns() and
tmp_restore_column_map() instead of the above dbug_ variants.
- All 'status' fields in the handler base class (like records,
data_file_length etc) are now stored in a 'stats' struct. This makes
it easier to know what status variables are provided by the base
handler. This requires some trivial variable names in the extra()
function.
- New virtual function handler::records(). This is called to optimize
COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true.
(stats.records is not supposed to be an exact value. It's only has to
be 'reasonable enough' for the optimizer to be able to choose a good
optimization path).
- Non virtual handler::init() function added for caching of virtual
constants from engine.
- Removed has_transactions() virtual method. Now one should instead return
HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support
transactions.
- The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument
that is to be used with 'new handler_name()' to allocate the handler
in the right area. The xxxx_create_handler() function is also
responsible for any initialization of the object before returning.
For example, one should change:
static handler *myisam_create_handler(TABLE_SHARE *table)
{
return new ha_myisam(table);
}
->
static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root)
{
return new (mem_root) ha_myisam(table);
}
- New optional virtual function: use_hidden_primary_key().
This is called in case of an update/delete when
(table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
but we don't have a primary key. This allows the handler to take precisions
in remembering any hidden primary key to able to update/delete any
found row. The default handler marks all columns to be read.
- handler::table_flags() now returns a ulonglong (to allow for more flags).
- New/changed table_flags()
- HA_HAS_RECORDS Set if ::records() is supported
- HA_NO_TRANSACTIONS Set if engine doesn't support transactions
- HA_PRIMARY_KEY_REQUIRED_FOR_DELETE
Set if we should mark all primary key columns for
read when reading rows as part of a DELETE
statement. If there is no primary key,
all columns are marked for read.
- HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some
cases (based on table->read_set)
- HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS
Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION.
- HA_DUPP_POS Renamed to HA_DUPLICATE_POS
- HA_REQUIRES_KEY_COLUMNS_FOR_DELETE
Set this if we should mark ALL key columns for
read when when reading rows as part of a DELETE
statement. In case of an update we will mark
all keys for read for which key part changed
value.
- HA_STATS_RECORDS_IS_EXACT
Set this if stats.records is exact.
(This saves us some extra records() calls
when optimizing COUNT(*))
- Removed table_flags()
- HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if
handler::records() gives an exact count() and
HA_STATS_RECORDS_IS_EXACT if stats.records is exact.
- HA_READ_RND_SAME Removed (no one supported this one)
- Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk()
- Renamed handler::dupp_pos to handler::dup_pos
- Removed not used variable handler::sortkey
Upper level handler changes:
- ha_reset() now does some overall checks and calls ::reset()
- ha_table_flags() added. This is a cached version of table_flags(). The
cache is updated on engine creation time and updated on open.
MySQL level changes (not obvious from the above):
- DBUG_ASSERT() added to check that column usage matches what is set
in the column usage bit maps. (This found a LOT of bugs in current
column marking code).
- In 5.1 before, all used columns was marked in read_set and only updated
columns was marked in write_set. Now we only mark columns for which we
need a value in read_set.
- Column bitmaps are created in open_binary_frm() and open_table_from_share().
(Before this was in table.cc)
- handler::table_flags() calls are replaced with handler::ha_table_flags()
- For calling field->val() you must have the corresponding bit set in
table->read_set. For calling field->store() you must have the
corresponding bit set in table->write_set. (There are asserts in
all store()/val() functions to catch wrong usage)
- thd->set_query_id is renamed to thd->mark_used_columns and instead
of setting this to an integer value, this has now the values:
MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE
Changed also all variables named 'set_query_id' to mark_used_columns.
- In filesort() we now inform the handler of exactly which columns are needed
doing the sort and choosing the rows.
- The TABLE_SHARE object has a 'all_set' column bitmap one can use
when one needs a column bitmap with all columns set.
(This is used for table->use_all_columns() and other places)
- The TABLE object has 3 column bitmaps:
- def_read_set Default bitmap for columns to be read
- def_write_set Default bitmap for columns to be written
- tmp_set Can be used as a temporary bitmap when needed.
The table object has also two pointer to bitmaps read_set and write_set
that the handler should use to find out which columns are used in which way.
- count() optimization now calls handler::records() instead of using
handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true).
- Added extra argument to Item::walk() to indicate if we should also
traverse sub queries.
- Added TABLE parameter to cp_buffer_from_ref()
- Don't close tables created with CREATE ... SELECT but keep them in
the table cache. (Faster usage of newly created tables).
New interfaces:
- table->clear_column_bitmaps() to initialize the bitmaps for tables
at start of new statements.
- table->column_bitmaps_set() to set up new column bitmaps and signal
the handler about this.
- table->column_bitmaps_set_no_signal() for some few cases where we need
to setup new column bitmaps but don't signal the handler (as the handler
has already been signaled about these before). Used for the momement
only in opt_range.cc when doing ROR scans.
- table->use_all_columns() to install a bitmap where all columns are marked
as use in the read and the write set.
- table->default_column_bitmaps() to install the normal read and write
column bitmaps, but not signaling the handler about this.
This is mainly used when creating TABLE instances.
- table->mark_columns_needed_for_delete(),
table->mark_columns_needed_for_delete() and
table->mark_columns_needed_for_insert() to allow us to put additional
columns in column usage maps if handler so requires.
(The handler indicates what it neads in handler->table_flags())
- table->prepare_for_position() to allow us to tell handler that it
needs to read primary key parts to be able to store them in
future table->position() calls.
(This replaces the table->file->ha_retrieve_all_pk function)
- table->mark_auto_increment_column() to tell handler are going to update
columns part of any auto_increment key.
- table->mark_columns_used_by_index() to mark all columns that is part of
an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow
it to quickly know that it only needs to read colums that are part
of the key. (The handler can also use the column map for detecting this,
but simpler/faster handler can just monitor the extra() call).
- table->mark_columns_used_by_index_no_reset() to in addition to other columns,
also mark all columns that is used by the given key.
- table->restore_column_maps_after_mark_index() to restore to default
column maps after a call to table->mark_columns_used_by_index().
- New item function register_field_in_read_map(), for marking used columns
in table->read_map. Used by filesort() to mark all used columns
- Maintain in TABLE->merge_keys set of all keys that are used in query.
(Simplices some optimization loops)
- Maintain Field->part_of_key_not_clustered which is like Field->part_of_key
but the field in the clustered key is not assumed to be part of all index.
(used in opt_range.cc for faster loops)
- dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map()
tmp_use_all_columns() and tmp_restore_column_map() functions to temporally
mark all columns as usable. The 'dbug_' version is primarily intended
inside a handler when it wants to just call Field:store() & Field::val()
functions, but don't need the column maps set for any other usage.
(ie:: bitmap_is_set() is never called)
- We can't use compare_records() to skip updates for handlers that returns
a partial column set and the read_set doesn't cover all columns in the
write set. The reason for this is that if we have a column marked only for
write we can't in the MySQL level know if the value changed or not.
The reason this worked before was that MySQL marked all to be written
columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden
bug'.
- open_table_from_share() does not anymore setup temporary MEM_ROOT
object as a thread specific variable for the handler. Instead we
send the to-be-used MEMROOT to get_new_handler().
(Simpler, faster code)
Bugs fixed:
- Column marking was not done correctly in a lot of cases.
(ALTER TABLE, when using triggers, auto_increment fields etc)
(Could potentially result in wrong values inserted in table handlers
relying on that the old column maps or field->set_query_id was correct)
Especially when it comes to triggers, there may be cases where the
old code would cause lost/wrong values for NDB and/or InnoDB tables.
- Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags:
OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG.
This allowed me to remove some wrong warnings about:
"Some non-transactional changed tables couldn't be rolled back"
- Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset
(thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose
some warnings about
"Some non-transactional changed tables couldn't be rolled back")
- Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table()
which could cause delete_table to report random failures.
- Fixed core dumps for some tests when running with --debug
- Added missing FN_LIBCHAR in mysql_rm_tmp_tables()
(This has probably caused us to not properly remove temporary files after
crash)
- slow_logs was not properly initialized, which could maybe cause
extra/lost entries in slow log.
- If we get an duplicate row on insert, change column map to read and
write all columns while retrying the operation. This is required by
the definition of REPLACE and also ensures that fields that are only
part of UPDATE are properly handled. This fixed a bug in NDB and
REPLACE where REPLACE wrongly copied some column values from the replaced
row.
- For table handler that doesn't support NULL in keys, we would give an error
when creating a primary key with NULL fields, even after the fields has been
automaticly converted to NOT NULL.
- Creating a primary key on a SPATIAL key, would fail if field was not
declared as NOT NULL.
Cleanups:
- Removed not used condition argument to setup_tables
- Removed not needed item function reset_query_id_processor().
- Field->add_index is removed. Now this is instead maintained in
(field->flags & FIELD_IN_ADD_INDEX)
- Field->fieldnr is removed (use field->field_index instead)
- New argument to filesort() to indicate that it should return a set of
row pointers (not used columns). This allowed me to remove some references
to sql_command in filesort and should also enable us to return column
results in some cases where we couldn't before.
- Changed column bitmap handling in opt_range.cc to be aligned with TABLE
bitmap, which allowed me to use bitmap functions instead of looping over
all fields to create some needed bitmaps. (Faster and smaller code)
- Broke up found too long lines
- Moved some variable declaration at start of function for better code
readability.
- Removed some not used arguments from functions.
(setup_fields(), mysql_prepare_insert_check_table())
- setup_fields() now takes an enum instead of an int for marking columns
usage.
- For internal temporary tables, use handler::write_row(),
handler::delete_row() and handler::update_row() instead of
handler::ha_xxxx() for faster execution.
- Changed some constants to enum's and define's.
- Using separate column read and write sets allows for easier checking
of timestamp field was set by statement.
- Remove calls to free_io_cache() as this is now done automaticly in ha_reset()
- Don't build table->normalized_path as this is now identical to table->path
(after bar's fixes to convert filenames)
- Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to
do comparision with the 'convert-dbug-for-diff' tool.
Things left to do in 5.1:
- We wrongly log failed CREATE TABLE ... SELECT in some cases when using
row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result)
Mats has promised to look into this.
- Test that my fix for CREATE TABLE ... SELECT is indeed correct.
(I added several test cases for this, but in this case it's better that
someone else also tests this throughly).
Lars has promosed to do this.
2006-06-04 17:52:22 +02:00
|
|
|
(SQL_SELECT *) 0, HA_POS_ERROR, 1,
|
2005-05-14 17:31:22 +02:00
|
|
|
&examined_rows)) ==
|
|
|
|
HA_POS_ERROR)
|
2000-11-11 19:27:34 +01:00
|
|
|
goto err;
|
|
|
|
};
|
|
|
|
|
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes
Changes that requires code changes in other code of other storage engines.
(Note that all changes are very straightforward and one should find all issues
by compiling a --debug build and fixing all compiler errors and all
asserts in field.cc while running the test suite),
- New optional handler function introduced: reset()
This is called after every DML statement to make it easy for a handler to
statement specific cleanups.
(The only case it's not called is if force the file to be closed)
- handler::extra(HA_EXTRA_RESET) is removed. Code that was there before
should be moved to handler::reset()
- table->read_set contains a bitmap over all columns that are needed
in the query. read_row() and similar functions only needs to read these
columns
- table->write_set contains a bitmap over all columns that will be updated
in the query. write_row() and update_row() only needs to update these
columns.
The above bitmaps should now be up to date in all context
(including ALTER TABLE, filesort()).
The handler is informed of any changes to the bitmap after
fix_fields() by calling the virtual function
handler::column_bitmaps_signal(). If the handler does caching of
these bitmaps (instead of using table->read_set, table->write_set),
it should redo the caching in this code. as the signal() may be sent
several times, it's probably best to set a variable in the signal
and redo the caching on read_row() / write_row() if the variable was
set.
- Removed the read_set and write_set bitmap objects from the handler class
- Removed all column bit handling functions from the handler class.
(Now one instead uses the normal bitmap functions in my_bitmap.c instead
of handler dedicated bitmap functions)
- field->query_id is removed. One should instead instead check
table->read_set and table->write_set if a field is used in the query.
- handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and
handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now
instead use table->read_set to check for which columns to retrieve.
- If a handler needs to call Field->val() or Field->store() on columns
that are not used in the query, one should install a temporary
all-columns-used map while doing so. For this, we provide the following
functions:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
field->val();
dbug_tmp_restore_column_map(table->read_set, old_map);
and similar for the write map:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
field->val();
dbug_tmp_restore_column_map(table->write_set, old_map);
If this is not done, you will sooner or later hit a DBUG_ASSERT
in the field store() / val() functions.
(For not DBUG binaries, the dbug_tmp_restore_column_map() and
dbug_tmp_restore_column_map() are inline dummy functions and should
be optimized away be the compiler).
- If one needs to temporary set the column map for all binaries (and not
just to avoid the DBUG_ASSERT() in the Field::store() / Field::val()
methods) one should use the functions tmp_use_all_columns() and
tmp_restore_column_map() instead of the above dbug_ variants.
- All 'status' fields in the handler base class (like records,
data_file_length etc) are now stored in a 'stats' struct. This makes
it easier to know what status variables are provided by the base
handler. This requires some trivial variable names in the extra()
function.
- New virtual function handler::records(). This is called to optimize
COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true.
(stats.records is not supposed to be an exact value. It's only has to
be 'reasonable enough' for the optimizer to be able to choose a good
optimization path).
- Non virtual handler::init() function added for caching of virtual
constants from engine.
- Removed has_transactions() virtual method. Now one should instead return
HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support
transactions.
- The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument
that is to be used with 'new handler_name()' to allocate the handler
in the right area. The xxxx_create_handler() function is also
responsible for any initialization of the object before returning.
For example, one should change:
static handler *myisam_create_handler(TABLE_SHARE *table)
{
return new ha_myisam(table);
}
->
static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root)
{
return new (mem_root) ha_myisam(table);
}
- New optional virtual function: use_hidden_primary_key().
This is called in case of an update/delete when
(table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
but we don't have a primary key. This allows the handler to take precisions
in remembering any hidden primary key to able to update/delete any
found row. The default handler marks all columns to be read.
- handler::table_flags() now returns a ulonglong (to allow for more flags).
- New/changed table_flags()
- HA_HAS_RECORDS Set if ::records() is supported
- HA_NO_TRANSACTIONS Set if engine doesn't support transactions
- HA_PRIMARY_KEY_REQUIRED_FOR_DELETE
Set if we should mark all primary key columns for
read when reading rows as part of a DELETE
statement. If there is no primary key,
all columns are marked for read.
- HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some
cases (based on table->read_set)
- HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS
Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION.
- HA_DUPP_POS Renamed to HA_DUPLICATE_POS
- HA_REQUIRES_KEY_COLUMNS_FOR_DELETE
Set this if we should mark ALL key columns for
read when when reading rows as part of a DELETE
statement. In case of an update we will mark
all keys for read for which key part changed
value.
- HA_STATS_RECORDS_IS_EXACT
Set this if stats.records is exact.
(This saves us some extra records() calls
when optimizing COUNT(*))
- Removed table_flags()
- HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if
handler::records() gives an exact count() and
HA_STATS_RECORDS_IS_EXACT if stats.records is exact.
- HA_READ_RND_SAME Removed (no one supported this one)
- Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk()
- Renamed handler::dupp_pos to handler::dup_pos
- Removed not used variable handler::sortkey
Upper level handler changes:
- ha_reset() now does some overall checks and calls ::reset()
- ha_table_flags() added. This is a cached version of table_flags(). The
cache is updated on engine creation time and updated on open.
MySQL level changes (not obvious from the above):
- DBUG_ASSERT() added to check that column usage matches what is set
in the column usage bit maps. (This found a LOT of bugs in current
column marking code).
- In 5.1 before, all used columns was marked in read_set and only updated
columns was marked in write_set. Now we only mark columns for which we
need a value in read_set.
- Column bitmaps are created in open_binary_frm() and open_table_from_share().
(Before this was in table.cc)
- handler::table_flags() calls are replaced with handler::ha_table_flags()
- For calling field->val() you must have the corresponding bit set in
table->read_set. For calling field->store() you must have the
corresponding bit set in table->write_set. (There are asserts in
all store()/val() functions to catch wrong usage)
- thd->set_query_id is renamed to thd->mark_used_columns and instead
of setting this to an integer value, this has now the values:
MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE
Changed also all variables named 'set_query_id' to mark_used_columns.
- In filesort() we now inform the handler of exactly which columns are needed
doing the sort and choosing the rows.
- The TABLE_SHARE object has a 'all_set' column bitmap one can use
when one needs a column bitmap with all columns set.
(This is used for table->use_all_columns() and other places)
- The TABLE object has 3 column bitmaps:
- def_read_set Default bitmap for columns to be read
- def_write_set Default bitmap for columns to be written
- tmp_set Can be used as a temporary bitmap when needed.
The table object has also two pointer to bitmaps read_set and write_set
that the handler should use to find out which columns are used in which way.
- count() optimization now calls handler::records() instead of using
handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true).
- Added extra argument to Item::walk() to indicate if we should also
traverse sub queries.
- Added TABLE parameter to cp_buffer_from_ref()
- Don't close tables created with CREATE ... SELECT but keep them in
the table cache. (Faster usage of newly created tables).
New interfaces:
- table->clear_column_bitmaps() to initialize the bitmaps for tables
at start of new statements.
- table->column_bitmaps_set() to set up new column bitmaps and signal
the handler about this.
- table->column_bitmaps_set_no_signal() for some few cases where we need
to setup new column bitmaps but don't signal the handler (as the handler
has already been signaled about these before). Used for the momement
only in opt_range.cc when doing ROR scans.
- table->use_all_columns() to install a bitmap where all columns are marked
as use in the read and the write set.
- table->default_column_bitmaps() to install the normal read and write
column bitmaps, but not signaling the handler about this.
This is mainly used when creating TABLE instances.
- table->mark_columns_needed_for_delete(),
table->mark_columns_needed_for_delete() and
table->mark_columns_needed_for_insert() to allow us to put additional
columns in column usage maps if handler so requires.
(The handler indicates what it neads in handler->table_flags())
- table->prepare_for_position() to allow us to tell handler that it
needs to read primary key parts to be able to store them in
future table->position() calls.
(This replaces the table->file->ha_retrieve_all_pk function)
- table->mark_auto_increment_column() to tell handler are going to update
columns part of any auto_increment key.
- table->mark_columns_used_by_index() to mark all columns that is part of
an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow
it to quickly know that it only needs to read colums that are part
of the key. (The handler can also use the column map for detecting this,
but simpler/faster handler can just monitor the extra() call).
- table->mark_columns_used_by_index_no_reset() to in addition to other columns,
also mark all columns that is used by the given key.
- table->restore_column_maps_after_mark_index() to restore to default
column maps after a call to table->mark_columns_used_by_index().
- New item function register_field_in_read_map(), for marking used columns
in table->read_map. Used by filesort() to mark all used columns
- Maintain in TABLE->merge_keys set of all keys that are used in query.
(Simplices some optimization loops)
- Maintain Field->part_of_key_not_clustered which is like Field->part_of_key
but the field in the clustered key is not assumed to be part of all index.
(used in opt_range.cc for faster loops)
- dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map()
tmp_use_all_columns() and tmp_restore_column_map() functions to temporally
mark all columns as usable. The 'dbug_' version is primarily intended
inside a handler when it wants to just call Field:store() & Field::val()
functions, but don't need the column maps set for any other usage.
(ie:: bitmap_is_set() is never called)
- We can't use compare_records() to skip updates for handlers that returns
a partial column set and the read_set doesn't cover all columns in the
write set. The reason for this is that if we have a column marked only for
write we can't in the MySQL level know if the value changed or not.
The reason this worked before was that MySQL marked all to be written
columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden
bug'.
- open_table_from_share() does not anymore setup temporary MEM_ROOT
object as a thread specific variable for the handler. Instead we
send the to-be-used MEMROOT to get_new_handler().
(Simpler, faster code)
Bugs fixed:
- Column marking was not done correctly in a lot of cases.
(ALTER TABLE, when using triggers, auto_increment fields etc)
(Could potentially result in wrong values inserted in table handlers
relying on that the old column maps or field->set_query_id was correct)
Especially when it comes to triggers, there may be cases where the
old code would cause lost/wrong values for NDB and/or InnoDB tables.
- Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags:
OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG.
This allowed me to remove some wrong warnings about:
"Some non-transactional changed tables couldn't be rolled back"
- Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset
(thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose
some warnings about
"Some non-transactional changed tables couldn't be rolled back")
- Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table()
which could cause delete_table to report random failures.
- Fixed core dumps for some tests when running with --debug
- Added missing FN_LIBCHAR in mysql_rm_tmp_tables()
(This has probably caused us to not properly remove temporary files after
crash)
- slow_logs was not properly initialized, which could maybe cause
extra/lost entries in slow log.
- If we get an duplicate row on insert, change column map to read and
write all columns while retrying the operation. This is required by
the definition of REPLACE and also ensures that fields that are only
part of UPDATE are properly handled. This fixed a bug in NDB and
REPLACE where REPLACE wrongly copied some column values from the replaced
row.
- For table handler that doesn't support NULL in keys, we would give an error
when creating a primary key with NULL fields, even after the fields has been
automaticly converted to NOT NULL.
- Creating a primary key on a SPATIAL key, would fail if field was not
declared as NOT NULL.
Cleanups:
- Removed not used condition argument to setup_tables
- Removed not needed item function reset_query_id_processor().
- Field->add_index is removed. Now this is instead maintained in
(field->flags & FIELD_IN_ADD_INDEX)
- Field->fieldnr is removed (use field->field_index instead)
- New argument to filesort() to indicate that it should return a set of
row pointers (not used columns). This allowed me to remove some references
to sql_command in filesort and should also enable us to return column
results in some cases where we couldn't before.
- Changed column bitmap handling in opt_range.cc to be aligned with TABLE
bitmap, which allowed me to use bitmap functions instead of looping over
all fields to create some needed bitmaps. (Faster and smaller code)
- Broke up found too long lines
- Moved some variable declaration at start of function for better code
readability.
- Removed some not used arguments from functions.
(setup_fields(), mysql_prepare_insert_check_table())
- setup_fields() now takes an enum instead of an int for marking columns
usage.
- For internal temporary tables, use handler::write_row(),
handler::delete_row() and handler::update_row() instead of
handler::ha_xxxx() for faster execution.
- Changed some constants to enum's and define's.
- Using separate column read and write sets allows for easier checking
of timestamp field was set by statement.
- Remove calls to free_io_cache() as this is now done automaticly in ha_reset()
- Don't build table->normalized_path as this is now identical to table->path
(after bar's fixes to convert filenames)
- Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to
do comparision with the 'convert-dbug-for-diff' tool.
Things left to do in 5.1:
- We wrongly log failed CREATE TABLE ... SELECT in some cases when using
row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result)
Mats has promised to look into this.
- Test that my fix for CREATE TABLE ... SELECT is indeed correct.
(I added several test cases for this, but in this case it's better that
someone else also tests this throughly).
Lars has promosed to do this.
2006-06-04 17:52:22 +02:00
|
|
|
/* Tell handler that we have values for all columns in the to table */
|
|
|
|
to->use_all_columns();
|
2000-07-31 21:29:14 +02:00
|
|
|
init_read_record(&info, thd, from, (SQL_SELECT *) 0, 1,1);
|
2006-07-01 23:51:10 +02:00
|
|
|
if (ignore)
|
2000-12-24 14:19:00 +01:00
|
|
|
to->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
|
2003-04-30 09:02:28 +02:00
|
|
|
thd->row_count= 0;
|
2005-01-06 12:00:13 +01:00
|
|
|
restore_record(to, s->default_values); // Create empty record
|
2000-07-31 21:29:14 +02:00
|
|
|
while (!(error=info.read_record(&info)))
|
|
|
|
{
|
|
|
|
if (thd->killed)
|
|
|
|
{
|
2003-04-08 16:18:33 +02:00
|
|
|
thd->send_kill_message();
|
2000-07-31 21:29:14 +02:00
|
|
|
error= 1;
|
|
|
|
break;
|
|
|
|
}
|
2003-04-30 09:02:28 +02:00
|
|
|
thd->row_count++;
|
2004-07-30 14:17:12 +02:00
|
|
|
if (to->next_number_field)
|
|
|
|
{
|
2004-08-24 17:00:45 +02:00
|
|
|
if (auto_increment_field_copied)
|
2004-07-30 14:17:12 +02:00
|
|
|
to->auto_increment_field_not_null= TRUE;
|
2004-08-24 17:00:45 +02:00
|
|
|
else
|
|
|
|
to->next_number_field->reset();
|
|
|
|
}
|
2005-04-01 14:04:50 +02:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
for (Copy_field *copy_ptr=copy ; copy_ptr != copy_end ; copy_ptr++)
|
2004-08-24 17:00:45 +02:00
|
|
|
{
|
2000-07-31 21:29:14 +02:00
|
|
|
copy_ptr->do_copy(copy_ptr);
|
2004-07-30 14:17:12 +02:00
|
|
|
}
|
WL#3146 "less locking in auto_increment":
this is a cleanup patch for our current auto_increment handling:
new names for auto_increment variables in THD, new methods to manipulate them
(see sql_class.h), some move into handler::, causing less backup/restore
work when executing substatements.
This makes the logic hopefully clearer, less work is is needed in
mysql_insert().
By cleaning up, using different variables for different purposes (instead
of one for 3 things...), we fix those bugs, which someone may want to fix
in 5.0 too:
BUG#20339 "stored procedure using LAST_INSERT_ID() does not replicate
statement-based"
BUG#20341 "stored function inserting into one auto_increment puts bad
data in slave"
BUG#19243 "wrong LAST_INSERT_ID() after ON DUPLICATE KEY UPDATE"
(now if a row is updated, LAST_INSERT_ID() will return its id)
and re-fixes:
BUG#6880 "LAST_INSERT_ID() value changes during multi-row INSERT"
(already fixed differently by Ramil in 4.1)
Test of documented behaviour of mysql_insert_id() (there was no test).
The behaviour changes introduced are:
- LAST_INSERT_ID() now returns "the first autogenerated auto_increment value
successfully inserted", instead of "the first autogenerated auto_increment
value if any row was successfully inserted", see auto_increment.test.
Same for mysql_insert_id(), see mysql_client_test.c.
- LAST_INSERT_ID() returns the id of the updated row if ON DUPLICATE KEY
UPDATE, see auto_increment.test. Same for mysql_insert_id(), see
mysql_client_test.c.
- LAST_INSERT_ID() does not change if no autogenerated value was successfully
inserted (it used to then be 0), see auto_increment.test.
- if in INSERT SELECT no autogenerated value was successfully inserted,
mysql_insert_id() now returns the id of the last inserted row (it already
did this for INSERT VALUES), see mysql_client_test.c.
- if INSERT SELECT uses LAST_INSERT_ID(X), mysql_insert_id() now returns X
(it already did this for INSERT VALUES), see mysql_client_test.c.
- NDB now behaves like other engines wrt SET INSERT_ID: with INSERT IGNORE,
the id passed in SET INSERT_ID is re-used until a row succeeds; SET INSERT_ID
influences not only the first row now.
Additionally, when unlocking a table we check that the thread is not keeping
a next_insert_id (as the table is unlocked that id is potentially out-of-date);
forgetting about this next_insert_id is done in a new
handler::ha_release_auto_increment().
Finally we prepare for engines capable of reserving finite-length intervals
of auto_increment values: we store such intervals in THD. The next step
(to be done by the replication team in 5.1) is to read those intervals from
THD and actually store them in the statement-based binary log. NDB
will be a good engine to test that.
2006-07-09 17:52:19 +02:00
|
|
|
prev_insert_id= to->file->next_insert_id;
|
2005-12-22 06:39:02 +01:00
|
|
|
if ((error=to->file->ha_write_row((byte*) to->record[0])))
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2006-07-01 23:51:10 +02:00
|
|
|
if (!ignore ||
|
2006-07-01 06:01:37 +02:00
|
|
|
to->file->is_fatal_error(error, HA_CHECK_DUP))
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2006-07-01 06:01:37 +02:00
|
|
|
if (!to->file->is_fatal_error(error, HA_CHECK_DUP))
|
2006-05-12 15:02:42 +02:00
|
|
|
{
|
|
|
|
uint key_nr= to->file->get_dup_key(error);
|
|
|
|
if ((int) key_nr >= 0)
|
|
|
|
{
|
|
|
|
const char *err_msg= ER(ER_DUP_ENTRY);
|
|
|
|
if (key_nr == 0 &&
|
2006-06-05 05:16:08 +02:00
|
|
|
(to->key_info[0].key_part[0].field->flags &
|
|
|
|
AUTO_INCREMENT_FLAG))
|
2006-05-12 15:02:42 +02:00
|
|
|
err_msg= ER(ER_DUP_ENTRY_AUTOINCREMENT_CASE);
|
2006-06-05 05:16:08 +02:00
|
|
|
to->file->print_keydup_error(key_nr, err_msg);
|
2006-05-12 15:02:42 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
to->file->print_error(error,MYF(0));
|
|
|
|
break;
|
|
|
|
}
|
WL#3146 "less locking in auto_increment":
this is a cleanup patch for our current auto_increment handling:
new names for auto_increment variables in THD, new methods to manipulate them
(see sql_class.h), some move into handler::, causing less backup/restore
work when executing substatements.
This makes the logic hopefully clearer, less work is is needed in
mysql_insert().
By cleaning up, using different variables for different purposes (instead
of one for 3 things...), we fix those bugs, which someone may want to fix
in 5.0 too:
BUG#20339 "stored procedure using LAST_INSERT_ID() does not replicate
statement-based"
BUG#20341 "stored function inserting into one auto_increment puts bad
data in slave"
BUG#19243 "wrong LAST_INSERT_ID() after ON DUPLICATE KEY UPDATE"
(now if a row is updated, LAST_INSERT_ID() will return its id)
and re-fixes:
BUG#6880 "LAST_INSERT_ID() value changes during multi-row INSERT"
(already fixed differently by Ramil in 4.1)
Test of documented behaviour of mysql_insert_id() (there was no test).
The behaviour changes introduced are:
- LAST_INSERT_ID() now returns "the first autogenerated auto_increment value
successfully inserted", instead of "the first autogenerated auto_increment
value if any row was successfully inserted", see auto_increment.test.
Same for mysql_insert_id(), see mysql_client_test.c.
- LAST_INSERT_ID() returns the id of the updated row if ON DUPLICATE KEY
UPDATE, see auto_increment.test. Same for mysql_insert_id(), see
mysql_client_test.c.
- LAST_INSERT_ID() does not change if no autogenerated value was successfully
inserted (it used to then be 0), see auto_increment.test.
- if in INSERT SELECT no autogenerated value was successfully inserted,
mysql_insert_id() now returns the id of the last inserted row (it already
did this for INSERT VALUES), see mysql_client_test.c.
- if INSERT SELECT uses LAST_INSERT_ID(X), mysql_insert_id() now returns X
(it already did this for INSERT VALUES), see mysql_client_test.c.
- NDB now behaves like other engines wrt SET INSERT_ID: with INSERT IGNORE,
the id passed in SET INSERT_ID is re-used until a row succeeds; SET INSERT_ID
influences not only the first row now.
Additionally, when unlocking a table we check that the thread is not keeping
a next_insert_id (as the table is unlocked that id is potentially out-of-date);
forgetting about this next_insert_id is done in a new
handler::ha_release_auto_increment().
Finally we prepare for engines capable of reserving finite-length intervals
of auto_increment values: we store such intervals in THD. The next step
(to be done by the replication team in 5.1) is to read those intervals from
THD and actually store them in the statement-based binary log. NDB
will be a good engine to test that.
2006-07-09 17:52:19 +02:00
|
|
|
to->file->restore_auto_increment(prev_insert_id);
|
2000-07-31 21:29:14 +02:00
|
|
|
delete_count++;
|
|
|
|
}
|
|
|
|
else
|
2003-08-21 16:15:06 +02:00
|
|
|
found_count++;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
end_read_record(&info);
|
2001-01-23 00:25:07 +01:00
|
|
|
free_io_cache(from);
|
2001-10-08 03:58:07 +02:00
|
|
|
delete [] copy; // This is never 0
|
2004-04-07 16:04:28 +02:00
|
|
|
|
2006-06-02 22:21:32 +02:00
|
|
|
if (to->file->ha_end_bulk_insert() && error <= 0)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-04-07 16:04:28 +02:00
|
|
|
to->file->print_error(my_errno,MYF(0));
|
2000-07-31 21:29:14 +02:00
|
|
|
error=1;
|
|
|
|
}
|
2000-12-24 14:19:00 +01:00
|
|
|
to->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
|
2000-11-24 00:51:18 +01:00
|
|
|
|
2004-09-03 17:11:09 +02:00
|
|
|
ha_enable_transaction(thd,TRUE);
|
2004-09-26 17:11:28 +02:00
|
|
|
|
2000-11-24 00:51:18 +01:00
|
|
|
/*
|
|
|
|
Ensure that the new table is saved properly to disk so that we
|
|
|
|
can do a rename
|
|
|
|
*/
|
|
|
|
if (ha_commit_stmt(thd))
|
|
|
|
error=1;
|
|
|
|
if (ha_commit(thd))
|
|
|
|
error=1;
|
2004-10-06 00:24:21 +02:00
|
|
|
|
2000-11-11 19:27:34 +01:00
|
|
|
err:
|
2004-10-07 11:02:39 +02:00
|
|
|
thd->variables.sql_mode= save_sql_mode;
|
2005-04-01 14:04:50 +02:00
|
|
|
thd->abort_on_warning= 0;
|
2000-11-11 19:27:34 +01:00
|
|
|
free_io_cache(from);
|
2000-07-31 21:29:14 +02:00
|
|
|
*copied= found_count;
|
|
|
|
*deleted=delete_count;
|
WL#3146 "less locking in auto_increment":
this is a cleanup patch for our current auto_increment handling:
new names for auto_increment variables in THD, new methods to manipulate them
(see sql_class.h), some move into handler::, causing less backup/restore
work when executing substatements.
This makes the logic hopefully clearer, less work is is needed in
mysql_insert().
By cleaning up, using different variables for different purposes (instead
of one for 3 things...), we fix those bugs, which someone may want to fix
in 5.0 too:
BUG#20339 "stored procedure using LAST_INSERT_ID() does not replicate
statement-based"
BUG#20341 "stored function inserting into one auto_increment puts bad
data in slave"
BUG#19243 "wrong LAST_INSERT_ID() after ON DUPLICATE KEY UPDATE"
(now if a row is updated, LAST_INSERT_ID() will return its id)
and re-fixes:
BUG#6880 "LAST_INSERT_ID() value changes during multi-row INSERT"
(already fixed differently by Ramil in 4.1)
Test of documented behaviour of mysql_insert_id() (there was no test).
The behaviour changes introduced are:
- LAST_INSERT_ID() now returns "the first autogenerated auto_increment value
successfully inserted", instead of "the first autogenerated auto_increment
value if any row was successfully inserted", see auto_increment.test.
Same for mysql_insert_id(), see mysql_client_test.c.
- LAST_INSERT_ID() returns the id of the updated row if ON DUPLICATE KEY
UPDATE, see auto_increment.test. Same for mysql_insert_id(), see
mysql_client_test.c.
- LAST_INSERT_ID() does not change if no autogenerated value was successfully
inserted (it used to then be 0), see auto_increment.test.
- if in INSERT SELECT no autogenerated value was successfully inserted,
mysql_insert_id() now returns the id of the last inserted row (it already
did this for INSERT VALUES), see mysql_client_test.c.
- if INSERT SELECT uses LAST_INSERT_ID(X), mysql_insert_id() now returns X
(it already did this for INSERT VALUES), see mysql_client_test.c.
- NDB now behaves like other engines wrt SET INSERT_ID: with INSERT IGNORE,
the id passed in SET INSERT_ID is re-used until a row succeeds; SET INSERT_ID
influences not only the first row now.
Additionally, when unlocking a table we check that the thread is not keeping
a next_insert_id (as the table is unlocked that id is potentially out-of-date);
forgetting about this next_insert_id is done in a new
handler::ha_release_auto_increment().
Finally we prepare for engines capable of reserving finite-length intervals
of auto_increment values: we store such intervals in THD. The next step
(to be done by the replication team in 5.1) is to read those intervals from
THD and actually store them in the statement-based binary log. NDB
will be a good engine to test that.
2006-07-09 17:52:19 +02:00
|
|
|
to->file->ha_release_auto_increment();
|
2006-01-26 09:25:37 +01:00
|
|
|
if (to->file->ha_external_lock(thd,F_UNLCK))
|
2004-09-18 20:33:39 +02:00
|
|
|
error=1;
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(error > 0 ? -1 : 0);
|
|
|
|
}
|
2003-08-21 16:15:06 +02:00
|
|
|
|
2003-09-11 18:06:23 +02:00
|
|
|
|
2004-06-10 16:41:24 +02:00
|
|
|
/*
|
|
|
|
Recreates tables by calling mysql_alter_table().
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
mysql_recreate_table()
|
|
|
|
thd Thread handler
|
|
|
|
tables Tables to recreate
|
|
|
|
do_send_ok If we should send_ok() or leave it to caller
|
|
|
|
|
|
|
|
RETURN
|
|
|
|
Like mysql_alter_table().
|
|
|
|
*/
|
2004-10-20 03:04:37 +02:00
|
|
|
bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list,
|
|
|
|
bool do_send_ok)
|
2004-06-10 16:41:24 +02:00
|
|
|
{
|
|
|
|
DBUG_ENTER("mysql_recreate_table");
|
|
|
|
LEX *lex= thd->lex;
|
|
|
|
HA_CREATE_INFO create_info;
|
|
|
|
lex->create_list.empty();
|
|
|
|
lex->key_list.empty();
|
|
|
|
lex->col_list.empty();
|
|
|
|
lex->alter_info.reset();
|
|
|
|
bzero((char*) &create_info,sizeof(create_info));
|
2006-05-28 14:51:01 +02:00
|
|
|
create_info.db_type= 0;
|
2005-01-02 14:23:34 +01:00
|
|
|
create_info.row_type=ROW_TYPE_NOT_USED;
|
2004-06-10 16:41:24 +02:00
|
|
|
create_info.default_table_charset=default_charset_info;
|
2004-11-09 02:58:44 +01:00
|
|
|
/* Force alter table to recreate table */
|
2005-07-22 22:43:59 +02:00
|
|
|
lex->alter_info.flags= (ALTER_CHANGE_COLUMN | ALTER_RECREATE);
|
2004-06-10 16:41:24 +02:00
|
|
|
DBUG_RETURN(mysql_alter_table(thd, NullS, NullS, &create_info,
|
|
|
|
table_list, lex->create_list,
|
|
|
|
lex->key_list, 0, (ORDER *) 0,
|
2006-07-01 23:51:10 +02:00
|
|
|
0, &lex->alter_info, do_send_ok));
|
2004-06-10 16:41:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-05-05 19:08:40 +02:00
|
|
|
bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
|
|
|
|
HA_CHECK_OPT *check_opt)
|
2003-08-21 16:15:06 +02:00
|
|
|
{
|
|
|
|
TABLE_LIST *table;
|
|
|
|
List<Item> field_list;
|
|
|
|
Item *item;
|
|
|
|
Protocol *protocol= thd->protocol;
|
2004-03-23 18:28:18 +01:00
|
|
|
DBUG_ENTER("mysql_checksum_table");
|
2003-08-21 16:15:06 +02:00
|
|
|
|
|
|
|
field_list.push_back(item = new Item_empty_string("Table", NAME_LEN*2));
|
|
|
|
item->maybe_null= 1;
|
|
|
|
field_list.push_back(item=new Item_int("Checksum",(longlong) 1,21));
|
|
|
|
item->maybe_null= 1;
|
2004-08-03 12:32:21 +02:00
|
|
|
if (protocol->send_fields(&field_list,
|
|
|
|
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
2003-08-21 16:15:06 +02:00
|
|
|
|
2004-07-16 00:15:55 +02:00
|
|
|
for (table= tables; table; table= table->next_local)
|
2003-08-21 16:15:06 +02:00
|
|
|
{
|
|
|
|
char table_name[NAME_LEN*2+2];
|
2003-09-03 11:34:32 +02:00
|
|
|
TABLE *t;
|
2003-08-21 16:15:06 +02:00
|
|
|
|
2005-01-06 12:00:13 +01:00
|
|
|
strxmov(table_name, table->db ,".", table->table_name, NullS);
|
2003-09-11 18:06:23 +02:00
|
|
|
|
2005-09-27 20:11:09 +02:00
|
|
|
t= table->table= open_ltable(thd, table, TL_READ);
|
2003-09-11 18:06:23 +02:00
|
|
|
thd->clear_error(); // these errors shouldn't get client
|
2003-08-21 16:15:06 +02:00
|
|
|
|
|
|
|
protocol->prepare_for_resend();
|
|
|
|
protocol->store(table_name, system_charset_info);
|
|
|
|
|
2003-09-03 11:34:32 +02:00
|
|
|
if (!t)
|
2003-08-21 16:15:06 +02:00
|
|
|
{
|
2003-09-11 18:06:23 +02:00
|
|
|
/* Table didn't exist */
|
2003-08-21 16:15:06 +02:00
|
|
|
protocol->store_null();
|
2004-10-20 03:04:37 +02:00
|
|
|
thd->clear_error();
|
2003-08-21 16:15:06 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2003-09-03 11:34:32 +02:00
|
|
|
t->pos_in_table_list= table;
|
2003-08-21 16:15:06 +02:00
|
|
|
|
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes
Changes that requires code changes in other code of other storage engines.
(Note that all changes are very straightforward and one should find all issues
by compiling a --debug build and fixing all compiler errors and all
asserts in field.cc while running the test suite),
- New optional handler function introduced: reset()
This is called after every DML statement to make it easy for a handler to
statement specific cleanups.
(The only case it's not called is if force the file to be closed)
- handler::extra(HA_EXTRA_RESET) is removed. Code that was there before
should be moved to handler::reset()
- table->read_set contains a bitmap over all columns that are needed
in the query. read_row() and similar functions only needs to read these
columns
- table->write_set contains a bitmap over all columns that will be updated
in the query. write_row() and update_row() only needs to update these
columns.
The above bitmaps should now be up to date in all context
(including ALTER TABLE, filesort()).
The handler is informed of any changes to the bitmap after
fix_fields() by calling the virtual function
handler::column_bitmaps_signal(). If the handler does caching of
these bitmaps (instead of using table->read_set, table->write_set),
it should redo the caching in this code. as the signal() may be sent
several times, it's probably best to set a variable in the signal
and redo the caching on read_row() / write_row() if the variable was
set.
- Removed the read_set and write_set bitmap objects from the handler class
- Removed all column bit handling functions from the handler class.
(Now one instead uses the normal bitmap functions in my_bitmap.c instead
of handler dedicated bitmap functions)
- field->query_id is removed. One should instead instead check
table->read_set and table->write_set if a field is used in the query.
- handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and
handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now
instead use table->read_set to check for which columns to retrieve.
- If a handler needs to call Field->val() or Field->store() on columns
that are not used in the query, one should install a temporary
all-columns-used map while doing so. For this, we provide the following
functions:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
field->val();
dbug_tmp_restore_column_map(table->read_set, old_map);
and similar for the write map:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
field->val();
dbug_tmp_restore_column_map(table->write_set, old_map);
If this is not done, you will sooner or later hit a DBUG_ASSERT
in the field store() / val() functions.
(For not DBUG binaries, the dbug_tmp_restore_column_map() and
dbug_tmp_restore_column_map() are inline dummy functions and should
be optimized away be the compiler).
- If one needs to temporary set the column map for all binaries (and not
just to avoid the DBUG_ASSERT() in the Field::store() / Field::val()
methods) one should use the functions tmp_use_all_columns() and
tmp_restore_column_map() instead of the above dbug_ variants.
- All 'status' fields in the handler base class (like records,
data_file_length etc) are now stored in a 'stats' struct. This makes
it easier to know what status variables are provided by the base
handler. This requires some trivial variable names in the extra()
function.
- New virtual function handler::records(). This is called to optimize
COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true.
(stats.records is not supposed to be an exact value. It's only has to
be 'reasonable enough' for the optimizer to be able to choose a good
optimization path).
- Non virtual handler::init() function added for caching of virtual
constants from engine.
- Removed has_transactions() virtual method. Now one should instead return
HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support
transactions.
- The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument
that is to be used with 'new handler_name()' to allocate the handler
in the right area. The xxxx_create_handler() function is also
responsible for any initialization of the object before returning.
For example, one should change:
static handler *myisam_create_handler(TABLE_SHARE *table)
{
return new ha_myisam(table);
}
->
static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root)
{
return new (mem_root) ha_myisam(table);
}
- New optional virtual function: use_hidden_primary_key().
This is called in case of an update/delete when
(table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
but we don't have a primary key. This allows the handler to take precisions
in remembering any hidden primary key to able to update/delete any
found row. The default handler marks all columns to be read.
- handler::table_flags() now returns a ulonglong (to allow for more flags).
- New/changed table_flags()
- HA_HAS_RECORDS Set if ::records() is supported
- HA_NO_TRANSACTIONS Set if engine doesn't support transactions
- HA_PRIMARY_KEY_REQUIRED_FOR_DELETE
Set if we should mark all primary key columns for
read when reading rows as part of a DELETE
statement. If there is no primary key,
all columns are marked for read.
- HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some
cases (based on table->read_set)
- HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS
Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION.
- HA_DUPP_POS Renamed to HA_DUPLICATE_POS
- HA_REQUIRES_KEY_COLUMNS_FOR_DELETE
Set this if we should mark ALL key columns for
read when when reading rows as part of a DELETE
statement. In case of an update we will mark
all keys for read for which key part changed
value.
- HA_STATS_RECORDS_IS_EXACT
Set this if stats.records is exact.
(This saves us some extra records() calls
when optimizing COUNT(*))
- Removed table_flags()
- HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if
handler::records() gives an exact count() and
HA_STATS_RECORDS_IS_EXACT if stats.records is exact.
- HA_READ_RND_SAME Removed (no one supported this one)
- Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk()
- Renamed handler::dupp_pos to handler::dup_pos
- Removed not used variable handler::sortkey
Upper level handler changes:
- ha_reset() now does some overall checks and calls ::reset()
- ha_table_flags() added. This is a cached version of table_flags(). The
cache is updated on engine creation time and updated on open.
MySQL level changes (not obvious from the above):
- DBUG_ASSERT() added to check that column usage matches what is set
in the column usage bit maps. (This found a LOT of bugs in current
column marking code).
- In 5.1 before, all used columns was marked in read_set and only updated
columns was marked in write_set. Now we only mark columns for which we
need a value in read_set.
- Column bitmaps are created in open_binary_frm() and open_table_from_share().
(Before this was in table.cc)
- handler::table_flags() calls are replaced with handler::ha_table_flags()
- For calling field->val() you must have the corresponding bit set in
table->read_set. For calling field->store() you must have the
corresponding bit set in table->write_set. (There are asserts in
all store()/val() functions to catch wrong usage)
- thd->set_query_id is renamed to thd->mark_used_columns and instead
of setting this to an integer value, this has now the values:
MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE
Changed also all variables named 'set_query_id' to mark_used_columns.
- In filesort() we now inform the handler of exactly which columns are needed
doing the sort and choosing the rows.
- The TABLE_SHARE object has a 'all_set' column bitmap one can use
when one needs a column bitmap with all columns set.
(This is used for table->use_all_columns() and other places)
- The TABLE object has 3 column bitmaps:
- def_read_set Default bitmap for columns to be read
- def_write_set Default bitmap for columns to be written
- tmp_set Can be used as a temporary bitmap when needed.
The table object has also two pointer to bitmaps read_set and write_set
that the handler should use to find out which columns are used in which way.
- count() optimization now calls handler::records() instead of using
handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true).
- Added extra argument to Item::walk() to indicate if we should also
traverse sub queries.
- Added TABLE parameter to cp_buffer_from_ref()
- Don't close tables created with CREATE ... SELECT but keep them in
the table cache. (Faster usage of newly created tables).
New interfaces:
- table->clear_column_bitmaps() to initialize the bitmaps for tables
at start of new statements.
- table->column_bitmaps_set() to set up new column bitmaps and signal
the handler about this.
- table->column_bitmaps_set_no_signal() for some few cases where we need
to setup new column bitmaps but don't signal the handler (as the handler
has already been signaled about these before). Used for the momement
only in opt_range.cc when doing ROR scans.
- table->use_all_columns() to install a bitmap where all columns are marked
as use in the read and the write set.
- table->default_column_bitmaps() to install the normal read and write
column bitmaps, but not signaling the handler about this.
This is mainly used when creating TABLE instances.
- table->mark_columns_needed_for_delete(),
table->mark_columns_needed_for_delete() and
table->mark_columns_needed_for_insert() to allow us to put additional
columns in column usage maps if handler so requires.
(The handler indicates what it neads in handler->table_flags())
- table->prepare_for_position() to allow us to tell handler that it
needs to read primary key parts to be able to store them in
future table->position() calls.
(This replaces the table->file->ha_retrieve_all_pk function)
- table->mark_auto_increment_column() to tell handler are going to update
columns part of any auto_increment key.
- table->mark_columns_used_by_index() to mark all columns that is part of
an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow
it to quickly know that it only needs to read colums that are part
of the key. (The handler can also use the column map for detecting this,
but simpler/faster handler can just monitor the extra() call).
- table->mark_columns_used_by_index_no_reset() to in addition to other columns,
also mark all columns that is used by the given key.
- table->restore_column_maps_after_mark_index() to restore to default
column maps after a call to table->mark_columns_used_by_index().
- New item function register_field_in_read_map(), for marking used columns
in table->read_map. Used by filesort() to mark all used columns
- Maintain in TABLE->merge_keys set of all keys that are used in query.
(Simplices some optimization loops)
- Maintain Field->part_of_key_not_clustered which is like Field->part_of_key
but the field in the clustered key is not assumed to be part of all index.
(used in opt_range.cc for faster loops)
- dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map()
tmp_use_all_columns() and tmp_restore_column_map() functions to temporally
mark all columns as usable. The 'dbug_' version is primarily intended
inside a handler when it wants to just call Field:store() & Field::val()
functions, but don't need the column maps set for any other usage.
(ie:: bitmap_is_set() is never called)
- We can't use compare_records() to skip updates for handlers that returns
a partial column set and the read_set doesn't cover all columns in the
write set. The reason for this is that if we have a column marked only for
write we can't in the MySQL level know if the value changed or not.
The reason this worked before was that MySQL marked all to be written
columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden
bug'.
- open_table_from_share() does not anymore setup temporary MEM_ROOT
object as a thread specific variable for the handler. Instead we
send the to-be-used MEMROOT to get_new_handler().
(Simpler, faster code)
Bugs fixed:
- Column marking was not done correctly in a lot of cases.
(ALTER TABLE, when using triggers, auto_increment fields etc)
(Could potentially result in wrong values inserted in table handlers
relying on that the old column maps or field->set_query_id was correct)
Especially when it comes to triggers, there may be cases where the
old code would cause lost/wrong values for NDB and/or InnoDB tables.
- Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags:
OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG.
This allowed me to remove some wrong warnings about:
"Some non-transactional changed tables couldn't be rolled back"
- Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset
(thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose
some warnings about
"Some non-transactional changed tables couldn't be rolled back")
- Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table()
which could cause delete_table to report random failures.
- Fixed core dumps for some tests when running with --debug
- Added missing FN_LIBCHAR in mysql_rm_tmp_tables()
(This has probably caused us to not properly remove temporary files after
crash)
- slow_logs was not properly initialized, which could maybe cause
extra/lost entries in slow log.
- If we get an duplicate row on insert, change column map to read and
write all columns while retrying the operation. This is required by
the definition of REPLACE and also ensures that fields that are only
part of UPDATE are properly handled. This fixed a bug in NDB and
REPLACE where REPLACE wrongly copied some column values from the replaced
row.
- For table handler that doesn't support NULL in keys, we would give an error
when creating a primary key with NULL fields, even after the fields has been
automaticly converted to NOT NULL.
- Creating a primary key on a SPATIAL key, would fail if field was not
declared as NOT NULL.
Cleanups:
- Removed not used condition argument to setup_tables
- Removed not needed item function reset_query_id_processor().
- Field->add_index is removed. Now this is instead maintained in
(field->flags & FIELD_IN_ADD_INDEX)
- Field->fieldnr is removed (use field->field_index instead)
- New argument to filesort() to indicate that it should return a set of
row pointers (not used columns). This allowed me to remove some references
to sql_command in filesort and should also enable us to return column
results in some cases where we couldn't before.
- Changed column bitmap handling in opt_range.cc to be aligned with TABLE
bitmap, which allowed me to use bitmap functions instead of looping over
all fields to create some needed bitmaps. (Faster and smaller code)
- Broke up found too long lines
- Moved some variable declaration at start of function for better code
readability.
- Removed some not used arguments from functions.
(setup_fields(), mysql_prepare_insert_check_table())
- setup_fields() now takes an enum instead of an int for marking columns
usage.
- For internal temporary tables, use handler::write_row(),
handler::delete_row() and handler::update_row() instead of
handler::ha_xxxx() for faster execution.
- Changed some constants to enum's and define's.
- Using separate column read and write sets allows for easier checking
of timestamp field was set by statement.
- Remove calls to free_io_cache() as this is now done automaticly in ha_reset()
- Don't build table->normalized_path as this is now identical to table->path
(after bar's fixes to convert filenames)
- Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to
do comparision with the 'convert-dbug-for-diff' tool.
Things left to do in 5.1:
- We wrongly log failed CREATE TABLE ... SELECT in some cases when using
row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result)
Mats has promised to look into this.
- Test that my fix for CREATE TABLE ... SELECT is indeed correct.
(I added several test cases for this, but in this case it's better that
someone else also tests this throughly).
Lars has promosed to do this.
2006-06-04 17:52:22 +02:00
|
|
|
if (t->file->ha_table_flags() & HA_HAS_CHECKSUM &&
|
2004-04-08 16:56:45 +02:00
|
|
|
!(check_opt->flags & T_EXTEND))
|
|
|
|
protocol->store((ulonglong)t->file->checksum());
|
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes
Changes that requires code changes in other code of other storage engines.
(Note that all changes are very straightforward and one should find all issues
by compiling a --debug build and fixing all compiler errors and all
asserts in field.cc while running the test suite),
- New optional handler function introduced: reset()
This is called after every DML statement to make it easy for a handler to
statement specific cleanups.
(The only case it's not called is if force the file to be closed)
- handler::extra(HA_EXTRA_RESET) is removed. Code that was there before
should be moved to handler::reset()
- table->read_set contains a bitmap over all columns that are needed
in the query. read_row() and similar functions only needs to read these
columns
- table->write_set contains a bitmap over all columns that will be updated
in the query. write_row() and update_row() only needs to update these
columns.
The above bitmaps should now be up to date in all context
(including ALTER TABLE, filesort()).
The handler is informed of any changes to the bitmap after
fix_fields() by calling the virtual function
handler::column_bitmaps_signal(). If the handler does caching of
these bitmaps (instead of using table->read_set, table->write_set),
it should redo the caching in this code. as the signal() may be sent
several times, it's probably best to set a variable in the signal
and redo the caching on read_row() / write_row() if the variable was
set.
- Removed the read_set and write_set bitmap objects from the handler class
- Removed all column bit handling functions from the handler class.
(Now one instead uses the normal bitmap functions in my_bitmap.c instead
of handler dedicated bitmap functions)
- field->query_id is removed. One should instead instead check
table->read_set and table->write_set if a field is used in the query.
- handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and
handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now
instead use table->read_set to check for which columns to retrieve.
- If a handler needs to call Field->val() or Field->store() on columns
that are not used in the query, one should install a temporary
all-columns-used map while doing so. For this, we provide the following
functions:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
field->val();
dbug_tmp_restore_column_map(table->read_set, old_map);
and similar for the write map:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
field->val();
dbug_tmp_restore_column_map(table->write_set, old_map);
If this is not done, you will sooner or later hit a DBUG_ASSERT
in the field store() / val() functions.
(For not DBUG binaries, the dbug_tmp_restore_column_map() and
dbug_tmp_restore_column_map() are inline dummy functions and should
be optimized away be the compiler).
- If one needs to temporary set the column map for all binaries (and not
just to avoid the DBUG_ASSERT() in the Field::store() / Field::val()
methods) one should use the functions tmp_use_all_columns() and
tmp_restore_column_map() instead of the above dbug_ variants.
- All 'status' fields in the handler base class (like records,
data_file_length etc) are now stored in a 'stats' struct. This makes
it easier to know what status variables are provided by the base
handler. This requires some trivial variable names in the extra()
function.
- New virtual function handler::records(). This is called to optimize
COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true.
(stats.records is not supposed to be an exact value. It's only has to
be 'reasonable enough' for the optimizer to be able to choose a good
optimization path).
- Non virtual handler::init() function added for caching of virtual
constants from engine.
- Removed has_transactions() virtual method. Now one should instead return
HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support
transactions.
- The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument
that is to be used with 'new handler_name()' to allocate the handler
in the right area. The xxxx_create_handler() function is also
responsible for any initialization of the object before returning.
For example, one should change:
static handler *myisam_create_handler(TABLE_SHARE *table)
{
return new ha_myisam(table);
}
->
static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root)
{
return new (mem_root) ha_myisam(table);
}
- New optional virtual function: use_hidden_primary_key().
This is called in case of an update/delete when
(table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
but we don't have a primary key. This allows the handler to take precisions
in remembering any hidden primary key to able to update/delete any
found row. The default handler marks all columns to be read.
- handler::table_flags() now returns a ulonglong (to allow for more flags).
- New/changed table_flags()
- HA_HAS_RECORDS Set if ::records() is supported
- HA_NO_TRANSACTIONS Set if engine doesn't support transactions
- HA_PRIMARY_KEY_REQUIRED_FOR_DELETE
Set if we should mark all primary key columns for
read when reading rows as part of a DELETE
statement. If there is no primary key,
all columns are marked for read.
- HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some
cases (based on table->read_set)
- HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS
Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION.
- HA_DUPP_POS Renamed to HA_DUPLICATE_POS
- HA_REQUIRES_KEY_COLUMNS_FOR_DELETE
Set this if we should mark ALL key columns for
read when when reading rows as part of a DELETE
statement. In case of an update we will mark
all keys for read for which key part changed
value.
- HA_STATS_RECORDS_IS_EXACT
Set this if stats.records is exact.
(This saves us some extra records() calls
when optimizing COUNT(*))
- Removed table_flags()
- HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if
handler::records() gives an exact count() and
HA_STATS_RECORDS_IS_EXACT if stats.records is exact.
- HA_READ_RND_SAME Removed (no one supported this one)
- Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk()
- Renamed handler::dupp_pos to handler::dup_pos
- Removed not used variable handler::sortkey
Upper level handler changes:
- ha_reset() now does some overall checks and calls ::reset()
- ha_table_flags() added. This is a cached version of table_flags(). The
cache is updated on engine creation time and updated on open.
MySQL level changes (not obvious from the above):
- DBUG_ASSERT() added to check that column usage matches what is set
in the column usage bit maps. (This found a LOT of bugs in current
column marking code).
- In 5.1 before, all used columns was marked in read_set and only updated
columns was marked in write_set. Now we only mark columns for which we
need a value in read_set.
- Column bitmaps are created in open_binary_frm() and open_table_from_share().
(Before this was in table.cc)
- handler::table_flags() calls are replaced with handler::ha_table_flags()
- For calling field->val() you must have the corresponding bit set in
table->read_set. For calling field->store() you must have the
corresponding bit set in table->write_set. (There are asserts in
all store()/val() functions to catch wrong usage)
- thd->set_query_id is renamed to thd->mark_used_columns and instead
of setting this to an integer value, this has now the values:
MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE
Changed also all variables named 'set_query_id' to mark_used_columns.
- In filesort() we now inform the handler of exactly which columns are needed
doing the sort and choosing the rows.
- The TABLE_SHARE object has a 'all_set' column bitmap one can use
when one needs a column bitmap with all columns set.
(This is used for table->use_all_columns() and other places)
- The TABLE object has 3 column bitmaps:
- def_read_set Default bitmap for columns to be read
- def_write_set Default bitmap for columns to be written
- tmp_set Can be used as a temporary bitmap when needed.
The table object has also two pointer to bitmaps read_set and write_set
that the handler should use to find out which columns are used in which way.
- count() optimization now calls handler::records() instead of using
handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true).
- Added extra argument to Item::walk() to indicate if we should also
traverse sub queries.
- Added TABLE parameter to cp_buffer_from_ref()
- Don't close tables created with CREATE ... SELECT but keep them in
the table cache. (Faster usage of newly created tables).
New interfaces:
- table->clear_column_bitmaps() to initialize the bitmaps for tables
at start of new statements.
- table->column_bitmaps_set() to set up new column bitmaps and signal
the handler about this.
- table->column_bitmaps_set_no_signal() for some few cases where we need
to setup new column bitmaps but don't signal the handler (as the handler
has already been signaled about these before). Used for the momement
only in opt_range.cc when doing ROR scans.
- table->use_all_columns() to install a bitmap where all columns are marked
as use in the read and the write set.
- table->default_column_bitmaps() to install the normal read and write
column bitmaps, but not signaling the handler about this.
This is mainly used when creating TABLE instances.
- table->mark_columns_needed_for_delete(),
table->mark_columns_needed_for_delete() and
table->mark_columns_needed_for_insert() to allow us to put additional
columns in column usage maps if handler so requires.
(The handler indicates what it neads in handler->table_flags())
- table->prepare_for_position() to allow us to tell handler that it
needs to read primary key parts to be able to store them in
future table->position() calls.
(This replaces the table->file->ha_retrieve_all_pk function)
- table->mark_auto_increment_column() to tell handler are going to update
columns part of any auto_increment key.
- table->mark_columns_used_by_index() to mark all columns that is part of
an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow
it to quickly know that it only needs to read colums that are part
of the key. (The handler can also use the column map for detecting this,
but simpler/faster handler can just monitor the extra() call).
- table->mark_columns_used_by_index_no_reset() to in addition to other columns,
also mark all columns that is used by the given key.
- table->restore_column_maps_after_mark_index() to restore to default
column maps after a call to table->mark_columns_used_by_index().
- New item function register_field_in_read_map(), for marking used columns
in table->read_map. Used by filesort() to mark all used columns
- Maintain in TABLE->merge_keys set of all keys that are used in query.
(Simplices some optimization loops)
- Maintain Field->part_of_key_not_clustered which is like Field->part_of_key
but the field in the clustered key is not assumed to be part of all index.
(used in opt_range.cc for faster loops)
- dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map()
tmp_use_all_columns() and tmp_restore_column_map() functions to temporally
mark all columns as usable. The 'dbug_' version is primarily intended
inside a handler when it wants to just call Field:store() & Field::val()
functions, but don't need the column maps set for any other usage.
(ie:: bitmap_is_set() is never called)
- We can't use compare_records() to skip updates for handlers that returns
a partial column set and the read_set doesn't cover all columns in the
write set. The reason for this is that if we have a column marked only for
write we can't in the MySQL level know if the value changed or not.
The reason this worked before was that MySQL marked all to be written
columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden
bug'.
- open_table_from_share() does not anymore setup temporary MEM_ROOT
object as a thread specific variable for the handler. Instead we
send the to-be-used MEMROOT to get_new_handler().
(Simpler, faster code)
Bugs fixed:
- Column marking was not done correctly in a lot of cases.
(ALTER TABLE, when using triggers, auto_increment fields etc)
(Could potentially result in wrong values inserted in table handlers
relying on that the old column maps or field->set_query_id was correct)
Especially when it comes to triggers, there may be cases where the
old code would cause lost/wrong values for NDB and/or InnoDB tables.
- Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags:
OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG.
This allowed me to remove some wrong warnings about:
"Some non-transactional changed tables couldn't be rolled back"
- Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset
(thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose
some warnings about
"Some non-transactional changed tables couldn't be rolled back")
- Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table()
which could cause delete_table to report random failures.
- Fixed core dumps for some tests when running with --debug
- Added missing FN_LIBCHAR in mysql_rm_tmp_tables()
(This has probably caused us to not properly remove temporary files after
crash)
- slow_logs was not properly initialized, which could maybe cause
extra/lost entries in slow log.
- If we get an duplicate row on insert, change column map to read and
write all columns while retrying the operation. This is required by
the definition of REPLACE and also ensures that fields that are only
part of UPDATE are properly handled. This fixed a bug in NDB and
REPLACE where REPLACE wrongly copied some column values from the replaced
row.
- For table handler that doesn't support NULL in keys, we would give an error
when creating a primary key with NULL fields, even after the fields has been
automaticly converted to NOT NULL.
- Creating a primary key on a SPATIAL key, would fail if field was not
declared as NOT NULL.
Cleanups:
- Removed not used condition argument to setup_tables
- Removed not needed item function reset_query_id_processor().
- Field->add_index is removed. Now this is instead maintained in
(field->flags & FIELD_IN_ADD_INDEX)
- Field->fieldnr is removed (use field->field_index instead)
- New argument to filesort() to indicate that it should return a set of
row pointers (not used columns). This allowed me to remove some references
to sql_command in filesort and should also enable us to return column
results in some cases where we couldn't before.
- Changed column bitmap handling in opt_range.cc to be aligned with TABLE
bitmap, which allowed me to use bitmap functions instead of looping over
all fields to create some needed bitmaps. (Faster and smaller code)
- Broke up found too long lines
- Moved some variable declaration at start of function for better code
readability.
- Removed some not used arguments from functions.
(setup_fields(), mysql_prepare_insert_check_table())
- setup_fields() now takes an enum instead of an int for marking columns
usage.
- For internal temporary tables, use handler::write_row(),
handler::delete_row() and handler::update_row() instead of
handler::ha_xxxx() for faster execution.
- Changed some constants to enum's and define's.
- Using separate column read and write sets allows for easier checking
of timestamp field was set by statement.
- Remove calls to free_io_cache() as this is now done automaticly in ha_reset()
- Don't build table->normalized_path as this is now identical to table->path
(after bar's fixes to convert filenames)
- Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to
do comparision with the 'convert-dbug-for-diff' tool.
Things left to do in 5.1:
- We wrongly log failed CREATE TABLE ... SELECT in some cases when using
row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result)
Mats has promised to look into this.
- Test that my fix for CREATE TABLE ... SELECT is indeed correct.
(I added several test cases for this, but in this case it's better that
someone else also tests this throughly).
Lars has promosed to do this.
2006-06-04 17:52:22 +02:00
|
|
|
else if (!(t->file->ha_table_flags() & HA_HAS_CHECKSUM) &&
|
2003-09-11 18:06:23 +02:00
|
|
|
(check_opt->flags & T_QUICK))
|
2004-04-08 16:56:45 +02:00
|
|
|
protocol->store_null();
|
2003-09-03 11:34:32 +02:00
|
|
|
else
|
|
|
|
{
|
2004-04-08 16:56:45 +02:00
|
|
|
/* calculating table's checksum */
|
|
|
|
ha_checksum crc= 0;
|
2005-10-04 17:04:20 +02:00
|
|
|
uchar null_mask=256 - (1 << t->s->last_null_bit_pos);
|
2004-04-08 16:56:45 +02:00
|
|
|
|
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes
Changes that requires code changes in other code of other storage engines.
(Note that all changes are very straightforward and one should find all issues
by compiling a --debug build and fixing all compiler errors and all
asserts in field.cc while running the test suite),
- New optional handler function introduced: reset()
This is called after every DML statement to make it easy for a handler to
statement specific cleanups.
(The only case it's not called is if force the file to be closed)
- handler::extra(HA_EXTRA_RESET) is removed. Code that was there before
should be moved to handler::reset()
- table->read_set contains a bitmap over all columns that are needed
in the query. read_row() and similar functions only needs to read these
columns
- table->write_set contains a bitmap over all columns that will be updated
in the query. write_row() and update_row() only needs to update these
columns.
The above bitmaps should now be up to date in all context
(including ALTER TABLE, filesort()).
The handler is informed of any changes to the bitmap after
fix_fields() by calling the virtual function
handler::column_bitmaps_signal(). If the handler does caching of
these bitmaps (instead of using table->read_set, table->write_set),
it should redo the caching in this code. as the signal() may be sent
several times, it's probably best to set a variable in the signal
and redo the caching on read_row() / write_row() if the variable was
set.
- Removed the read_set and write_set bitmap objects from the handler class
- Removed all column bit handling functions from the handler class.
(Now one instead uses the normal bitmap functions in my_bitmap.c instead
of handler dedicated bitmap functions)
- field->query_id is removed. One should instead instead check
table->read_set and table->write_set if a field is used in the query.
- handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and
handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now
instead use table->read_set to check for which columns to retrieve.
- If a handler needs to call Field->val() or Field->store() on columns
that are not used in the query, one should install a temporary
all-columns-used map while doing so. For this, we provide the following
functions:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
field->val();
dbug_tmp_restore_column_map(table->read_set, old_map);
and similar for the write map:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
field->val();
dbug_tmp_restore_column_map(table->write_set, old_map);
If this is not done, you will sooner or later hit a DBUG_ASSERT
in the field store() / val() functions.
(For not DBUG binaries, the dbug_tmp_restore_column_map() and
dbug_tmp_restore_column_map() are inline dummy functions and should
be optimized away be the compiler).
- If one needs to temporary set the column map for all binaries (and not
just to avoid the DBUG_ASSERT() in the Field::store() / Field::val()
methods) one should use the functions tmp_use_all_columns() and
tmp_restore_column_map() instead of the above dbug_ variants.
- All 'status' fields in the handler base class (like records,
data_file_length etc) are now stored in a 'stats' struct. This makes
it easier to know what status variables are provided by the base
handler. This requires some trivial variable names in the extra()
function.
- New virtual function handler::records(). This is called to optimize
COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true.
(stats.records is not supposed to be an exact value. It's only has to
be 'reasonable enough' for the optimizer to be able to choose a good
optimization path).
- Non virtual handler::init() function added for caching of virtual
constants from engine.
- Removed has_transactions() virtual method. Now one should instead return
HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support
transactions.
- The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument
that is to be used with 'new handler_name()' to allocate the handler
in the right area. The xxxx_create_handler() function is also
responsible for any initialization of the object before returning.
For example, one should change:
static handler *myisam_create_handler(TABLE_SHARE *table)
{
return new ha_myisam(table);
}
->
static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root)
{
return new (mem_root) ha_myisam(table);
}
- New optional virtual function: use_hidden_primary_key().
This is called in case of an update/delete when
(table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
but we don't have a primary key. This allows the handler to take precisions
in remembering any hidden primary key to able to update/delete any
found row. The default handler marks all columns to be read.
- handler::table_flags() now returns a ulonglong (to allow for more flags).
- New/changed table_flags()
- HA_HAS_RECORDS Set if ::records() is supported
- HA_NO_TRANSACTIONS Set if engine doesn't support transactions
- HA_PRIMARY_KEY_REQUIRED_FOR_DELETE
Set if we should mark all primary key columns for
read when reading rows as part of a DELETE
statement. If there is no primary key,
all columns are marked for read.
- HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some
cases (based on table->read_set)
- HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS
Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION.
- HA_DUPP_POS Renamed to HA_DUPLICATE_POS
- HA_REQUIRES_KEY_COLUMNS_FOR_DELETE
Set this if we should mark ALL key columns for
read when when reading rows as part of a DELETE
statement. In case of an update we will mark
all keys for read for which key part changed
value.
- HA_STATS_RECORDS_IS_EXACT
Set this if stats.records is exact.
(This saves us some extra records() calls
when optimizing COUNT(*))
- Removed table_flags()
- HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if
handler::records() gives an exact count() and
HA_STATS_RECORDS_IS_EXACT if stats.records is exact.
- HA_READ_RND_SAME Removed (no one supported this one)
- Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk()
- Renamed handler::dupp_pos to handler::dup_pos
- Removed not used variable handler::sortkey
Upper level handler changes:
- ha_reset() now does some overall checks and calls ::reset()
- ha_table_flags() added. This is a cached version of table_flags(). The
cache is updated on engine creation time and updated on open.
MySQL level changes (not obvious from the above):
- DBUG_ASSERT() added to check that column usage matches what is set
in the column usage bit maps. (This found a LOT of bugs in current
column marking code).
- In 5.1 before, all used columns was marked in read_set and only updated
columns was marked in write_set. Now we only mark columns for which we
need a value in read_set.
- Column bitmaps are created in open_binary_frm() and open_table_from_share().
(Before this was in table.cc)
- handler::table_flags() calls are replaced with handler::ha_table_flags()
- For calling field->val() you must have the corresponding bit set in
table->read_set. For calling field->store() you must have the
corresponding bit set in table->write_set. (There are asserts in
all store()/val() functions to catch wrong usage)
- thd->set_query_id is renamed to thd->mark_used_columns and instead
of setting this to an integer value, this has now the values:
MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE
Changed also all variables named 'set_query_id' to mark_used_columns.
- In filesort() we now inform the handler of exactly which columns are needed
doing the sort and choosing the rows.
- The TABLE_SHARE object has a 'all_set' column bitmap one can use
when one needs a column bitmap with all columns set.
(This is used for table->use_all_columns() and other places)
- The TABLE object has 3 column bitmaps:
- def_read_set Default bitmap for columns to be read
- def_write_set Default bitmap for columns to be written
- tmp_set Can be used as a temporary bitmap when needed.
The table object has also two pointer to bitmaps read_set and write_set
that the handler should use to find out which columns are used in which way.
- count() optimization now calls handler::records() instead of using
handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true).
- Added extra argument to Item::walk() to indicate if we should also
traverse sub queries.
- Added TABLE parameter to cp_buffer_from_ref()
- Don't close tables created with CREATE ... SELECT but keep them in
the table cache. (Faster usage of newly created tables).
New interfaces:
- table->clear_column_bitmaps() to initialize the bitmaps for tables
at start of new statements.
- table->column_bitmaps_set() to set up new column bitmaps and signal
the handler about this.
- table->column_bitmaps_set_no_signal() for some few cases where we need
to setup new column bitmaps but don't signal the handler (as the handler
has already been signaled about these before). Used for the momement
only in opt_range.cc when doing ROR scans.
- table->use_all_columns() to install a bitmap where all columns are marked
as use in the read and the write set.
- table->default_column_bitmaps() to install the normal read and write
column bitmaps, but not signaling the handler about this.
This is mainly used when creating TABLE instances.
- table->mark_columns_needed_for_delete(),
table->mark_columns_needed_for_delete() and
table->mark_columns_needed_for_insert() to allow us to put additional
columns in column usage maps if handler so requires.
(The handler indicates what it neads in handler->table_flags())
- table->prepare_for_position() to allow us to tell handler that it
needs to read primary key parts to be able to store them in
future table->position() calls.
(This replaces the table->file->ha_retrieve_all_pk function)
- table->mark_auto_increment_column() to tell handler are going to update
columns part of any auto_increment key.
- table->mark_columns_used_by_index() to mark all columns that is part of
an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow
it to quickly know that it only needs to read colums that are part
of the key. (The handler can also use the column map for detecting this,
but simpler/faster handler can just monitor the extra() call).
- table->mark_columns_used_by_index_no_reset() to in addition to other columns,
also mark all columns that is used by the given key.
- table->restore_column_maps_after_mark_index() to restore to default
column maps after a call to table->mark_columns_used_by_index().
- New item function register_field_in_read_map(), for marking used columns
in table->read_map. Used by filesort() to mark all used columns
- Maintain in TABLE->merge_keys set of all keys that are used in query.
(Simplices some optimization loops)
- Maintain Field->part_of_key_not_clustered which is like Field->part_of_key
but the field in the clustered key is not assumed to be part of all index.
(used in opt_range.cc for faster loops)
- dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map()
tmp_use_all_columns() and tmp_restore_column_map() functions to temporally
mark all columns as usable. The 'dbug_' version is primarily intended
inside a handler when it wants to just call Field:store() & Field::val()
functions, but don't need the column maps set for any other usage.
(ie:: bitmap_is_set() is never called)
- We can't use compare_records() to skip updates for handlers that returns
a partial column set and the read_set doesn't cover all columns in the
write set. The reason for this is that if we have a column marked only for
write we can't in the MySQL level know if the value changed or not.
The reason this worked before was that MySQL marked all to be written
columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden
bug'.
- open_table_from_share() does not anymore setup temporary MEM_ROOT
object as a thread specific variable for the handler. Instead we
send the to-be-used MEMROOT to get_new_handler().
(Simpler, faster code)
Bugs fixed:
- Column marking was not done correctly in a lot of cases.
(ALTER TABLE, when using triggers, auto_increment fields etc)
(Could potentially result in wrong values inserted in table handlers
relying on that the old column maps or field->set_query_id was correct)
Especially when it comes to triggers, there may be cases where the
old code would cause lost/wrong values for NDB and/or InnoDB tables.
- Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags:
OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG.
This allowed me to remove some wrong warnings about:
"Some non-transactional changed tables couldn't be rolled back"
- Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset
(thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose
some warnings about
"Some non-transactional changed tables couldn't be rolled back")
- Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table()
which could cause delete_table to report random failures.
- Fixed core dumps for some tests when running with --debug
- Added missing FN_LIBCHAR in mysql_rm_tmp_tables()
(This has probably caused us to not properly remove temporary files after
crash)
- slow_logs was not properly initialized, which could maybe cause
extra/lost entries in slow log.
- If we get an duplicate row on insert, change column map to read and
write all columns while retrying the operation. This is required by
the definition of REPLACE and also ensures that fields that are only
part of UPDATE are properly handled. This fixed a bug in NDB and
REPLACE where REPLACE wrongly copied some column values from the replaced
row.
- For table handler that doesn't support NULL in keys, we would give an error
when creating a primary key with NULL fields, even after the fields has been
automaticly converted to NOT NULL.
- Creating a primary key on a SPATIAL key, would fail if field was not
declared as NOT NULL.
Cleanups:
- Removed not used condition argument to setup_tables
- Removed not needed item function reset_query_id_processor().
- Field->add_index is removed. Now this is instead maintained in
(field->flags & FIELD_IN_ADD_INDEX)
- Field->fieldnr is removed (use field->field_index instead)
- New argument to filesort() to indicate that it should return a set of
row pointers (not used columns). This allowed me to remove some references
to sql_command in filesort and should also enable us to return column
results in some cases where we couldn't before.
- Changed column bitmap handling in opt_range.cc to be aligned with TABLE
bitmap, which allowed me to use bitmap functions instead of looping over
all fields to create some needed bitmaps. (Faster and smaller code)
- Broke up found too long lines
- Moved some variable declaration at start of function for better code
readability.
- Removed some not used arguments from functions.
(setup_fields(), mysql_prepare_insert_check_table())
- setup_fields() now takes an enum instead of an int for marking columns
usage.
- For internal temporary tables, use handler::write_row(),
handler::delete_row() and handler::update_row() instead of
handler::ha_xxxx() for faster execution.
- Changed some constants to enum's and define's.
- Using separate column read and write sets allows for easier checking
of timestamp field was set by statement.
- Remove calls to free_io_cache() as this is now done automaticly in ha_reset()
- Don't build table->normalized_path as this is now identical to table->path
(after bar's fixes to convert filenames)
- Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to
do comparision with the 'convert-dbug-for-diff' tool.
Things left to do in 5.1:
- We wrongly log failed CREATE TABLE ... SELECT in some cases when using
row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result)
Mats has promised to look into this.
- Test that my fix for CREATE TABLE ... SELECT is indeed correct.
(I added several test cases for this, but in this case it's better that
someone else also tests this throughly).
Lars has promosed to do this.
2006-06-04 17:52:22 +02:00
|
|
|
t->use_all_columns();
|
2004-04-08 16:56:45 +02:00
|
|
|
|
2004-06-23 12:29:05 +02:00
|
|
|
if (t->file->ha_rnd_init(1))
|
2004-04-08 16:56:45 +02:00
|
|
|
protocol->store_null();
|
|
|
|
else
|
|
|
|
{
|
2005-08-29 17:08:41 +02:00
|
|
|
for (;;)
|
2004-04-08 16:56:45 +02:00
|
|
|
{
|
|
|
|
ha_checksum row_crc= 0;
|
2005-08-29 17:08:41 +02:00
|
|
|
int error= t->file->rnd_next(t->record[0]);
|
|
|
|
if (unlikely(error))
|
|
|
|
{
|
|
|
|
if (error == HA_ERR_RECORD_DELETED)
|
|
|
|
continue;
|
|
|
|
break;
|
|
|
|
}
|
2005-10-04 17:04:20 +02:00
|
|
|
if (t->s->null_bytes)
|
|
|
|
{
|
|
|
|
/* fix undefined null bits */
|
|
|
|
t->record[0][t->s->null_bytes-1] |= null_mask;
|
2005-10-05 19:36:20 +02:00
|
|
|
if (!(t->s->db_create_options & HA_OPTION_PACK_RECORD))
|
|
|
|
t->record[0][0] |= 1;
|
|
|
|
|
2005-10-04 17:04:20 +02:00
|
|
|
row_crc= my_checksum(row_crc, t->record[0], t->s->null_bytes);
|
|
|
|
}
|
2003-09-03 11:34:32 +02:00
|
|
|
|
2005-01-06 12:00:13 +01:00
|
|
|
for (uint i= 0; i < t->s->fields; i++ )
|
2004-04-08 16:56:45 +02:00
|
|
|
{
|
|
|
|
Field *f= t->field[i];
|
2006-02-01 16:46:44 +01:00
|
|
|
if ((f->type() == FIELD_TYPE_BLOB) ||
|
|
|
|
(f->type() == MYSQL_TYPE_VARCHAR))
|
2004-04-08 16:56:45 +02:00
|
|
|
{
|
|
|
|
String tmp;
|
|
|
|
f->val_str(&tmp);
|
|
|
|
row_crc= my_checksum(row_crc, (byte*) tmp.ptr(), tmp.length());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
row_crc= my_checksum(row_crc, (byte*) f->ptr,
|
2003-10-15 21:40:36 +02:00
|
|
|
f->pack_length());
|
2004-04-08 16:56:45 +02:00
|
|
|
}
|
2003-09-03 11:34:32 +02:00
|
|
|
|
2004-04-08 16:56:45 +02:00
|
|
|
crc+= row_crc;
|
|
|
|
}
|
|
|
|
protocol->store((ulonglong)crc);
|
2004-06-23 12:29:05 +02:00
|
|
|
t->file->ha_rnd_end();
|
2004-04-08 16:56:45 +02:00
|
|
|
}
|
2003-09-03 11:34:32 +02:00
|
|
|
}
|
2003-09-11 18:06:23 +02:00
|
|
|
thd->clear_error();
|
2003-08-21 16:15:06 +02:00
|
|
|
close_thread_tables(thd);
|
|
|
|
table->table=0; // For query cache
|
|
|
|
}
|
|
|
|
if (protocol->write())
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
send_eof(thd);
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(FALSE);
|
2003-09-11 18:06:23 +02:00
|
|
|
|
2003-08-21 16:15:06 +02:00
|
|
|
err:
|
|
|
|
close_thread_tables(thd); // Shouldn't be needed
|
|
|
|
if (table)
|
|
|
|
table->table=0;
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
2003-08-21 16:15:06 +02:00
|
|
|
}
|
2005-06-17 23:14:44 +02:00
|
|
|
|
|
|
|
static bool check_engine(THD *thd, const char *table_name,
|
2006-02-17 17:12:35 +01:00
|
|
|
HA_CREATE_INFO *create_info)
|
2005-06-17 23:14:44 +02:00
|
|
|
{
|
2006-02-17 17:12:35 +01:00
|
|
|
handlerton **new_engine= &create_info->db_type;
|
2005-12-21 19:18:40 +01:00
|
|
|
handlerton *req_engine= *new_engine;
|
2005-10-04 17:04:20 +02:00
|
|
|
bool no_substitution=
|
2005-06-17 23:14:44 +02:00
|
|
|
test(thd->variables.sql_mode & MODE_NO_ENGINE_SUBSTITUTION);
|
2005-12-21 19:18:40 +01:00
|
|
|
if (!(*new_engine= ha_checktype(thd, ha_legacy_type(req_engine),
|
|
|
|
no_substitution, 1)))
|
2005-06-17 23:14:44 +02:00
|
|
|
return TRUE;
|
|
|
|
|
2006-05-28 14:51:01 +02:00
|
|
|
if (req_engine && req_engine != *new_engine)
|
2005-06-17 23:14:44 +02:00
|
|
|
{
|
|
|
|
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
|
|
ER_WARN_USING_OTHER_HANDLER,
|
|
|
|
ER(ER_WARN_USING_OTHER_HANDLER),
|
2005-12-21 19:18:40 +01:00
|
|
|
ha_resolve_storage_engine_name(*new_engine),
|
2005-06-17 23:14:44 +02:00
|
|
|
table_name);
|
|
|
|
}
|
2006-02-17 17:12:35 +01:00
|
|
|
if (create_info->options & HA_LEX_CREATE_TMP_TABLE &&
|
|
|
|
ha_check_storage_engine_flag(*new_engine, HTON_TEMPORARY_NOT_SUPPORTED))
|
|
|
|
{
|
|
|
|
if (create_info->used_fields & HA_CREATE_USED_ENGINE)
|
|
|
|
{
|
2006-05-28 14:51:01 +02:00
|
|
|
my_error(ER_ILLEGAL_HA_CREATE_OPTION, MYF(0),
|
2006-05-31 18:07:32 +02:00
|
|
|
hton2plugin[(*new_engine)->slot]->name.str, "TEMPORARY");
|
2006-02-17 17:12:35 +01:00
|
|
|
*new_engine= 0;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
*new_engine= &myisam_hton;
|
|
|
|
}
|
2005-06-17 23:14:44 +02:00
|
|
|
return FALSE;
|
|
|
|
}
|