You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4059 lines
117 KiB

26 years ago
26 years ago
26 years ago
26 years ago
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.
20 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
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.
20 years ago
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.
20 years ago
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.
20 years ago
26 years ago
26 years ago
26 years ago
26 years ago
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.
20 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
21 years ago
26 years ago
26 years ago
21 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
21 years ago
26 years ago
26 years ago
26 years ago
23 years ago
26 years ago
21 years ago
26 years ago
21 years ago
26 years ago
21 years ago
26 years ago
26 years ago
26 years ago
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.
20 years ago
26 years ago
26 years ago
26 years ago
26 years ago
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.
20 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
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.
20 years ago
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.
20 years ago
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.
20 years ago
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.
20 years ago
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.
20 years ago
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.
20 years ago
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.
20 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
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.
20 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
22 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
22 years ago
26 years ago
26 years ago
26 years ago
26 years ago
22 years ago
26 years ago
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.
20 years ago
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.
20 years ago
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.
20 years ago
26 years ago
21 years ago
21 years ago
22 years ago
22 years ago
22 years ago
22 years ago
22 years ago
21 years ago
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.
20 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
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.
20 years ago
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.
20 years ago
26 years ago
26 years ago
  1. /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
  2. This program is free software; you can redistribute it and/or modify
  3. it under the terms of the GNU General Public License as published by
  4. the Free Software Foundation; either version 2 of the License, or
  5. (at your option) any later version.
  6. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  9. GNU General Public License for more details.
  10. You should have received a copy of the GNU General Public License
  11. along with this program; if not, write to the Free Software
  12. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
  13. /* Some general useful functions */
  14. #include "mysql_priv.h"
  15. #include "sql_trigger.h"
  16. #include <m_ctype.h>
  17. #include "md5.h"
  18. /* Functions defined in this file */
  19. void open_table_error(TABLE_SHARE *share, int error, int db_errno,
  20. myf errortype, int errarg);
  21. static int open_binary_frm(THD *thd, TABLE_SHARE *share,
  22. uchar *head, File file);
  23. static void fix_type_pointers(const char ***array, TYPELIB *point_to_type,
  24. uint types, char **names);
  25. static uint find_field(Field **fields, uint start, uint length);
  26. /* Get column name from column hash */
  27. static byte *get_field_name(Field **buff, uint *length,
  28. my_bool not_used __attribute__((unused)))
  29. {
  30. *length= (uint) strlen((*buff)->field_name);
  31. return (byte*) (*buff)->field_name;
  32. }
  33. /*
  34. Returns pointer to '.frm' extension of the file name.
  35. SYNOPSIS
  36. fn_rext()
  37. name file name
  38. DESCRIPTION
  39. Checks file name part starting with the rightmost '.' character,
  40. and returns it if it is equal to '.frm'.
  41. TODO
  42. It is a good idea to get rid of this function modifying the code
  43. to garantee that the functions presently calling fn_rext() always
  44. get arguments in the same format: either with '.frm' or without '.frm'.
  45. RETURN VALUES
  46. Pointer to the '.frm' extension. If there is no extension,
  47. or extension is not '.frm', pointer at the end of file name.
  48. */
  49. char *fn_rext(char *name)
  50. {
  51. char *res= strrchr(name, '.');
  52. if (res && !strcmp(res, ".frm"))
  53. return res;
  54. return name + strlen(name);
  55. }
  56. /*
  57. Allocate a setup TABLE_SHARE structure
  58. SYNOPSIS
  59. alloc_table_share()
  60. TABLE_LIST Take database and table name from there
  61. key Table cache key (db \0 table_name \0...)
  62. key_length Length of key
  63. RETURN
  64. 0 Error (out of memory)
  65. # Share
  66. */
  67. TABLE_SHARE *alloc_table_share(TABLE_LIST *table_list, char *key,
  68. uint key_length)
  69. {
  70. MEM_ROOT mem_root;
  71. TABLE_SHARE *share;
  72. char path[FN_REFLEN];
  73. uint path_length;
  74. path_length= build_table_filename(path, sizeof(path) - 1,
  75. table_list->db,
  76. table_list->table_name, "");
  77. init_sql_alloc(&mem_root, TABLE_ALLOC_BLOCK_SIZE, 0);
  78. if ((share= (TABLE_SHARE*) alloc_root(&mem_root,
  79. sizeof(*share) + key_length +
  80. path_length +1)))
  81. {
  82. bzero((char*) share, sizeof(*share));
  83. share->table_cache_key.str= (char*) (share+1);
  84. share->table_cache_key.length= key_length;
  85. memcpy(share->table_cache_key.str, key, key_length);
  86. /* Use the fact the key is db/0/table_name/0 */
  87. share->db.str= share->table_cache_key.str;
  88. share->db.length= strlen(share->db.str);
  89. share->table_name.str= share->db.str + share->db.length + 1;
  90. share->table_name.length= strlen(share->table_name.str);
  91. share->path.str= share->table_cache_key.str+ key_length;
  92. share->path.length= path_length;
  93. strmov(share->path.str, path);
  94. share->normalized_path.str= share->path.str;
  95. share->normalized_path.length= path_length;
  96. share->version= refresh_version;
  97. share->flush_version= flush_version;
  98. #ifdef HAVE_ROW_BASED_REPLICATION
  99. /*
  100. This constant is used to mark that no table map version has been
  101. assigned. No arithmetic is done on the value: it will be
  102. overwritten with a value taken from MYSQL_BIN_LOG.
  103. */
  104. share->table_map_version= ~(ulonglong)0;
  105. /*
  106. Since alloc_table_share() can be called without any locking (for
  107. example, ha_create_table... functions), we do not assign a table
  108. map id here. Instead we assign a value that is not used
  109. elsewhere, and then assign a table map id inside open_table()
  110. under the protection of the LOCK_open mutex.
  111. */
  112. share->table_map_id= ~0UL;
  113. share->cached_row_logging_check= -1;
  114. #endif
  115. memcpy((char*) &share->mem_root, (char*) &mem_root, sizeof(mem_root));
  116. pthread_mutex_init(&share->mutex, MY_MUTEX_INIT_FAST);
  117. pthread_cond_init(&share->cond, NULL);
  118. }
  119. return share;
  120. }
  121. /*
  122. Initialize share for temporary tables
  123. SYNOPSIS
  124. init_tmp_table_share()
  125. share Share to fill
  126. key Table_cache_key, as generated from create_table_def_key.
  127. must start with db name.
  128. key_length Length of key
  129. table_name Table name
  130. path Path to file (possible in lower case) without .frm
  131. NOTES
  132. This is different from alloc_table_share() because temporary tables
  133. don't have to be shared between threads or put into the table def
  134. cache, so we can do some things notable simpler and faster
  135. If table is not put in thd->temporary_tables (happens only when
  136. one uses OPEN TEMPORARY) then one can specify 'db' as key and
  137. use key_length= 0 as neither table_cache_key or key_length will be used).
  138. */
  139. void init_tmp_table_share(TABLE_SHARE *share, const char *key,
  140. uint key_length, const char *table_name,
  141. const char *path)
  142. {
  143. DBUG_ENTER("init_tmp_table_share");
  144. bzero((char*) share, sizeof(*share));
  145. init_sql_alloc(&share->mem_root, TABLE_ALLOC_BLOCK_SIZE, 0);
  146. share->tmp_table= INTERNAL_TMP_TABLE;
  147. share->db.str= (char*) key;
  148. share->db.length= strlen(key);
  149. share->table_cache_key.str= (char*) key;
  150. share->table_cache_key.length= key_length;
  151. share->table_name.str= (char*) table_name;
  152. share->table_name.length= strlen(table_name);
  153. share->path.str= (char*) path;
  154. share->normalized_path.str= (char*) path;
  155. share->path.length= share->normalized_path.length= strlen(path);
  156. share->frm_version= FRM_VER_TRUE_VARCHAR;
  157. #ifdef HAVE_ROW_BASED_REPLICATION
  158. /*
  159. Temporary tables are not replicated, but we set up these fields
  160. anyway to be able to catch errors.
  161. */
  162. share->table_map_version= ~(ulonglong)0;
  163. share->table_map_id= ~0UL;
  164. share->cached_row_logging_check= -1;
  165. #endif
  166. DBUG_VOID_RETURN;
  167. }
  168. /*
  169. Free table share and memory used by it
  170. SYNOPSIS
  171. free_table_share()
  172. share Table share
  173. NOTES
  174. share->mutex must be locked when we come here if it's not a temp table
  175. */
  176. void free_table_share(TABLE_SHARE *share)
  177. {
  178. MEM_ROOT mem_root;
  179. DBUG_ENTER("free_table_share");
  180. DBUG_PRINT("enter", ("table: %s.%s", share->db.str, share->table_name.str));
  181. DBUG_ASSERT(share->ref_count == 0);
  182. /*
  183. If someone is waiting for this to be deleted, inform it about this.
  184. Don't do a delete until we know that no one is refering to this anymore.
  185. */
  186. if (share->tmp_table == NO_TMP_TABLE)
  187. {
  188. /* share->mutex is locked in release_table_share() */
  189. while (share->waiting_on_cond)
  190. {
  191. pthread_cond_broadcast(&share->cond);
  192. pthread_cond_wait(&share->cond, &share->mutex);
  193. }
  194. /* No thread refers to this anymore */
  195. pthread_mutex_unlock(&share->mutex);
  196. pthread_mutex_destroy(&share->mutex);
  197. pthread_cond_destroy(&share->cond);
  198. }
  199. hash_free(&share->name_hash);
  200. /* We must copy mem_root from share because share is allocated through it */
  201. memcpy((char*) &mem_root, (char*) &share->mem_root, sizeof(mem_root));
  202. free_root(&mem_root, MYF(0)); // Free's share
  203. DBUG_VOID_RETURN;
  204. }
  205. /*
  206. Read table definition from a binary / text based .frm file
  207. SYNOPSIS
  208. open_table_def()
  209. thd Thread handler
  210. share Fill this with table definition
  211. db_flags Bit mask of the following flags: OPEN_VIEW
  212. NOTES
  213. This function is called when the table definition is not cached in
  214. table_def_cache
  215. The data is returned in 'share', which is alloced by
  216. alloc_table_share().. The code assumes that share is initialized.
  217. RETURN VALUES
  218. 0 ok
  219. 1 Error (see open_table_error)
  220. 2 Error (see open_table_error)
  221. 3 Wrong data in .frm file
  222. 4 Error (see open_table_error)
  223. 5 Error (see open_table_error: charset unavailable)
  224. 6 Unknown .frm version
  225. */
  226. int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags)
  227. {
  228. int error, table_type;
  229. bool error_given;
  230. File file;
  231. uchar head[288], *disk_buff;
  232. char path[FN_REFLEN];
  233. MEM_ROOT **root_ptr, *old_root;
  234. DBUG_ENTER("open_table_def");
  235. DBUG_PRINT("enter", ("name: '%s.%s'",share->db.str, share->table_name.str));
  236. error= 1;
  237. error_given= 0;
  238. disk_buff= NULL;
  239. strxmov(path, share->normalized_path.str, reg_ext, NullS);
  240. if ((file= my_open(path, O_RDONLY | O_SHARE, MYF(0))) < 0)
  241. {
  242. if (strchr(share->table_name.str, '@'))
  243. goto err_not_open;
  244. /* Try unecoded 5.0 name */
  245. uint length;
  246. strxnmov(path, sizeof(path)-1,
  247. mysql_data_home, "/", share->db.str, "/",
  248. share->table_name.str, reg_ext, NullS);
  249. length= unpack_filename(path, path) - reg_ext_length;
  250. /*
  251. The following is a safety test and should never fail
  252. as the old file name should never be longer than the new one.
  253. */
  254. DBUG_ASSERT(length <= share->normalized_path.length);
  255. /*
  256. If the old and the new names have the same length,
  257. then table name does not have tricky characters,
  258. so no need to check the old file name.
  259. */
  260. if (length == share->normalized_path.length ||
  261. ((file= my_open(path, O_RDONLY | O_SHARE, MYF(0))) < 0))
  262. goto err_not_open;
  263. /* Unencoded 5.0 table name found */
  264. path[length]= '\0'; // Remove .frm extension
  265. strmov(share->normalized_path.str, path);
  266. share->normalized_path.length= length;
  267. }
  268. error= 4;
  269. if (my_read(file,(byte*) head, 64, MYF(MY_NABP)))
  270. goto err;
  271. if (head[0] == (uchar) 254 && head[1] == 1)
  272. {
  273. if (head[2] == FRM_VER || head[2] == FRM_VER+1 ||
  274. (head[2] >= FRM_VER+3 && head[2] <= FRM_VER+4))
  275. table_type= 1;
  276. else
  277. {
  278. error= 6; // Unkown .frm version
  279. goto err;
  280. }
  281. }
  282. else if (memcmp(head, STRING_WITH_LEN("TYPE=")) == 0)
  283. {
  284. error= 5;
  285. if (memcmp(head+5,"VIEW",4) == 0)
  286. {
  287. share->is_view= 1;
  288. if (db_flags & OPEN_VIEW)
  289. error= 0;
  290. }
  291. goto err;
  292. }
  293. else
  294. goto err;
  295. /* No handling of text based files yet */
  296. if (table_type == 1)
  297. {
  298. root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**, THR_MALLOC);
  299. old_root= *root_ptr;
  300. *root_ptr= &share->mem_root;
  301. error= open_binary_frm(thd, share, head, file);
  302. *root_ptr= old_root;
  303. if (share->db.length == 5 &&
  304. !my_strcasecmp(system_charset_info, share->db.str, "mysql"))
  305. {
  306. /*
  307. We can't mark all tables in 'mysql' database as system since we don't
  308. allow to lock such tables for writing with any other tables (even with
  309. other system tables) and some privilege tables need this.
  310. */
  311. if (!my_strcasecmp(system_charset_info, share->table_name.str, "proc"))
  312. share->system_table= 1;
  313. else
  314. {
  315. if (!my_strcasecmp(system_charset_info, share->table_name.str,
  316. "general_log"))
  317. share->log_table= QUERY_LOG_GENERAL;
  318. else
  319. if (!my_strcasecmp(system_charset_info, share->table_name.str,
  320. "slow_log"))
  321. share->log_table= QUERY_LOG_SLOW;
  322. }
  323. }
  324. error_given= 1;
  325. }
  326. if (!error)
  327. thd->status_var.opened_shares++;
  328. err:
  329. my_close(file, MYF(MY_WME));
  330. err_not_open:
  331. if (error && !error_given)
  332. {
  333. share->error= error;
  334. open_table_error(share, error, (share->open_errno= my_errno), 0);
  335. }
  336. DBUG_RETURN(error);
  337. }
  338. /*
  339. Read data from a binary .frm file from MySQL 3.23 - 5.0 into TABLE_SHARE
  340. */
  341. static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
  342. File file)
  343. {
  344. int error, errarg= 0;
  345. uint new_frm_ver, field_pack_length, new_field_pack_flag;
  346. uint interval_count, interval_parts, read_length, int_length;
  347. uint db_create_options, keys, key_parts, n_length;
  348. uint key_info_length, com_length, null_bit_pos;
  349. uint extra_rec_buf_length;
  350. uint i,j;
  351. bool use_hash;
  352. char *keynames, *record, *names, *comment_pos;
  353. uchar *disk_buff, *strpos, *null_flags, *null_pos;
  354. ulong pos, record_offset, *rec_per_key, rec_buff_length;
  355. handler *handler_file= 0;
  356. KEY *keyinfo;
  357. KEY_PART_INFO *key_part;
  358. SQL_CRYPT *crypted=0;
  359. Field **field_ptr, *reg_field;
  360. const char **interval_array;
  361. enum legacy_db_type legacy_db_type;
  362. my_bitmap_map *bitmaps;
  363. DBUG_ENTER("open_binary_frm");
  364. new_field_pack_flag= head[27];
  365. new_frm_ver= (head[2] - FRM_VER);
  366. field_pack_length= new_frm_ver < 2 ? 11 : 17;
  367. disk_buff= 0;
  368. error= 3;
  369. if (!(pos=get_form_pos(file,head,(TYPELIB*) 0)))
  370. goto err; /* purecov: inspected */
  371. share->frm_version= head[2];
  372. /*
  373. Check if .frm file created by MySQL 5.0. In this case we want to
  374. display CHAR fields as CHAR and not as VARCHAR.
  375. We do it this way as we want to keep the old frm version to enable
  376. MySQL 4.1 to read these files.
  377. */
  378. if (share->frm_version == FRM_VER_TRUE_VARCHAR -1 && head[33] == 5)
  379. share->frm_version= FRM_VER_TRUE_VARCHAR;
  380. #ifdef WITH_PARTITION_STORAGE_ENGINE
  381. share->default_part_db_type=
  382. ha_checktype(thd, (enum legacy_db_type) (uint) *(head+61), 0, 0);
  383. DBUG_PRINT("info", ("default_part_db_type = %u", head[61]));
  384. #endif
  385. legacy_db_type= (enum legacy_db_type) (uint) *(head+3);
  386. share->db_type= ha_checktype(thd, legacy_db_type, 0, 0);
  387. share->db_create_options= db_create_options= uint2korr(head+30);
  388. share->db_options_in_use= share->db_create_options;
  389. share->mysql_version= uint4korr(head+51);
  390. share->null_field_first= 0;
  391. if (!head[32]) // New frm file in 3.23
  392. {
  393. share->avg_row_length= uint4korr(head+34);
  394. share-> row_type= (row_type) head[40];
  395. share->table_charset= get_charset((uint) head[38],MYF(0));
  396. share->null_field_first= 1;
  397. }
  398. if (!share->table_charset)
  399. {
  400. /* unknown charset in head[38] or pre-3.23 frm */
  401. if (use_mb(default_charset_info))
  402. {
  403. /* Warn that we may be changing the size of character columns */
  404. sql_print_warning("'%s' had no or invalid character set, "
  405. "and default character set is multi-byte, "
  406. "so character column sizes may have changed",
  407. share->path);
  408. }
  409. share->table_charset= default_charset_info;
  410. }
  411. share->db_record_offset= 1;
  412. if (db_create_options & HA_OPTION_LONG_BLOB_PTR)
  413. share->blob_ptr_size= portable_sizeof_char_ptr;
  414. /* Set temporarily a good value for db_low_byte_first */
  415. share->db_low_byte_first= test(legacy_db_type != DB_TYPE_ISAM);
  416. error=4;
  417. share->max_rows= uint4korr(head+18);
  418. share->min_rows= uint4korr(head+22);
  419. /* Read keyinformation */
  420. key_info_length= (uint) uint2korr(head+28);
  421. VOID(my_seek(file,(ulong) uint2korr(head+6),MY_SEEK_SET,MYF(0)));
  422. if (read_string(file,(gptr*) &disk_buff,key_info_length))
  423. goto err; /* purecov: inspected */
  424. if (disk_buff[0] & 0x80)
  425. {
  426. share->keys= keys= (disk_buff[1] << 7) | (disk_buff[0] & 0x7f);
  427. share->key_parts= key_parts= uint2korr(disk_buff+2);
  428. }
  429. else
  430. {
  431. share->keys= keys= disk_buff[0];
  432. share->key_parts= key_parts= disk_buff[1];
  433. }
  434. share->keys_for_keyread.init(0);
  435. share->keys_in_use.init(keys);
  436. n_length=keys*sizeof(KEY)+key_parts*sizeof(KEY_PART_INFO);
  437. if (!(keyinfo = (KEY*) alloc_root(&share->mem_root,
  438. n_length + uint2korr(disk_buff+4))))
  439. goto err; /* purecov: inspected */
  440. bzero((char*) keyinfo,n_length);
  441. share->key_info= keyinfo;
  442. key_part= my_reinterpret_cast(KEY_PART_INFO*) (keyinfo+keys);
  443. strpos=disk_buff+6;
  444. if (!(rec_per_key= (ulong*) alloc_root(&share->mem_root,
  445. sizeof(ulong*)*key_parts)))
  446. goto err;
  447. for (i=0 ; i < keys ; i++, keyinfo++)
  448. {
  449. keyinfo->table= 0; // Updated in open_frm
  450. if (new_frm_ver >= 3)
  451. {
  452. keyinfo->flags= (uint) uint2korr(strpos) ^ HA_NOSAME;
  453. keyinfo->key_length= (uint) uint2korr(strpos+2);
  454. keyinfo->key_parts= (uint) strpos[4];
  455. keyinfo->algorithm= (enum ha_key_alg) strpos[5];
  456. keyinfo->block_size= uint2korr(strpos+6);
  457. strpos+=8;
  458. }
  459. else
  460. {
  461. keyinfo->flags= ((uint) strpos[0]) ^ HA_NOSAME;
  462. keyinfo->key_length= (uint) uint2korr(strpos+1);
  463. keyinfo->key_parts= (uint) strpos[3];
  464. keyinfo->algorithm= HA_KEY_ALG_UNDEF;
  465. strpos+=4;
  466. }
  467. keyinfo->key_part= key_part;
  468. keyinfo->rec_per_key= rec_per_key;
  469. for (j=keyinfo->key_parts ; j-- ; key_part++)
  470. {
  471. *rec_per_key++=0;
  472. key_part->fieldnr= (uint16) (uint2korr(strpos) & FIELD_NR_MASK);
  473. key_part->offset= (uint) uint2korr(strpos+2)-1;
  474. key_part->key_type= (uint) uint2korr(strpos+5);
  475. // key_part->field= (Field*) 0; // Will be fixed later
  476. if (new_frm_ver >= 1)
  477. {
  478. key_part->key_part_flag= *(strpos+4);
  479. key_part->length= (uint) uint2korr(strpos+7);
  480. strpos+=9;
  481. }
  482. else
  483. {
  484. key_part->length= *(strpos+4);
  485. key_part->key_part_flag=0;
  486. if (key_part->length > 128)
  487. {
  488. key_part->length&=127; /* purecov: inspected */
  489. key_part->key_part_flag=HA_REVERSE_SORT; /* purecov: inspected */
  490. }
  491. strpos+=7;
  492. }
  493. key_part->store_length=key_part->length;
  494. }
  495. }
  496. keynames=(char*) key_part;
  497. strpos+= (strmov(keynames, (char *) strpos) - keynames)+1;
  498. share->reclength = uint2korr((head+16));
  499. if (*(head+26) == 1)
  500. share->system= 1; /* one-record-database */
  501. #ifdef HAVE_CRYPTED_FRM
  502. else if (*(head+26) == 2)
  503. {
  504. crypted= get_crypt_for_frm();
  505. share->crypted= 1;
  506. }
  507. #endif
  508. record_offset= (ulong) (uint2korr(head+6)+
  509. ((uint2korr(head+14) == 0xffff ?
  510. uint4korr(head+47) : uint2korr(head+14))));
  511. if ((n_length= uint4korr(head+55)))
  512. {
  513. /* Read extra data segment */
  514. char *buff, *next_chunk, *buff_end;
  515. DBUG_PRINT("info", ("extra segment size is %u bytes", n_length));
  516. if (!(next_chunk= buff= my_malloc(n_length, MYF(MY_WME))))
  517. goto err;
  518. if (my_pread(file, (byte*)buff, n_length, record_offset + share->reclength,
  519. MYF(MY_NABP)))
  520. {
  521. my_free(buff, MYF(0));
  522. goto err;
  523. }
  524. share->connect_string.length= uint2korr(buff);
  525. if (! (share->connect_string.str= strmake_root(&share->mem_root,
  526. next_chunk + 2, share->connect_string.length)))
  527. {
  528. my_free(buff, MYF(0));
  529. goto err;
  530. }
  531. next_chunk+= share->connect_string.length + 2;
  532. buff_end= buff + n_length;
  533. if (next_chunk + 2 < buff_end)
  534. {
  535. uint str_db_type_length= uint2korr(next_chunk);
  536. LEX_STRING name= { next_chunk + 2, str_db_type_length };
  537. handlerton *tmp_db_type= ha_resolve_by_name(thd, &name);
  538. if (tmp_db_type != NULL)
  539. {
  540. share->db_type= tmp_db_type;
  541. DBUG_PRINT("info", ("setting dbtype to '%.*s' (%d)",
  542. str_db_type_length, next_chunk + 2,
  543. ha_legacy_type(share->db_type)));
  544. }
  545. #ifdef WITH_PARTITION_STORAGE_ENGINE
  546. else
  547. {
  548. if (!strncmp(next_chunk + 2, "partition", str_db_type_length))
  549. {
  550. /* Use partition handler */
  551. share->db_type= &partition_hton;
  552. DBUG_PRINT("info", ("setting dbtype to '%.*s' (%d)",
  553. str_db_type_length, next_chunk + 2,
  554. ha_legacy_type(share->db_type)));
  555. }
  556. }
  557. #endif
  558. next_chunk+= str_db_type_length + 2;
  559. }
  560. if (next_chunk + 5 < buff_end)
  561. {
  562. uint32 partition_info_len = uint4korr(next_chunk);
  563. #ifdef WITH_PARTITION_STORAGE_ENGINE
  564. if ((share->partition_info_len= partition_info_len))
  565. {
  566. if (!(share->partition_info=
  567. (uchar*) memdup_root(&share->mem_root, next_chunk + 4,
  568. partition_info_len + 1)))
  569. {
  570. my_free(buff, MYF(0));
  571. goto err;
  572. }
  573. }
  574. #else
  575. if (partition_info_len)
  576. {
  577. DBUG_PRINT("info", ("WITH_PARTITION_STORAGE_ENGINE is not defined"));
  578. my_free(buff, MYF(0));
  579. goto err;
  580. }
  581. #endif
  582. next_chunk+= 5 + partition_info_len;
  583. }
  584. #if MYSQL_VERSION_ID < 50200
  585. if (share->mysql_version >= 50106 && share->mysql_version <= 50109)
  586. {
  587. /*
  588. Partition state array was here in version 5.1.6 to 5.1.9, this code
  589. makes it possible to load a 5.1.6 table in later versions. Can most
  590. likely be removed at some point in time. Will only be used for
  591. upgrades within 5.1 series of versions. Upgrade to 5.2 can only be
  592. done from newer 5.1 versions.
  593. */
  594. next_chunk+= 4;
  595. }
  596. else if (share->mysql_version >= 50110)
  597. #endif
  598. {
  599. /* New auto_partitioned indicator introduced in 5.1.11 */
  600. #ifdef WITH_PARTITION_STORAGE_ENGINE
  601. share->auto_partitioned= *next_chunk;
  602. #endif
  603. next_chunk++;
  604. }
  605. keyinfo= share->key_info;
  606. for (i= 0; i < keys; i++, keyinfo++)
  607. {
  608. if (keyinfo->flags & HA_USES_PARSER)
  609. {
  610. LEX_STRING parser_name;
  611. if (next_chunk >= buff_end)
  612. {
  613. DBUG_PRINT("error",
  614. ("fulltext key uses parser that is not defined in .frm"));
  615. my_free(buff, MYF(0));
  616. goto err;
  617. }
  618. parser_name.str= next_chunk;
  619. parser_name.length= strlen(next_chunk);
  620. keyinfo->parser= plugin_lock(&parser_name, MYSQL_FTPARSER_PLUGIN);
  621. if (! keyinfo->parser)
  622. {
  623. my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), parser_name.str);
  624. my_free(buff, MYF(0));
  625. goto err;
  626. }
  627. }
  628. }
  629. my_free(buff, MYF(0));
  630. }
  631. share->key_block_size= uint2korr(head+62);
  632. error=4;
  633. extra_rec_buf_length= uint2korr(head+59);
  634. rec_buff_length= ALIGN_SIZE(share->reclength + 1 + extra_rec_buf_length);
  635. share->rec_buff_length= rec_buff_length;
  636. if (!(record= (char *) alloc_root(&share->mem_root,
  637. rec_buff_length)))
  638. goto err; /* purecov: inspected */
  639. share->default_values= (byte *) record;
  640. if (my_pread(file,(byte*) record, (uint) share->reclength,
  641. record_offset, MYF(MY_NABP)))
  642. goto err; /* purecov: inspected */
  643. VOID(my_seek(file,pos,MY_SEEK_SET,MYF(0)));
  644. if (my_read(file,(byte*) head,288,MYF(MY_NABP)))
  645. goto err;
  646. #ifdef HAVE_CRYPTED_FRM
  647. if (crypted)
  648. {
  649. crypted->decode((char*) head+256,288-256);
  650. if (sint2korr(head+284) != 0) // Should be 0
  651. goto err; // Wrong password
  652. }
  653. #endif
  654. share->fields= uint2korr(head+258);
  655. pos= uint2korr(head+260); /* Length of all screens */
  656. n_length= uint2korr(head+268);
  657. interval_count= uint2korr(head+270);
  658. interval_parts= uint2korr(head+272);
  659. int_length= uint2korr(head+274);
  660. share->null_fields= uint2korr(head+282);
  661. com_length= uint2korr(head+284);
  662. share->comment= strdup_root(&share->mem_root, (char*) head+47);
  663. DBUG_PRINT("info",("i_count: %d i_parts: %d index: %d n_length: %d int_length: %d com_length: %d", interval_count,interval_parts, share->keys,n_length,int_length, com_length));
  664. if (!(field_ptr = (Field **)
  665. alloc_root(&share->mem_root,
  666. (uint) ((share->fields+1)*sizeof(Field*)+
  667. interval_count*sizeof(TYPELIB)+
  668. (share->fields+interval_parts+
  669. keys+3)*sizeof(my_string)+
  670. (n_length+int_length+com_length)))))
  671. goto err; /* purecov: inspected */
  672. share->field= field_ptr;
  673. read_length=(uint) (share->fields * field_pack_length +
  674. pos+ (uint) (n_length+int_length+com_length));
  675. if (read_string(file,(gptr*) &disk_buff,read_length))
  676. goto err; /* purecov: inspected */
  677. #ifdef HAVE_CRYPTED_FRM
  678. if (crypted)
  679. {
  680. crypted->decode((char*) disk_buff,read_length);
  681. delete crypted;
  682. crypted=0;
  683. }
  684. #endif
  685. strpos= disk_buff+pos;
  686. share->intervals= (TYPELIB*) (field_ptr+share->fields+1);
  687. interval_array= (const char **) (share->intervals+interval_count);
  688. names= (char*) (interval_array+share->fields+interval_parts+keys+3);
  689. if (!interval_count)
  690. share->intervals= 0; // For better debugging
  691. memcpy((char*) names, strpos+(share->fields*field_pack_length),
  692. (uint) (n_length+int_length));
  693. comment_pos= names+(n_length+int_length);
  694. memcpy(comment_pos, disk_buff+read_length-com_length, com_length);
  695. fix_type_pointers(&interval_array, &share->fieldnames, 1, &names);
  696. fix_type_pointers(&interval_array, share->intervals, interval_count,
  697. &names);
  698. {
  699. /* Set ENUM and SET lengths */
  700. TYPELIB *interval;
  701. for (interval= share->intervals;
  702. interval < share->intervals + interval_count;
  703. interval++)
  704. {
  705. uint count= (uint) (interval->count + 1) * sizeof(uint);
  706. if (!(interval->type_lengths= (uint *) alloc_root(&share->mem_root,
  707. count)))
  708. goto err;
  709. for (count= 0; count < interval->count; count++)
  710. interval->type_lengths[count]= strlen(interval->type_names[count]);
  711. interval->type_lengths[count]= 0;
  712. }
  713. }
  714. if (keynames)
  715. fix_type_pointers(&interval_array, &share->keynames, 1, &keynames);
  716. /* Allocate handler */
  717. if (!(handler_file= get_new_handler(share, thd->mem_root,
  718. share->db_type)))
  719. goto err;
  720. record= (char*) share->default_values-1; /* Fieldstart = 1 */
  721. if (share->null_field_first)
  722. {
  723. null_flags= null_pos= (uchar*) record+1;
  724. null_bit_pos= (db_create_options & HA_OPTION_PACK_RECORD) ? 0 : 1;
  725. /*
  726. null_bytes below is only correct under the condition that
  727. there are no bit fields. Correct values is set below after the
  728. table struct is initialized
  729. */
  730. share->null_bytes= (share->null_fields + null_bit_pos + 7) / 8;
  731. }
  732. #ifndef WE_WANT_TO_SUPPORT_VERY_OLD_FRM_FILES
  733. else
  734. {
  735. share->null_bytes= (share->null_fields+7)/8;
  736. null_flags= null_pos= (uchar*) (record + 1 +share->reclength -
  737. share->null_bytes);
  738. null_bit_pos= 0;
  739. }
  740. #endif
  741. use_hash= share->fields >= MAX_FIELDS_BEFORE_HASH;
  742. if (use_hash)
  743. use_hash= !hash_init(&share->name_hash,
  744. system_charset_info,
  745. share->fields,0,0,
  746. (hash_get_key) get_field_name,0,0);
  747. for (i=0 ; i < share->fields; i++, strpos+=field_pack_length, field_ptr++)
  748. {
  749. uint pack_flag, interval_nr, unireg_type, recpos, field_length;
  750. enum_field_types field_type;
  751. CHARSET_INFO *charset=NULL;
  752. Field::geometry_type geom_type= Field::GEOM_GEOMETRY;
  753. LEX_STRING comment;
  754. if (new_frm_ver >= 3)
  755. {
  756. /* new frm file in 4.1 */
  757. field_length= uint2korr(strpos+3);
  758. recpos= uint3korr(strpos+5);
  759. pack_flag= uint2korr(strpos+8);
  760. unireg_type= (uint) strpos[10];
  761. interval_nr= (uint) strpos[12];
  762. uint comment_length=uint2korr(strpos+15);
  763. field_type=(enum_field_types) (uint) strpos[13];
  764. /* charset and geometry_type share the same byte in frm */
  765. if (field_type == FIELD_TYPE_GEOMETRY)
  766. {
  767. #ifdef HAVE_SPATIAL
  768. geom_type= (Field::geometry_type) strpos[14];
  769. charset= &my_charset_bin;
  770. #else
  771. error= 4; // unsupported field type
  772. goto err;
  773. #endif
  774. }
  775. else
  776. {
  777. if (!strpos[14])
  778. charset= &my_charset_bin;
  779. else if (!(charset=get_charset((uint) strpos[14], MYF(0))))
  780. {
  781. error= 5; // Unknown or unavailable charset
  782. errarg= (int) strpos[14];
  783. goto err;
  784. }
  785. }
  786. if (!comment_length)
  787. {
  788. comment.str= (char*) "";
  789. comment.length=0;
  790. }
  791. else
  792. {
  793. comment.str= (char*) comment_pos;
  794. comment.length= comment_length;
  795. comment_pos+= comment_length;
  796. }
  797. }
  798. else
  799. {
  800. field_length= (uint) strpos[3];
  801. recpos= uint2korr(strpos+4),
  802. pack_flag= uint2korr(strpos+6);
  803. pack_flag&= ~FIELDFLAG_NO_DEFAULT; // Safety for old files
  804. unireg_type= (uint) strpos[8];
  805. interval_nr= (uint) strpos[10];
  806. /* old frm file */
  807. field_type= (enum_field_types) f_packtype(pack_flag);
  808. if (f_is_binary(pack_flag))
  809. {
  810. /*
  811. Try to choose the best 4.1 type:
  812. - for 4.0 "CHAR(N) BINARY" or "VARCHAR(N) BINARY"
  813. try to find a binary collation for character set.
  814. - for other types (e.g. BLOB) just use my_charset_bin.
  815. */
  816. if (!f_is_blob(pack_flag))
  817. {
  818. // 3.23 or 4.0 string
  819. if (!(charset= get_charset_by_csname(share->table_charset->csname,
  820. MY_CS_BINSORT, MYF(0))))
  821. charset= &my_charset_bin;
  822. }
  823. else
  824. charset= &my_charset_bin;
  825. }
  826. else
  827. charset= share->table_charset;
  828. bzero((char*) &comment, sizeof(comment));
  829. }
  830. if (interval_nr && charset->mbminlen > 1)
  831. {
  832. /* Unescape UCS2 intervals from HEX notation */
  833. TYPELIB *interval= share->intervals + interval_nr - 1;
  834. unhex_type2(interval);
  835. }
  836. #ifndef TO_BE_DELETED_ON_PRODUCTION
  837. if (field_type == FIELD_TYPE_NEWDECIMAL && !share->mysql_version)
  838. {
  839. /*
  840. Fix pack length of old decimal values from 5.0.3 -> 5.0.4
  841. The difference is that in the old version we stored precision
  842. in the .frm table while we now store the display_length
  843. */
  844. uint decimals= f_decimals(pack_flag);
  845. field_length= my_decimal_precision_to_length(field_length,
  846. decimals,
  847. f_is_dec(pack_flag) == 0);
  848. sql_print_error("Found incompatible DECIMAL field '%s' in %s; "
  849. "Please do \"ALTER TABLE '%s' FORCE\" to fix it!",
  850. share->fieldnames.type_names[i], share->table_name.str,
  851. share->table_name.str);
  852. push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
  853. ER_CRASHED_ON_USAGE,
  854. "Found incompatible DECIMAL field '%s' in %s; "
  855. "Please do \"ALTER TABLE '%s' FORCE\" to fix it!",
  856. share->fieldnames.type_names[i],
  857. share->table_name.str,
  858. share->table_name.str);
  859. share->crashed= 1; // Marker for CHECK TABLE
  860. }
  861. #endif
  862. *field_ptr= reg_field=
  863. make_field(share, record+recpos,
  864. (uint32) field_length,
  865. null_pos, null_bit_pos,
  866. pack_flag,
  867. field_type,
  868. charset,
  869. geom_type,
  870. (Field::utype) MTYP_TYPENR(unireg_type),
  871. (interval_nr ?
  872. share->intervals+interval_nr-1 :
  873. (TYPELIB*) 0),
  874. share->fieldnames.type_names[i]);
  875. if (!reg_field) // Not supported field type
  876. {
  877. error= 4;
  878. goto err; /* purecov: inspected */
  879. }
  880. reg_field->field_index= i;
  881. reg_field->comment=comment;
  882. if (field_type == FIELD_TYPE_BIT && !f_bit_as_char(pack_flag))
  883. {
  884. if ((null_bit_pos+= field_length & 7) > 7)
  885. {
  886. null_pos++;
  887. null_bit_pos-= 8;
  888. }
  889. }
  890. if (!(reg_field->flags & NOT_NULL_FLAG))
  891. {
  892. if (!(null_bit_pos= (null_bit_pos + 1) & 7))
  893. null_pos++;
  894. }
  895. if (f_no_default(pack_flag))
  896. reg_field->flags|= NO_DEFAULT_VALUE_FLAG;
  897. if (reg_field->unireg_check == Field::NEXT_NUMBER)
  898. share->found_next_number_field= field_ptr;
  899. if (share->timestamp_field == reg_field)
  900. share->timestamp_field_offset= i;
  901. if (use_hash)
  902. (void) my_hash_insert(&share->name_hash,
  903. (byte*) field_ptr); // never fail
  904. }
  905. *field_ptr=0; // End marker
  906. /* Fix key->name and key_part->field */
  907. if (key_parts)
  908. {
  909. uint primary_key=(uint) (find_type((char*) primary_key_name,
  910. &share->keynames, 3) - 1);
  911. uint ha_option= handler_file->ha_table_flags();
  912. keyinfo= share->key_info;
  913. key_part= keyinfo->key_part;
  914. for (uint key=0 ; key < share->keys ; key++,keyinfo++)
  915. {
  916. uint usable_parts= 0;
  917. keyinfo->name=(char*) share->keynames.type_names[key];
  918. /* Fix fulltext keys for old .frm files */
  919. if (share->key_info[key].flags & HA_FULLTEXT)
  920. share->key_info[key].algorithm= HA_KEY_ALG_FULLTEXT;
  921. if (primary_key >= MAX_KEY && (keyinfo->flags & HA_NOSAME))
  922. {
  923. /*
  924. If the UNIQUE key doesn't have NULL columns and is not a part key
  925. declare this as a primary key.
  926. */
  927. primary_key=key;
  928. for (i=0 ; i < keyinfo->key_parts ;i++)
  929. {
  930. uint fieldnr= key_part[i].fieldnr;
  931. if (!fieldnr ||
  932. share->field[fieldnr-1]->null_ptr ||
  933. share->field[fieldnr-1]->key_length() !=
  934. key_part[i].length)
  935. {
  936. primary_key=MAX_KEY; // Can't be used
  937. break;
  938. }
  939. }
  940. }
  941. for (i=0 ; i < keyinfo->key_parts ; key_part++,i++)
  942. {
  943. Field *field;
  944. if (new_field_pack_flag <= 1)
  945. key_part->fieldnr= (uint16) find_field(share->field,
  946. (uint) key_part->offset,
  947. (uint) key_part->length);
  948. if (!key_part->fieldnr)
  949. {
  950. error= 4; // Wrong file
  951. goto err;
  952. }
  953. field= key_part->field= share->field[key_part->fieldnr-1];
  954. if (field->null_ptr)
  955. {
  956. key_part->null_offset=(uint) ((byte*) field->null_ptr -
  957. share->default_values);
  958. key_part->null_bit= field->null_bit;
  959. key_part->store_length+=HA_KEY_NULL_LENGTH;
  960. keyinfo->flags|=HA_NULL_PART_KEY;
  961. keyinfo->extra_length+= HA_KEY_NULL_LENGTH;
  962. keyinfo->key_length+= HA_KEY_NULL_LENGTH;
  963. }
  964. if (field->type() == FIELD_TYPE_BLOB ||
  965. field->real_type() == MYSQL_TYPE_VARCHAR)
  966. {
  967. if (field->type() == FIELD_TYPE_BLOB)
  968. key_part->key_part_flag|= HA_BLOB_PART;
  969. else
  970. key_part->key_part_flag|= HA_VAR_LENGTH_PART;
  971. keyinfo->extra_length+=HA_KEY_BLOB_LENGTH;
  972. key_part->store_length+=HA_KEY_BLOB_LENGTH;
  973. keyinfo->key_length+= HA_KEY_BLOB_LENGTH;
  974. /*
  975. Mark that there may be many matching values for one key
  976. combination ('a', 'a ', 'a '...)
  977. */
  978. if (!(field->flags & BINARY_FLAG))
  979. keyinfo->flags|= HA_END_SPACE_KEY;
  980. }
  981. if (field->type() == MYSQL_TYPE_BIT)
  982. key_part->key_part_flag|= HA_BIT_PART;
  983. if (i == 0 && key != primary_key)
  984. field->flags |= (((keyinfo->flags & HA_NOSAME) &&
  985. (keyinfo->key_parts == 1)) ?
  986. UNIQUE_KEY_FLAG : MULTIPLE_KEY_FLAG);
  987. if (i == 0)
  988. field->key_start.set_bit(key);
  989. if (field->key_length() == key_part->length &&
  990. !(field->flags & BLOB_FLAG))
  991. {
  992. if (handler_file->index_flags(key, i, 0) & HA_KEYREAD_ONLY)
  993. {
  994. share->keys_for_keyread.set_bit(key);
  995. field->part_of_key.set_bit(key);
  996. field->part_of_key_not_clustered.set_bit(key);
  997. }
  998. if (handler_file->index_flags(key, i, 1) & HA_READ_ORDER)
  999. field->part_of_sortkey.set_bit(key);
  1000. }
  1001. if (!(key_part->key_part_flag & HA_REVERSE_SORT) &&
  1002. usable_parts == i)
  1003. usable_parts++; // For FILESORT
  1004. field->flags|= PART_KEY_FLAG;
  1005. if (key == primary_key)
  1006. {
  1007. field->flags|= PRI_KEY_FLAG;
  1008. /*
  1009. If this field is part of the primary key and all keys contains
  1010. the primary key, then we can use any key to find this column
  1011. */
  1012. if (ha_option & HA_PRIMARY_KEY_IN_READ_INDEX)
  1013. field->part_of_key= share->keys_in_use;
  1014. }
  1015. if (field->key_length() != key_part->length)
  1016. {
  1017. #ifndef TO_BE_DELETED_ON_PRODUCTION
  1018. if (field->type() == FIELD_TYPE_NEWDECIMAL)
  1019. {
  1020. /*
  1021. Fix a fatal error in decimal key handling that causes crashes
  1022. on Innodb. We fix it by reducing the key length so that
  1023. InnoDB never gets a too big key when searching.
  1024. This allows the end user to do an ALTER TABLE to fix the
  1025. error.
  1026. */
  1027. keyinfo->key_length-= (key_part->length - field->key_length());
  1028. key_part->store_length-= (uint16)(key_part->length -
  1029. field->key_length());
  1030. key_part->length= (uint16)field->key_length();
  1031. sql_print_error("Found wrong key definition in %s; "
  1032. "Please do \"ALTER TABLE '%s' FORCE \" to fix it!",
  1033. share->table_name.str,
  1034. share->table_name.str);
  1035. push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
  1036. ER_CRASHED_ON_USAGE,
  1037. "Found wrong key definition in %s; "
  1038. "Please do \"ALTER TABLE '%s' FORCE\" to fix "
  1039. "it!",
  1040. share->table_name.str,
  1041. share->table_name.str);
  1042. share->crashed= 1; // Marker for CHECK TABLE
  1043. goto to_be_deleted;
  1044. }
  1045. #endif
  1046. key_part->key_part_flag|= HA_PART_KEY_SEG;
  1047. }
  1048. to_be_deleted:
  1049. /*
  1050. If the field can be NULL, don't optimize away the test
  1051. key_part_column = expression from the WHERE clause
  1052. as we need to test for NULL = NULL.
  1053. */
  1054. if (field->real_maybe_null())
  1055. key_part->key_part_flag|= HA_PART_KEY_SEG;
  1056. }
  1057. keyinfo->usable_key_parts= usable_parts; // Filesort
  1058. set_if_bigger(share->max_key_length,keyinfo->key_length+
  1059. keyinfo->key_parts);
  1060. share->total_key_length+= keyinfo->key_length;
  1061. /*
  1062. MERGE tables do not have unique indexes. But every key could be
  1063. an unique index on the underlying MyISAM table. (Bug #10400)
  1064. */
  1065. if ((keyinfo->flags & HA_NOSAME) ||
  1066. (ha_option & HA_ANY_INDEX_MAY_BE_UNIQUE))
  1067. set_if_bigger(share->max_unique_length,keyinfo->key_length);
  1068. }
  1069. if (primary_key < MAX_KEY &&
  1070. (share->keys_in_use.is_set(primary_key)))
  1071. {
  1072. share->primary_key= primary_key;
  1073. /*
  1074. If we are using an integer as the primary key then allow the user to
  1075. refer to it as '_rowid'
  1076. */
  1077. if (share->key_info[primary_key].key_parts == 1)
  1078. {
  1079. Field *field= share->key_info[primary_key].key_part[0].field;
  1080. if (field && field->result_type() == INT_RESULT)
  1081. {
  1082. /* note that fieldnr here (and rowid_field_offset) starts from 1 */
  1083. share->rowid_field_offset= (share->key_info[primary_key].key_part[0].
  1084. fieldnr);
  1085. }
  1086. }
  1087. }
  1088. else
  1089. share->primary_key = MAX_KEY; // we do not have a primary key
  1090. }
  1091. else
  1092. share->primary_key= MAX_KEY;
  1093. x_free((gptr) disk_buff);
  1094. disk_buff=0;
  1095. if (new_field_pack_flag <= 1)
  1096. {
  1097. /* Old file format with default as not null */
  1098. uint null_length= (share->null_fields+7)/8;
  1099. bfill(share->default_values + (null_flags - (uchar*) record),
  1100. null_length, 255);
  1101. }
  1102. if (share->found_next_number_field)
  1103. {
  1104. /*
  1105. We must have a table object for find_ref_key to calculate field offset
  1106. */
  1107. TABLE tmp_table;
  1108. tmp_table.record[0]= share->default_values;
  1109. reg_field= *share->found_next_number_field;
  1110. reg_field->table= &tmp_table;
  1111. if ((int) (share->next_number_index= (uint)
  1112. find_ref_key(share->key_info, share->keys, reg_field,
  1113. &share->next_number_key_offset)) < 0)
  1114. {
  1115. reg_field->unireg_check= Field::NONE; /* purecov: inspected */
  1116. share->found_next_number_field= 0;
  1117. }
  1118. else
  1119. reg_field->flags |= AUTO_INCREMENT_FLAG;
  1120. reg_field->table= 0;
  1121. }
  1122. if (share->blob_fields)
  1123. {
  1124. Field **ptr;
  1125. uint i, *save;
  1126. /* Store offsets to blob fields to find them fast */
  1127. if (!(share->blob_field= save=
  1128. (uint*) alloc_root(&share->mem_root,
  1129. (uint) (share->blob_fields* sizeof(uint)))))
  1130. goto err;
  1131. for (i=0, ptr= share->field ; *ptr ; ptr++, i++)
  1132. {
  1133. if ((*ptr)->flags & BLOB_FLAG)
  1134. (*save++)= i;
  1135. }
  1136. }
  1137. /*
  1138. the correct null_bytes can now be set, since bitfields have been taken
  1139. into account
  1140. */
  1141. share->null_bytes= (null_pos - (uchar*) null_flags +
  1142. (null_bit_pos + 7) / 8);
  1143. share->last_null_bit_pos= null_bit_pos;
  1144. share->db_low_byte_first= handler_file->low_byte_first();
  1145. share->column_bitmap_size= bitmap_buffer_size(share->fields);
  1146. if (!(bitmaps= (my_bitmap_map*) alloc_root(&share->mem_root,
  1147. share->column_bitmap_size)))
  1148. goto err;
  1149. bitmap_init(&share->all_set, bitmaps, share->fields, FALSE);
  1150. bitmap_set_all(&share->all_set);
  1151. delete handler_file;
  1152. #ifndef DBUG_OFF
  1153. if (use_hash)
  1154. (void) hash_check(&share->name_hash);
  1155. #endif
  1156. DBUG_RETURN (0);
  1157. err:
  1158. share->error= error;
  1159. share->open_errno= my_errno;
  1160. share->errarg= errarg;
  1161. x_free((gptr) disk_buff);
  1162. delete crypted;
  1163. delete handler_file;
  1164. hash_free(&share->name_hash);
  1165. open_table_error(share, error, share->open_errno, errarg);
  1166. DBUG_RETURN(error);
  1167. } /* open_binary_frm */
  1168. /*
  1169. Open a table based on a TABLE_SHARE
  1170. SYNOPSIS
  1171. open_table_from_share()
  1172. thd Thread handler
  1173. share Table definition
  1174. alias Alias for table
  1175. db_stat open flags (for example HA_OPEN_KEYFILE|
  1176. HA_OPEN_RNDFILE..) can be 0 (example in
  1177. ha_example_table)
  1178. prgflag READ_ALL etc..
  1179. ha_open_flags HA_OPEN_ABORT_IF_LOCKED etc..
  1180. outparam result table
  1181. RETURN VALUES
  1182. 0 ok
  1183. 1 Error (see open_table_error)
  1184. 2 Error (see open_table_error)
  1185. 3 Wrong data in .frm file
  1186. 4 Error (see open_table_error)
  1187. 5 Error (see open_table_error: charset unavailable)
  1188. 7 Table definition has changed in engine
  1189. */
  1190. int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias,
  1191. uint db_stat, uint prgflag, uint ha_open_flags,
  1192. TABLE *outparam, bool is_create_table)
  1193. {
  1194. int error;
  1195. uint records, i, bitmap_size;
  1196. bool error_reported= FALSE;
  1197. byte *record, *bitmaps;
  1198. Field **field_ptr;
  1199. DBUG_ENTER("open_table_from_share");
  1200. DBUG_PRINT("enter",("name: '%s.%s' form: 0x%lx", share->db.str,
  1201. share->table_name.str, outparam));
  1202. error= 1;
  1203. bzero((char*) outparam, sizeof(*outparam));
  1204. outparam->in_use= thd;
  1205. outparam->s= share;
  1206. outparam->db_stat= db_stat;
  1207. outparam->write_row_record= NULL;
  1208. init_sql_alloc(&outparam->mem_root, TABLE_ALLOC_BLOCK_SIZE, 0);
  1209. if (!(outparam->alias= my_strdup(alias, MYF(MY_WME))))
  1210. goto err;
  1211. outparam->quick_keys.init();
  1212. outparam->used_keys.init();
  1213. outparam->keys_in_use_for_query.init();
  1214. /* Allocate handler */
  1215. if (!(outparam->file= get_new_handler(share, &outparam->mem_root,
  1216. share->db_type)))
  1217. goto err;
  1218. error= 4;
  1219. outparam->reginfo.lock_type= TL_UNLOCK;
  1220. outparam->current_lock= F_UNLCK;
  1221. records=0;
  1222. if ((db_stat & HA_OPEN_KEYFILE) || (prgflag & DELAYED_OPEN))
  1223. records=1;
  1224. if (prgflag & (READ_ALL+EXTRA_RECORD))
  1225. records++;
  1226. if (!(record= (byte*) alloc_root(&outparam->mem_root,
  1227. share->rec_buff_length * records)))
  1228. goto err; /* purecov: inspected */
  1229. if (records == 0)
  1230. {
  1231. /* We are probably in hard repair, and the buffers should not be used */
  1232. outparam->record[0]= outparam->record[1]= share->default_values;
  1233. }
  1234. else
  1235. {
  1236. outparam->record[0]= record;
  1237. if (records > 1)
  1238. outparam->record[1]= record+ share->rec_buff_length;
  1239. else
  1240. outparam->record[1]= outparam->record[0]; // Safety
  1241. }
  1242. #ifdef HAVE_purify
  1243. /*
  1244. We need this because when we read var-length rows, we are not updating
  1245. bytes after end of varchar
  1246. */
  1247. if (records > 1)
  1248. {
  1249. memcpy(outparam->record[0], share->default_values, share->rec_buff_length);
  1250. if (records > 2)
  1251. memcpy(outparam->record[1], share->default_values,
  1252. share->rec_buff_length);
  1253. }
  1254. #endif
  1255. if (!(field_ptr = (Field **) alloc_root(&outparam->mem_root,
  1256. (uint) ((share->fields+1)*
  1257. sizeof(Field*)))))
  1258. goto err; /* purecov: inspected */
  1259. outparam->field= field_ptr;
  1260. record= (byte*) outparam->record[0]-1; /* Fieldstart = 1 */
  1261. if (share->null_field_first)
  1262. outparam->null_flags= (uchar*) record+1;
  1263. else
  1264. outparam->null_flags= (uchar*) (record+ 1+ share->reclength -
  1265. share->null_bytes);
  1266. /* Setup copy of fields from share, but use the right alias and record */
  1267. for (i=0 ; i < share->fields; i++, field_ptr++)
  1268. {
  1269. if (!((*field_ptr)= share->field[i]->clone(&outparam->mem_root, outparam)))
  1270. goto err;
  1271. }
  1272. (*field_ptr)= 0; // End marker
  1273. if (share->found_next_number_field)
  1274. outparam->found_next_number_field=
  1275. outparam->field[(uint) (share->found_next_number_field - share->field)];
  1276. if (share->timestamp_field)
  1277. outparam->timestamp_field= (Field_timestamp*) outparam->field[share->timestamp_field_offset];
  1278. /* Fix key->name and key_part->field */
  1279. if (share->key_parts)
  1280. {
  1281. KEY *key_info, *key_info_end;
  1282. KEY_PART_INFO *key_part;
  1283. uint n_length;
  1284. n_length= share->keys*sizeof(KEY) + share->key_parts*sizeof(KEY_PART_INFO);
  1285. if (!(key_info= (KEY*) alloc_root(&outparam->mem_root, n_length)))
  1286. goto err;
  1287. outparam->key_info= key_info;
  1288. key_part= (my_reinterpret_cast(KEY_PART_INFO*) (key_info+share->keys));
  1289. memcpy(key_info, share->key_info, sizeof(*key_info)*share->keys);
  1290. memcpy(key_part, share->key_info[0].key_part, (sizeof(*key_part) *
  1291. share->key_parts));
  1292. for (key_info_end= key_info + share->keys ;
  1293. key_info < key_info_end ;
  1294. key_info++)
  1295. {
  1296. KEY_PART_INFO *key_part_end;
  1297. key_info->table= outparam;
  1298. key_info->key_part= key_part;
  1299. for (key_part_end= key_part+ key_info->key_parts ;
  1300. key_part < key_part_end ;
  1301. key_part++)
  1302. {
  1303. Field *field= key_part->field= outparam->field[key_part->fieldnr-1];
  1304. if (field->key_length() != key_part->length &&
  1305. !(field->flags & BLOB_FLAG))
  1306. {
  1307. /*
  1308. We are using only a prefix of the column as a key:
  1309. Create a new field for the key part that matches the index
  1310. */
  1311. field= key_part->field=field->new_field(&outparam->mem_root,
  1312. outparam);
  1313. field->field_length= key_part->length;
  1314. }
  1315. }
  1316. }
  1317. }
  1318. #ifdef WITH_PARTITION_STORAGE_ENGINE
  1319. if (share->partition_info_len)
  1320. {
  1321. MEM_ROOT **root_ptr, *old_root;
  1322. bool tmp;
  1323. root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**, THR_MALLOC);
  1324. old_root= *root_ptr;
  1325. *root_ptr= &outparam->mem_root;
  1326. tmp= mysql_unpack_partition(thd, share->partition_info,
  1327. share->partition_info_len,
  1328. (uchar*)share->part_state,
  1329. share->part_state_len,
  1330. outparam, is_create_table,
  1331. share->default_part_db_type);
  1332. outparam->part_info->is_auto_partitioned= share->auto_partitioned;
  1333. DBUG_PRINT("info", ("autopartitioned: %u", share->auto_partitioned));
  1334. if (!tmp)
  1335. tmp= fix_partition_func(thd, outparam, is_create_table);
  1336. *root_ptr= old_root;
  1337. if (tmp)
  1338. {
  1339. if (is_create_table)
  1340. {
  1341. /*
  1342. During CREATE/ALTER TABLE it is ok to receive errors here.
  1343. It is not ok if it happens during the opening of an frm
  1344. file as part of a normal query.
  1345. */
  1346. error_reported= TRUE;
  1347. }
  1348. goto err;
  1349. }
  1350. }
  1351. #endif
  1352. /* Allocate bitmaps */
  1353. bitmap_size= share->column_bitmap_size;
  1354. if (!(bitmaps= (byte*) alloc_root(&outparam->mem_root, bitmap_size*3)))
  1355. goto err;
  1356. bitmap_init(&outparam->def_read_set,
  1357. (my_bitmap_map*) bitmaps, share->fields, FALSE);
  1358. bitmap_init(&outparam->def_write_set,
  1359. (my_bitmap_map*) (bitmaps+bitmap_size), share->fields, FALSE);
  1360. bitmap_init(&outparam->tmp_set,
  1361. (my_bitmap_map*) (bitmaps+bitmap_size*2), share->fields, FALSE);
  1362. outparam->default_column_bitmaps();
  1363. /* The table struct is now initialized; Open the table */
  1364. error= 2;
  1365. if (db_stat)
  1366. {
  1367. int ha_err;
  1368. if ((ha_err= (outparam->file->
  1369. ha_open(outparam, share->normalized_path.str,
  1370. (db_stat & HA_READ_ONLY ? O_RDONLY : O_RDWR),
  1371. (db_stat & HA_OPEN_TEMPORARY ? HA_OPEN_TMP_TABLE :
  1372. ((db_stat & HA_WAIT_IF_LOCKED) ||
  1373. (specialflag & SPECIAL_WAIT_IF_LOCKED)) ?
  1374. HA_OPEN_WAIT_IF_LOCKED :
  1375. (db_stat & (HA_ABORT_IF_LOCKED | HA_GET_INFO)) ?
  1376. HA_OPEN_ABORT_IF_LOCKED :
  1377. HA_OPEN_IGNORE_IF_LOCKED) | ha_open_flags))))
  1378. {
  1379. /* Set a flag if the table is crashed and it can be auto. repaired */
  1380. share->crashed= ((ha_err == HA_ERR_CRASHED_ON_USAGE) &&
  1381. outparam->file->auto_repair() &&
  1382. !(ha_open_flags & HA_OPEN_FOR_REPAIR));
  1383. if (ha_err == HA_ERR_NO_SUCH_TABLE)
  1384. {
  1385. /*
  1386. The table did not exists in storage engine, use same error message
  1387. as if the .frm file didn't exist
  1388. */
  1389. error= 1;
  1390. my_errno= ENOENT;
  1391. }
  1392. else
  1393. {
  1394. outparam->file->print_error(ha_err, MYF(0));
  1395. error_reported= TRUE;
  1396. if (ha_err == HA_ERR_TABLE_DEF_CHANGED)
  1397. error= 7;
  1398. }
  1399. goto err; /* purecov: inspected */
  1400. }
  1401. }
  1402. #if defined(HAVE_purify) && !defined(DBUG_OFF)
  1403. bzero((char*) bitmaps, bitmap_size*3);
  1404. #endif
  1405. thd->status_var.opened_tables++;
  1406. DBUG_RETURN (0);
  1407. err:
  1408. if (! error_reported)
  1409. open_table_error(share, error, my_errno, 0);
  1410. delete outparam->file;
  1411. #ifdef WITH_PARTITION_STORAGE_ENGINE
  1412. if (outparam->part_info)
  1413. free_items(outparam->part_info->item_free_list);
  1414. #endif
  1415. outparam->file= 0; // For easier error checking
  1416. outparam->db_stat=0;
  1417. free_root(&outparam->mem_root, MYF(0)); // Safe to call on bzero'd root
  1418. my_free((char*) outparam->alias, MYF(MY_ALLOW_ZERO_PTR));
  1419. DBUG_RETURN (error);
  1420. }
  1421. /*
  1422. Free information allocated by openfrm
  1423. SYNOPSIS
  1424. closefrm()
  1425. table TABLE object to free
  1426. free_share Is 1 if we also want to free table_share
  1427. */
  1428. int closefrm(register TABLE *table, bool free_share)
  1429. {
  1430. int error=0;
  1431. uint idx;
  1432. KEY *key_info;
  1433. DBUG_ENTER("closefrm");
  1434. DBUG_PRINT("enter", ("table: 0x%lx", (long) table));
  1435. if (table->db_stat)
  1436. error=table->file->close();
  1437. key_info= table->key_info;
  1438. for (idx= table->s->keys; idx; idx--, key_info++)
  1439. {
  1440. if (key_info->flags & HA_USES_PARSER)
  1441. {
  1442. plugin_unlock(key_info->parser);
  1443. key_info->flags= 0;
  1444. }
  1445. }
  1446. my_free((char*) table->alias, MYF(MY_ALLOW_ZERO_PTR));
  1447. table->alias= 0;
  1448. if (table->field)
  1449. {
  1450. for (Field **ptr=table->field ; *ptr ; ptr++)
  1451. delete *ptr;
  1452. table->field= 0;
  1453. }
  1454. delete table->file;
  1455. table->file= 0; /* For easier errorchecking */
  1456. #ifdef WITH_PARTITION_STORAGE_ENGINE
  1457. if (table->part_info)
  1458. {
  1459. free_items(table->part_info->item_free_list);
  1460. table->part_info->item_free_list= 0;
  1461. table->part_info= 0;
  1462. }
  1463. #endif
  1464. if (free_share)
  1465. {
  1466. if (table->s->tmp_table == NO_TMP_TABLE)
  1467. release_table_share(table->s, RELEASE_NORMAL);
  1468. else
  1469. free_table_share(table->s);
  1470. }
  1471. free_root(&table->mem_root, MYF(0));
  1472. DBUG_RETURN(error);
  1473. }
  1474. /* Deallocate temporary blob storage */
  1475. void free_blobs(register TABLE *table)
  1476. {
  1477. uint *ptr, *end;
  1478. for (ptr= table->s->blob_field, end=ptr + table->s->blob_fields ;
  1479. ptr != end ;
  1480. ptr++)
  1481. ((Field_blob*) table->field[*ptr])->free();
  1482. }
  1483. /* Find where a form starts */
  1484. /* if formname is NullS then only formnames is read */
  1485. ulong get_form_pos(File file, uchar *head, TYPELIB *save_names)
  1486. {
  1487. uint a_length,names,length;
  1488. uchar *pos,*buf;
  1489. ulong ret_value=0;
  1490. DBUG_ENTER("get_form_pos");
  1491. names=uint2korr(head+8);
  1492. a_length=(names+2)*sizeof(my_string); /* Room for two extra */
  1493. if (!save_names)
  1494. a_length=0;
  1495. else
  1496. save_names->type_names=0; /* Clear if error */
  1497. if (names)
  1498. {
  1499. length=uint2korr(head+4);
  1500. VOID(my_seek(file,64L,MY_SEEK_SET,MYF(0)));
  1501. if (!(buf= (uchar*) my_malloc((uint) length+a_length+names*4,
  1502. MYF(MY_WME))) ||
  1503. my_read(file,(byte*) buf+a_length,(uint) (length+names*4),
  1504. MYF(MY_NABP)))
  1505. { /* purecov: inspected */
  1506. x_free((gptr) buf); /* purecov: inspected */
  1507. DBUG_RETURN(0L); /* purecov: inspected */
  1508. }
  1509. pos= buf+a_length+length;
  1510. ret_value=uint4korr(pos);
  1511. }
  1512. if (! save_names)
  1513. {
  1514. if (names)
  1515. my_free((gptr) buf,MYF(0));
  1516. }
  1517. else if (!names)
  1518. bzero((char*) save_names,sizeof(save_names));
  1519. else
  1520. {
  1521. char *str;
  1522. str=(char *) (buf+a_length);
  1523. fix_type_pointers((const char ***) &buf,save_names,1,&str);
  1524. }
  1525. DBUG_RETURN(ret_value);
  1526. }
  1527. /* Read string from a file with malloc */
  1528. int read_string(File file, gptr *to, uint length)
  1529. {
  1530. DBUG_ENTER("read_string");
  1531. x_free((gptr) *to);
  1532. if (!(*to= (gptr) my_malloc(length+1,MYF(MY_WME))) ||
  1533. my_read(file,(byte*) *to,length,MYF(MY_NABP)))
  1534. {
  1535. x_free((gptr) *to); /* purecov: inspected */
  1536. *to= 0; /* purecov: inspected */
  1537. DBUG_RETURN(1); /* purecov: inspected */
  1538. }
  1539. *((char*) *to+length)= '\0';
  1540. DBUG_RETURN (0);
  1541. } /* read_string */
  1542. /* Add a new form to a form file */
  1543. ulong make_new_entry(File file, uchar *fileinfo, TYPELIB *formnames,
  1544. const char *newname)
  1545. {
  1546. uint i,bufflength,maxlength,n_length,length,names;
  1547. ulong endpos,newpos;
  1548. char buff[IO_SIZE];
  1549. uchar *pos;
  1550. DBUG_ENTER("make_new_entry");
  1551. length=(uint) strlen(newname)+1;
  1552. n_length=uint2korr(fileinfo+4);
  1553. maxlength=uint2korr(fileinfo+6);
  1554. names=uint2korr(fileinfo+8);
  1555. newpos=uint4korr(fileinfo+10);
  1556. if (64+length+n_length+(names+1)*4 > maxlength)
  1557. { /* Expand file */
  1558. newpos+=IO_SIZE;
  1559. int4store(fileinfo+10,newpos);
  1560. endpos=(ulong) my_seek(file,0L,MY_SEEK_END,MYF(0));/* Copy from file-end */
  1561. bufflength= (uint) (endpos & (IO_SIZE-1)); /* IO_SIZE is a power of 2 */
  1562. while (endpos > maxlength)
  1563. {
  1564. VOID(my_seek(file,(ulong) (endpos-bufflength),MY_SEEK_SET,MYF(0)));
  1565. if (my_read(file,(byte*) buff,bufflength,MYF(MY_NABP+MY_WME)))
  1566. DBUG_RETURN(0L);
  1567. VOID(my_seek(file,(ulong) (endpos-bufflength+IO_SIZE),MY_SEEK_SET,
  1568. MYF(0)));
  1569. if ((my_write(file,(byte*) buff,bufflength,MYF(MY_NABP+MY_WME))))
  1570. DBUG_RETURN(0);
  1571. endpos-=bufflength; bufflength=IO_SIZE;
  1572. }
  1573. bzero(buff,IO_SIZE); /* Null new block */
  1574. VOID(my_seek(file,(ulong) maxlength,MY_SEEK_SET,MYF(0)));
  1575. if (my_write(file,(byte*) buff,bufflength,MYF(MY_NABP+MY_WME)))
  1576. DBUG_RETURN(0L);
  1577. maxlength+=IO_SIZE; /* Fix old ref */
  1578. int2store(fileinfo+6,maxlength);
  1579. for (i=names, pos= (uchar*) *formnames->type_names+n_length-1; i-- ;
  1580. pos+=4)
  1581. {
  1582. endpos=uint4korr(pos)+IO_SIZE;
  1583. int4store(pos,endpos);
  1584. }
  1585. }
  1586. if (n_length == 1 )
  1587. { /* First name */
  1588. length++;
  1589. VOID(strxmov(buff,"/",newname,"/",NullS));
  1590. }
  1591. else
  1592. VOID(strxmov(buff,newname,"/",NullS)); /* purecov: inspected */
  1593. VOID(my_seek(file,63L+(ulong) n_length,MY_SEEK_SET,MYF(0)));
  1594. if (my_write(file,(byte*) buff,(uint) length+1,MYF(MY_NABP+MY_WME)) ||
  1595. (names && my_write(file,(byte*) (*formnames->type_names+n_length-1),
  1596. names*4, MYF(MY_NABP+MY_WME))) ||
  1597. my_write(file,(byte*) fileinfo+10,(uint) 4,MYF(MY_NABP+MY_WME)))
  1598. DBUG_RETURN(0L); /* purecov: inspected */
  1599. int2store(fileinfo+8,names+1);
  1600. int2store(fileinfo+4,n_length+length);
  1601. VOID(my_chsize(file, newpos, 0, MYF(MY_WME)));/* Append file with '\0' */
  1602. DBUG_RETURN(newpos);
  1603. } /* make_new_entry */
  1604. /* error message when opening a form file */
  1605. void open_table_error(TABLE_SHARE *share, int error, int db_errno, int errarg)
  1606. {
  1607. int err_no;
  1608. char buff[FN_REFLEN];
  1609. myf errortype= ME_ERROR+ME_WAITTANG;
  1610. DBUG_ENTER("open_table_error");
  1611. switch (error) {
  1612. case 7:
  1613. case 1:
  1614. if (db_errno == ENOENT)
  1615. my_error(ER_NO_SUCH_TABLE, MYF(0), share->db.str, share->table_name.str);
  1616. else
  1617. {
  1618. strxmov(buff, share->normalized_path.str, reg_ext, NullS);
  1619. my_error((db_errno == EMFILE) ? ER_CANT_OPEN_FILE : ER_FILE_NOT_FOUND,
  1620. errortype, buff, db_errno);
  1621. }
  1622. break;
  1623. case 2:
  1624. {
  1625. handler *file= 0;
  1626. const char *datext= "";
  1627. if (share->db_type != NULL)
  1628. {
  1629. if ((file= get_new_handler(share, current_thd->mem_root,
  1630. share->db_type)))
  1631. {
  1632. if (!(datext= *file->bas_ext()))
  1633. datext= "";
  1634. }
  1635. }
  1636. err_no= (db_errno == ENOENT) ? ER_FILE_NOT_FOUND : (db_errno == EAGAIN) ?
  1637. ER_FILE_USED : ER_CANT_OPEN_FILE;
  1638. strxmov(buff, share->normalized_path.str, datext, NullS);
  1639. my_error(err_no,errortype, buff, db_errno);
  1640. delete file;
  1641. break;
  1642. }
  1643. case 5:
  1644. {
  1645. const char *csname= get_charset_name((uint) errarg);
  1646. char tmp[10];
  1647. if (!csname || csname[0] =='?')
  1648. {
  1649. my_snprintf(tmp, sizeof(tmp), "#%d", errarg);
  1650. csname= tmp;
  1651. }
  1652. my_printf_error(ER_UNKNOWN_COLLATION,
  1653. "Unknown collation '%s' in table '%-.64s' definition",
  1654. MYF(0), csname, share->table_name.str);
  1655. break;
  1656. }
  1657. case 6:
  1658. strxmov(buff, share->normalized_path.str, reg_ext, NullS);
  1659. my_printf_error(ER_NOT_FORM_FILE,
  1660. "Table '%-.64s' was created with a different version "
  1661. "of MySQL and cannot be read",
  1662. MYF(0), buff);
  1663. break;
  1664. default: /* Better wrong error than none */
  1665. case 4:
  1666. strxmov(buff, share->normalized_path.str, reg_ext, NullS);
  1667. my_error(ER_NOT_FORM_FILE, errortype, buff, 0);
  1668. break;
  1669. }
  1670. DBUG_VOID_RETURN;
  1671. } /* open_table_error */
  1672. /*
  1673. ** fix a str_type to a array type
  1674. ** typeparts separated with some char. differents types are separated
  1675. ** with a '\0'
  1676. */
  1677. static void
  1678. fix_type_pointers(const char ***array, TYPELIB *point_to_type, uint types,
  1679. char **names)
  1680. {
  1681. char *type_name, *ptr;
  1682. char chr;
  1683. ptr= *names;
  1684. while (types--)
  1685. {
  1686. point_to_type->name=0;
  1687. point_to_type->type_names= *array;
  1688. if ((chr= *ptr)) /* Test if empty type */
  1689. {
  1690. while ((type_name=strchr(ptr+1,chr)) != NullS)
  1691. {
  1692. *((*array)++) = ptr+1;
  1693. *type_name= '\0'; /* End string */
  1694. ptr=type_name;
  1695. }
  1696. ptr+=2; /* Skip end mark and last 0 */
  1697. }
  1698. else
  1699. ptr++;
  1700. point_to_type->count= (uint) (*array - point_to_type->type_names);
  1701. point_to_type++;
  1702. *((*array)++)= NullS; /* End of type */
  1703. }
  1704. *names=ptr; /* Update end */
  1705. return;
  1706. } /* fix_type_pointers */
  1707. TYPELIB *typelib(MEM_ROOT *mem_root, List<String> &strings)
  1708. {
  1709. TYPELIB *result= (TYPELIB*) alloc_root(mem_root, sizeof(TYPELIB));
  1710. if (!result)
  1711. return 0;
  1712. result->count=strings.elements;
  1713. result->name="";
  1714. uint nbytes= (sizeof(char*) + sizeof(uint)) * (result->count + 1);
  1715. if (!(result->type_names= (const char**) alloc_root(mem_root, nbytes)))
  1716. return 0;
  1717. result->type_lengths= (uint*) (result->type_names + result->count + 1);
  1718. List_iterator<String> it(strings);
  1719. String *tmp;
  1720. for (uint i=0; (tmp=it++) ; i++)
  1721. {
  1722. result->type_names[i]= tmp->ptr();
  1723. result->type_lengths[i]= tmp->length();
  1724. }
  1725. result->type_names[result->count]= 0; // End marker
  1726. result->type_lengths[result->count]= 0;
  1727. return result;
  1728. }
  1729. /*
  1730. Search after a field with given start & length
  1731. If an exact field isn't found, return longest field with starts
  1732. at right position.
  1733. NOTES
  1734. This is needed because in some .frm fields 'fieldnr' was saved wrong
  1735. RETURN
  1736. 0 error
  1737. # field number +1
  1738. */
  1739. static uint find_field(Field **fields, uint start, uint length)
  1740. {
  1741. Field **field;
  1742. uint i, pos;
  1743. pos= 0;
  1744. for (field= fields, i=1 ; *field ; i++,field++)
  1745. {
  1746. if ((*field)->offset() == start)
  1747. {
  1748. if ((*field)->key_length() == length)
  1749. return (i);
  1750. if (!pos || fields[pos-1]->pack_length() <
  1751. (*field)->pack_length())
  1752. pos= i;
  1753. }
  1754. }
  1755. return (pos);
  1756. }
  1757. /* Check that the integer is in the internal */
  1758. int set_zone(register int nr, int min_zone, int max_zone)
  1759. {
  1760. if (nr<=min_zone)
  1761. return (min_zone);
  1762. if (nr>=max_zone)
  1763. return (max_zone);
  1764. return (nr);
  1765. } /* set_zone */
  1766. /* Adjust number to next larger disk buffer */
  1767. ulong next_io_size(register ulong pos)
  1768. {
  1769. reg2 ulong offset;
  1770. if ((offset= pos & (IO_SIZE-1)))
  1771. return pos-offset+IO_SIZE;
  1772. return pos;
  1773. } /* next_io_size */
  1774. /*
  1775. Store an SQL quoted string.
  1776. SYNOPSIS
  1777. append_unescaped()
  1778. res result String
  1779. pos string to be quoted
  1780. length it's length
  1781. NOTE
  1782. This function works correctly with utf8 or single-byte charset strings.
  1783. May fail with some multibyte charsets though.
  1784. */
  1785. void append_unescaped(String *res, const char *pos, uint length)
  1786. {
  1787. const char *end= pos+length;
  1788. res->append('\'');
  1789. for (; pos != end ; pos++)
  1790. {
  1791. #if defined(USE_MB) && MYSQL_VERSION_ID < 40100
  1792. uint mblen;
  1793. if (use_mb(default_charset_info) &&
  1794. (mblen= my_ismbchar(default_charset_info, pos, end)))
  1795. {
  1796. res->append(pos, mblen);
  1797. pos+= mblen;
  1798. continue;
  1799. }
  1800. #endif
  1801. switch (*pos) {
  1802. case 0: /* Must be escaped for 'mysql' */
  1803. res->append('\\');
  1804. res->append('0');
  1805. break;
  1806. case '\n': /* Must be escaped for logs */
  1807. res->append('\\');
  1808. res->append('n');
  1809. break;
  1810. case '\r':
  1811. res->append('\\'); /* This gives better readability */
  1812. res->append('r');
  1813. break;
  1814. case '\\':
  1815. res->append('\\'); /* Because of the sql syntax */
  1816. res->append('\\');
  1817. break;
  1818. case '\'':
  1819. res->append('\''); /* Because of the sql syntax */
  1820. res->append('\'');
  1821. break;
  1822. default:
  1823. res->append(*pos);
  1824. break;
  1825. }
  1826. }
  1827. res->append('\'');
  1828. }
  1829. /* Create a .frm file */
  1830. File create_frm(THD *thd, const char *name, const char *db,
  1831. const char *table, uint reclength, uchar *fileinfo,
  1832. HA_CREATE_INFO *create_info, uint keys)
  1833. {
  1834. register File file;
  1835. uint key_length;
  1836. ulong length;
  1837. char fill[IO_SIZE];
  1838. int create_flags= O_RDWR | O_TRUNC;
  1839. if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
  1840. create_flags|= O_EXCL | O_NOFOLLOW;
  1841. /* Fix this when we have new .frm files; Current limit is 4G rows (QQ) */
  1842. if (create_info->max_rows > UINT_MAX32)
  1843. create_info->max_rows= UINT_MAX32;
  1844. if (create_info->min_rows > UINT_MAX32)
  1845. create_info->min_rows= UINT_MAX32;
  1846. if ((file= my_create(name, CREATE_MODE, create_flags, MYF(0))) >= 0)
  1847. {
  1848. uint key_length, tmp_key_length;
  1849. uint tmp;
  1850. bzero((char*) fileinfo,64);
  1851. /* header */
  1852. fileinfo[0]=(uchar) 254;
  1853. fileinfo[1]= 1;
  1854. fileinfo[2]= FRM_VER+3+ test(create_info->varchar);
  1855. fileinfo[3]= (uchar) ha_legacy_type(
  1856. ha_checktype(thd,ha_legacy_type(create_info->db_type),0,0));
  1857. fileinfo[4]=1;
  1858. int2store(fileinfo+6,IO_SIZE); /* Next block starts here */
  1859. key_length=keys*(7+NAME_LEN+MAX_REF_PARTS*9)+16;
  1860. length= next_io_size((ulong) (IO_SIZE+key_length+reclength+
  1861. create_info->extra_size));
  1862. int4store(fileinfo+10,length);
  1863. tmp_key_length= (key_length < 0xffff) ? key_length : 0xffff;
  1864. int2store(fileinfo+14,tmp_key_length);
  1865. int2store(fileinfo+16,reclength);
  1866. int4store(fileinfo+18,create_info->max_rows);
  1867. int4store(fileinfo+22,create_info->min_rows);
  1868. fileinfo[27]=2; // Use long pack-fields
  1869. create_info->table_options|=HA_OPTION_LONG_BLOB_PTR; // Use portable blob pointers
  1870. int2store(fileinfo+30,create_info->table_options);
  1871. fileinfo[32]=0; // No filename anymore
  1872. fileinfo[33]=5; // Mark for 5.0 frm file
  1873. int4store(fileinfo+34,create_info->avg_row_length);
  1874. fileinfo[38]= (create_info->default_table_charset ?
  1875. create_info->default_table_charset->number : 0);
  1876. fileinfo[40]= (uchar) create_info->row_type;
  1877. /* Next few bytes were for RAID support */
  1878. fileinfo[41]= 0;
  1879. fileinfo[42]= 0;
  1880. fileinfo[43]= 0;
  1881. fileinfo[44]= 0;
  1882. fileinfo[45]= 0;
  1883. fileinfo[46]= 0;
  1884. int4store(fileinfo+47, key_length);
  1885. tmp= MYSQL_VERSION_ID; // Store to avoid warning from int4store
  1886. int4store(fileinfo+51, tmp);
  1887. int4store(fileinfo+55, create_info->extra_size);
  1888. /*
  1889. 59-60 is reserved for extra_rec_buf_length,
  1890. 61 for default_part_db_type
  1891. */
  1892. int2store(fileinfo+62, create_info->key_block_size);
  1893. bzero(fill,IO_SIZE);
  1894. for (; length > IO_SIZE ; length-= IO_SIZE)
  1895. {
  1896. if (my_write(file,(byte*) fill,IO_SIZE,MYF(MY_WME | MY_NABP)))
  1897. {
  1898. VOID(my_close(file,MYF(0)));
  1899. VOID(my_delete(name,MYF(0)));
  1900. return(-1);
  1901. }
  1902. }
  1903. }
  1904. else
  1905. {
  1906. if (my_errno == ENOENT)
  1907. my_error(ER_BAD_DB_ERROR,MYF(0),db);
  1908. else
  1909. my_error(ER_CANT_CREATE_TABLE,MYF(0),table,my_errno);
  1910. }
  1911. return (file);
  1912. } /* create_frm */
  1913. void update_create_info_from_table(HA_CREATE_INFO *create_info, TABLE *table)
  1914. {
  1915. TABLE_SHARE *share= table->s;
  1916. DBUG_ENTER("update_create_info_from_table");
  1917. create_info->max_rows= share->max_rows;
  1918. create_info->min_rows= share->min_rows;
  1919. create_info->table_options= share->db_create_options;
  1920. create_info->avg_row_length= share->avg_row_length;
  1921. create_info->row_type= share->row_type;
  1922. create_info->default_table_charset= share->table_charset;
  1923. create_info->table_charset= 0;
  1924. DBUG_VOID_RETURN;
  1925. }
  1926. int
  1927. rename_file_ext(const char * from,const char * to,const char * ext)
  1928. {
  1929. char from_b[FN_REFLEN],to_b[FN_REFLEN];
  1930. VOID(strxmov(from_b,from,ext,NullS));
  1931. VOID(strxmov(to_b,to,ext,NullS));
  1932. return (my_rename(from_b,to_b,MYF(MY_WME)));
  1933. }
  1934. /*
  1935. Allocate string field in MEM_ROOT and return it as String
  1936. SYNOPSIS
  1937. get_field()
  1938. mem MEM_ROOT for allocating
  1939. field Field for retrieving of string
  1940. res result String
  1941. RETURN VALUES
  1942. 1 string is empty
  1943. 0 all ok
  1944. */
  1945. bool get_field(MEM_ROOT *mem, Field *field, String *res)
  1946. {
  1947. char buff[MAX_FIELD_WIDTH], *to;
  1948. String str(buff,sizeof(buff),&my_charset_bin);
  1949. uint length;
  1950. field->val_str(&str);
  1951. if (!(length= str.length()))
  1952. {
  1953. res->length(0);
  1954. return 1;
  1955. }
  1956. if (!(to= strmake_root(mem, str.ptr(), length)))
  1957. length= 0; // Safety fix
  1958. res->set(to, length, ((Field_str*)field)->charset());
  1959. return 0;
  1960. }
  1961. /*
  1962. Allocate string field in MEM_ROOT and return it as NULL-terminated string
  1963. SYNOPSIS
  1964. get_field()
  1965. mem MEM_ROOT for allocating
  1966. field Field for retrieving of string
  1967. RETURN VALUES
  1968. NullS string is empty
  1969. # pointer to NULL-terminated string value of field
  1970. */
  1971. char *get_field(MEM_ROOT *mem, Field *field)
  1972. {
  1973. char buff[MAX_FIELD_WIDTH], *to;
  1974. String str(buff,sizeof(buff),&my_charset_bin);
  1975. uint length;
  1976. field->val_str(&str);
  1977. length= str.length();
  1978. if (!length || !(to= (char*) alloc_root(mem,length+1)))
  1979. return NullS;
  1980. memcpy(to,str.ptr(),(uint) length);
  1981. to[length]=0;
  1982. return to;
  1983. }
  1984. /*
  1985. Check if database name is valid
  1986. SYNPOSIS
  1987. check_db_name()
  1988. name Name of database
  1989. NOTES
  1990. If lower_case_table_names is set then database is converted to lower case
  1991. RETURN
  1992. 0 ok
  1993. 1 error
  1994. */
  1995. bool check_db_name(char *name)
  1996. {
  1997. char *start=name;
  1998. /* Used to catch empty names and names with end space */
  1999. bool last_char_is_space= TRUE;
  2000. if (lower_case_table_names && name != any_db)
  2001. my_casedn_str(files_charset_info, name);
  2002. while (*name)
  2003. {
  2004. #if defined(USE_MB) && defined(USE_MB_IDENT)
  2005. last_char_is_space= my_isspace(system_charset_info, *name);
  2006. if (use_mb(system_charset_info))
  2007. {
  2008. int len=my_ismbchar(system_charset_info, name,
  2009. name+system_charset_info->mbmaxlen);
  2010. if (len)
  2011. {
  2012. name += len;
  2013. continue;
  2014. }
  2015. }
  2016. #else
  2017. last_char_is_space= *name==' ';
  2018. #endif
  2019. name++;
  2020. }
  2021. return last_char_is_space || (uint) (name - start) > NAME_LEN;
  2022. }
  2023. /*
  2024. Allow anything as a table name, as long as it doesn't contain an
  2025. ' ' at the end
  2026. returns 1 on error
  2027. */
  2028. bool check_table_name(const char *name, uint length)
  2029. {
  2030. const char *end= name+length;
  2031. if (!length || length > NAME_LEN)
  2032. return 1;
  2033. #if defined(USE_MB) && defined(USE_MB_IDENT)
  2034. bool last_char_is_space= FALSE;
  2035. #else
  2036. if (name[length-1]==' ')
  2037. return 1;
  2038. #endif
  2039. while (name != end)
  2040. {
  2041. #if defined(USE_MB) && defined(USE_MB_IDENT)
  2042. last_char_is_space= my_isspace(system_charset_info, *name);
  2043. if (use_mb(system_charset_info))
  2044. {
  2045. int len=my_ismbchar(system_charset_info, name, end);
  2046. if (len)
  2047. {
  2048. name += len;
  2049. continue;
  2050. }
  2051. }
  2052. #endif
  2053. name++;
  2054. }
  2055. #if defined(USE_MB) && defined(USE_MB_IDENT)
  2056. return last_char_is_space;
  2057. #else
  2058. return 0;
  2059. #endif
  2060. }
  2061. bool check_column_name(const char *name)
  2062. {
  2063. const char *start= name;
  2064. bool last_char_is_space= TRUE;
  2065. while (*name)
  2066. {
  2067. #if defined(USE_MB) && defined(USE_MB_IDENT)
  2068. last_char_is_space= my_isspace(system_charset_info, *name);
  2069. if (use_mb(system_charset_info))
  2070. {
  2071. int len=my_ismbchar(system_charset_info, name,
  2072. name+system_charset_info->mbmaxlen);
  2073. if (len)
  2074. {
  2075. name += len;
  2076. continue;
  2077. }
  2078. }
  2079. #else
  2080. last_char_is_space= *name==' ';
  2081. #endif
  2082. if (*name == NAMES_SEP_CHAR)
  2083. return 1;
  2084. name++;
  2085. }
  2086. /* Error if empty or too long column name */
  2087. return last_char_is_space || (uint) (name - start) > NAME_LEN;
  2088. }
  2089. /*
  2090. Checks whether a table is intact. Should be done *just* after the table has
  2091. been opened.
  2092. Synopsis
  2093. table_check_intact()
  2094. table - the table to check
  2095. table_f_count - expected number of columns in the table
  2096. table_def - expected structure of the table (column name and type)
  2097. last_create_time- the table->file->create_time of the table in memory
  2098. we have checked last time
  2099. error_num - ER_XXXX from the error messages file. When 0 no error
  2100. is sent to the client in case types does not match.
  2101. If different col number either
  2102. ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE or
  2103. ER_COL_COUNT_DOESNT_MATCH_CORRUPTED is used
  2104. RETURNS
  2105. 0 - OK
  2106. 1 - There was an error
  2107. */
  2108. my_bool
  2109. table_check_intact(TABLE *table, uint table_f_count,
  2110. TABLE_FIELD_W_TYPE *table_def, time_t *last_create_time,
  2111. int error_num)
  2112. {
  2113. uint i;
  2114. my_bool error= FALSE;
  2115. my_bool fields_diff_count;
  2116. DBUG_ENTER("table_check_intact");
  2117. DBUG_PRINT("info",("table=%s expected_count=%d",table->alias, table_f_count));
  2118. DBUG_PRINT("info",("last_create_time=%d", *last_create_time));
  2119. if ((fields_diff_count= (table->s->fields != table_f_count)) ||
  2120. (*last_create_time != table->file->stats.create_time))
  2121. {
  2122. DBUG_PRINT("info", ("I am suspecting, checking table"));
  2123. if (fields_diff_count)
  2124. {
  2125. // previous MySQL version
  2126. error= TRUE;
  2127. if (MYSQL_VERSION_ID > table->s->mysql_version)
  2128. {
  2129. my_error(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE, MYF(0), table->alias,
  2130. table_f_count, table->s->fields, table->s->mysql_version,
  2131. MYSQL_VERSION_ID);
  2132. sql_print_error(ER(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE),
  2133. table->alias, table_f_count, table->s->fields,
  2134. table->s->mysql_version, MYSQL_VERSION_ID);
  2135. DBUG_RETURN(error);
  2136. }
  2137. else if (MYSQL_VERSION_ID == table->s->mysql_version)
  2138. {
  2139. my_error(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED,MYF(0), table->alias,
  2140. table_f_count, table->s->fields);
  2141. sql_print_error(ER(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED), table->alias,
  2142. table_f_count, table->s->fields);
  2143. }
  2144. else
  2145. {
  2146. /*
  2147. moving from newer mysql to older one -> let's say not an error but
  2148. will check the definition afterwards. If a column was added at the
  2149. end then we don't care much since it's not in the middle.
  2150. */
  2151. error= FALSE;
  2152. }
  2153. }
  2154. //definitely something has changed
  2155. char buffer[255];
  2156. for (i=0 ; i < table_f_count; i++, table_def++)
  2157. {
  2158. String sql_type(buffer, sizeof(buffer), system_charset_info);
  2159. sql_type.length(0);
  2160. /*
  2161. name changes are not fatal, we use sequence numbers => no prob for us
  2162. but this can show tampered table or broken table.
  2163. */
  2164. if (i < table->s->fields)
  2165. {
  2166. Field *field= table->field[i];
  2167. if (strncmp(field->field_name, table_def->name.str,
  2168. table_def->name.length))
  2169. {
  2170. sql_print_error("(%s) Expected field %s at position %d, found %s",
  2171. table->alias, table_def->name.str, i,
  2172. field->field_name);
  2173. }
  2174. /*
  2175. IF the type does not match than something is really wrong
  2176. Check up to length - 1. Why?
  2177. 1. datetime -> datetim -> the same
  2178. 2. int(11) -> int(11 -> the same
  2179. 3. set('one','two') -> set('one','two'
  2180. so for sets if the same prefix is there it's ok if more are
  2181. added as part of the set. The same is valid for enum. So a new
  2182. table running on a old server will be valid.
  2183. */
  2184. field->sql_type(sql_type);
  2185. if (strncmp(sql_type.c_ptr_safe(), table_def->type.str,
  2186. table_def->type.length - 1))
  2187. {
  2188. sql_print_error("(%s) Expected field %s at position %d to have type "
  2189. "%s, found %s", table->alias, table_def->name.str,
  2190. i, table_def->type.str, sql_type.c_ptr_safe());
  2191. error= TRUE;
  2192. }
  2193. else if (table_def->cset.str && !field->has_charset())
  2194. {
  2195. sql_print_error("(%s) Expected field %s at position %d to have "
  2196. "character set '%s' but found no such", table->alias,
  2197. table_def->name.str, i, table_def->cset.str);
  2198. error= TRUE;
  2199. }
  2200. else if (table_def->cset.str &&
  2201. strcmp(field->charset()->csname, table_def->cset.str))
  2202. {
  2203. sql_print_error("(%s) Expected field %s at position %d to have "
  2204. "character set '%s' but found '%s'", table->alias,
  2205. table_def->name.str, i, table_def->cset.str,
  2206. field->charset()->csname);
  2207. error= TRUE;
  2208. }
  2209. }
  2210. else
  2211. {
  2212. sql_print_error("(%s) Expected field %s at position %d to have type %s "
  2213. " but no field found.", table->alias,
  2214. table_def->name.str, i, table_def->type.str);
  2215. error= TRUE;
  2216. }
  2217. }
  2218. if (!error)
  2219. *last_create_time= table->file->stats.create_time;
  2220. else if (!fields_diff_count && error_num)
  2221. my_error(error_num,MYF(0), table->alias, table_f_count, table->s->fields);
  2222. }
  2223. else
  2224. {
  2225. DBUG_PRINT("info", ("Table seems ok without thorough checking."));
  2226. *last_create_time= table->file->stats.create_time;
  2227. }
  2228. DBUG_RETURN(error);
  2229. }
  2230. /*
  2231. Create Item_field for each column in the table.
  2232. SYNPOSIS
  2233. st_table::fill_item_list()
  2234. item_list a pointer to an empty list used to store items
  2235. DESCRIPTION
  2236. Create Item_field object for each column in the table and
  2237. initialize it with the corresponding Field. New items are
  2238. created in the current THD memory root.
  2239. RETURN VALUE
  2240. 0 success
  2241. 1 out of memory
  2242. */
  2243. bool st_table::fill_item_list(List<Item> *item_list) const
  2244. {
  2245. /*
  2246. All Item_field's created using a direct pointer to a field
  2247. are fixed in Item_field constructor.
  2248. */
  2249. for (Field **ptr= field; *ptr; ptr++)
  2250. {
  2251. Item_field *item= new Item_field(*ptr);
  2252. if (!item || item_list->push_back(item))
  2253. return TRUE;
  2254. }
  2255. return FALSE;
  2256. }
  2257. /*
  2258. Reset an existing list of Item_field items to point to the
  2259. Fields of this table.
  2260. SYNPOSIS
  2261. st_table::fill_item_list()
  2262. item_list a non-empty list with Item_fields
  2263. DESCRIPTION
  2264. This is a counterpart of fill_item_list used to redirect
  2265. Item_fields to the fields of a newly created table.
  2266. The caller must ensure that number of items in the item_list
  2267. is the same as the number of columns in the table.
  2268. */
  2269. void st_table::reset_item_list(List<Item> *item_list) const
  2270. {
  2271. List_iterator_fast<Item> it(*item_list);
  2272. for (Field **ptr= field; *ptr; ptr++)
  2273. {
  2274. Item_field *item_field= (Item_field*) it++;
  2275. DBUG_ASSERT(item_field != 0);
  2276. item_field->reset_field(*ptr);
  2277. }
  2278. }
  2279. /*
  2280. calculate md5 of query
  2281. SYNOPSIS
  2282. st_table_list::calc_md5()
  2283. buffer buffer for md5 writing
  2284. */
  2285. void st_table_list::calc_md5(char *buffer)
  2286. {
  2287. my_MD5_CTX context;
  2288. uchar digest[16];
  2289. my_MD5Init(&context);
  2290. my_MD5Update(&context,(uchar *) query.str, query.length);
  2291. my_MD5Final(digest, &context);
  2292. sprintf((char *) buffer,
  2293. "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
  2294. digest[0], digest[1], digest[2], digest[3],
  2295. digest[4], digest[5], digest[6], digest[7],
  2296. digest[8], digest[9], digest[10], digest[11],
  2297. digest[12], digest[13], digest[14], digest[15]);
  2298. }
  2299. /*
  2300. set underlying TABLE for table place holder of VIEW
  2301. DESCRIPTION
  2302. Replace all views that only uses one table with the table itself.
  2303. This allows us to treat the view as a simple table and even update
  2304. it (it is a kind of optimisation)
  2305. SYNOPSIS
  2306. st_table_list::set_underlying_merge()
  2307. */
  2308. void st_table_list::set_underlying_merge()
  2309. {
  2310. TABLE_LIST *tbl;
  2311. if ((tbl= merge_underlying_list))
  2312. {
  2313. /* This is a view. Process all tables of view */
  2314. DBUG_ASSERT(view && effective_algorithm == VIEW_ALGORITHM_MERGE);
  2315. do
  2316. {
  2317. if (tbl->merge_underlying_list) // This is a view
  2318. {
  2319. DBUG_ASSERT(tbl->view &&
  2320. tbl->effective_algorithm == VIEW_ALGORITHM_MERGE);
  2321. /*
  2322. This is the only case where set_ancestor is called on an object
  2323. that may not be a view (in which case ancestor is 0)
  2324. */
  2325. tbl->merge_underlying_list->set_underlying_merge();
  2326. }
  2327. } while ((tbl= tbl->next_local));
  2328. if (!multitable_view)
  2329. {
  2330. table= merge_underlying_list->table;
  2331. schema_table= merge_underlying_list->schema_table;
  2332. }
  2333. }
  2334. }
  2335. /*
  2336. setup fields of placeholder of merged VIEW
  2337. SYNOPSIS
  2338. st_table_list::setup_underlying()
  2339. thd - thread handler
  2340. DESCRIPTION
  2341. It is:
  2342. - preparing translation table for view columns
  2343. If there are underlying view(s) procedure first will be called for them.
  2344. RETURN
  2345. FALSE - OK
  2346. TRUE - error
  2347. */
  2348. bool st_table_list::setup_underlying(THD *thd)
  2349. {
  2350. DBUG_ENTER("st_table_list::setup_underlying");
  2351. if (!field_translation && merge_underlying_list)
  2352. {
  2353. Field_translator *transl;
  2354. SELECT_LEX *select= &view->select_lex;
  2355. Item *item;
  2356. TABLE_LIST *tbl;
  2357. List_iterator_fast<Item> it(select->item_list);
  2358. uint field_count= 0;
  2359. if (check_stack_overrun(thd, STACK_MIN_SIZE, (char *)&field_count))
  2360. {
  2361. DBUG_RETURN(TRUE);
  2362. }
  2363. for (tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
  2364. {
  2365. if (tbl->merge_underlying_list &&
  2366. tbl->setup_underlying(thd))
  2367. {
  2368. DBUG_RETURN(TRUE);
  2369. }
  2370. }
  2371. /* Create view fields translation table */
  2372. if (!(transl=
  2373. (Field_translator*)(thd->stmt_arena->
  2374. alloc(select->item_list.elements *
  2375. sizeof(Field_translator)))))
  2376. {
  2377. DBUG_RETURN(TRUE);
  2378. }
  2379. while ((item= it++))
  2380. {
  2381. transl[field_count].name= item->name;
  2382. transl[field_count++].item= item;
  2383. }
  2384. field_translation= transl;
  2385. field_translation_end= transl + field_count;
  2386. /* TODO: use hash for big number of fields */
  2387. /* full text function moving to current select */
  2388. if (view->select_lex.ftfunc_list->elements)
  2389. {
  2390. Item_func_match *ifm;
  2391. SELECT_LEX *current_select= thd->lex->current_select;
  2392. List_iterator_fast<Item_func_match>
  2393. li(*(view->select_lex.ftfunc_list));
  2394. while ((ifm= li++))
  2395. current_select->ftfunc_list->push_front(ifm);
  2396. }
  2397. }
  2398. DBUG_RETURN(FALSE);
  2399. }
  2400. /*
  2401. Prepare where expression of view
  2402. SYNOPSIS
  2403. st_table_list::prep_where()
  2404. thd - thread handler
  2405. conds - condition of this JOIN
  2406. no_where_clause - do not build WHERE or ON outer qwery do not need it
  2407. (it is INSERT), we do not need conds if this flag is set
  2408. NOTE: have to be called befor CHECK OPTION preparation, because it makes
  2409. fix_fields for view WHERE clause
  2410. RETURN
  2411. FALSE - OK
  2412. TRUE - error
  2413. */
  2414. bool st_table_list::prep_where(THD *thd, Item **conds,
  2415. bool no_where_clause)
  2416. {
  2417. DBUG_ENTER("st_table_list::prep_where");
  2418. for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
  2419. {
  2420. if (tbl->view && tbl->prep_where(thd, conds, no_where_clause))
  2421. {
  2422. DBUG_RETURN(TRUE);
  2423. }
  2424. }
  2425. if (where)
  2426. {
  2427. if (!where->fixed && where->fix_fields(thd, &where))
  2428. {
  2429. DBUG_RETURN(TRUE);
  2430. }
  2431. /*
  2432. check that it is not VIEW in which we insert with INSERT SELECT
  2433. (in this case we can't add view WHERE condition to main SELECT_LEX)
  2434. */
  2435. if (!no_where_clause && !where_processed)
  2436. {
  2437. TABLE_LIST *tbl= this;
  2438. Query_arena *arena= thd->stmt_arena, backup;
  2439. arena= thd->activate_stmt_arena_if_needed(&backup); // For easier test
  2440. /* Go up to join tree and try to find left join */
  2441. for (; tbl; tbl= tbl->embedding)
  2442. {
  2443. if (tbl->outer_join)
  2444. {
  2445. /*
  2446. Store WHERE condition to ON expression for outer join, because
  2447. we can't use WHERE to correctly execute left joins on VIEWs and
  2448. this expression will not be moved to WHERE condition (i.e. will
  2449. be clean correctly for PS/SP)
  2450. */
  2451. tbl->on_expr= and_conds(tbl->on_expr, where);
  2452. break;
  2453. }
  2454. }
  2455. if (tbl == 0)
  2456. *conds= and_conds(*conds, where);
  2457. if (arena)
  2458. thd->restore_active_arena(arena, &backup);
  2459. where_processed= TRUE;
  2460. }
  2461. }
  2462. DBUG_RETURN(FALSE);
  2463. }
  2464. /*
  2465. Prepare check option expression of table
  2466. SYNOPSIS
  2467. st_table_list::prep_check_option()
  2468. thd - thread handler
  2469. check_opt_type - WITH CHECK OPTION type (VIEW_CHECK_NONE,
  2470. VIEW_CHECK_LOCAL, VIEW_CHECK_CASCADED)
  2471. we use this parameter instead of direct check of
  2472. effective_with_check to change type of underlying
  2473. views to VIEW_CHECK_CASCADED if outer view have
  2474. such option and prevent processing of underlying
  2475. view check options if outer view have just
  2476. VIEW_CHECK_LOCAL option.
  2477. NOTE
  2478. This method build check options for every call
  2479. (usual execution or every SP/PS call)
  2480. This method have to be called after WHERE preparation
  2481. (st_table_list::prep_where)
  2482. RETURN
  2483. FALSE - OK
  2484. TRUE - error
  2485. */
  2486. bool st_table_list::prep_check_option(THD *thd, uint8 check_opt_type)
  2487. {
  2488. DBUG_ENTER("st_table_list::prep_check_option");
  2489. for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
  2490. {
  2491. /* see comment of check_opt_type parameter */
  2492. if (tbl->view &&
  2493. tbl->prep_check_option(thd,
  2494. ((check_opt_type == VIEW_CHECK_CASCADED) ?
  2495. VIEW_CHECK_CASCADED :
  2496. VIEW_CHECK_NONE)))
  2497. {
  2498. DBUG_RETURN(TRUE);
  2499. }
  2500. }
  2501. if (check_opt_type)
  2502. {
  2503. Item *item= 0;
  2504. if (where)
  2505. {
  2506. DBUG_ASSERT(where->fixed);
  2507. item= where->copy_andor_structure(thd);
  2508. }
  2509. if (check_opt_type == VIEW_CHECK_CASCADED)
  2510. {
  2511. for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
  2512. {
  2513. if (tbl->check_option)
  2514. item= and_conds(item, tbl->check_option);
  2515. }
  2516. }
  2517. if (item)
  2518. thd->change_item_tree(&check_option, item);
  2519. }
  2520. if (check_option)
  2521. {
  2522. const char *save_where= thd->where;
  2523. thd->where= "check option";
  2524. if (!check_option->fixed &&
  2525. check_option->fix_fields(thd, &check_option) ||
  2526. check_option->check_cols(1))
  2527. {
  2528. DBUG_RETURN(TRUE);
  2529. }
  2530. thd->where= save_where;
  2531. }
  2532. DBUG_RETURN(FALSE);
  2533. }
  2534. /*
  2535. Hide errors which show view underlying table information
  2536. SYNOPSIS
  2537. st_table_list::hide_view_error()
  2538. thd thread handler
  2539. */
  2540. void st_table_list::hide_view_error(THD *thd)
  2541. {
  2542. /* Hide "Unknown column" or "Unknown function" error */
  2543. if (thd->net.last_errno == ER_BAD_FIELD_ERROR ||
  2544. thd->net.last_errno == ER_SP_DOES_NOT_EXIST ||
  2545. thd->net.last_errno == ER_PROCACCESS_DENIED_ERROR ||
  2546. thd->net.last_errno == ER_COLUMNACCESS_DENIED_ERROR ||
  2547. thd->net.last_errno == ER_TABLEACCESS_DENIED_ERROR)
  2548. {
  2549. TABLE_LIST *top= top_table();
  2550. thd->clear_error();
  2551. my_error(ER_VIEW_INVALID, MYF(0), top->view_db.str, top->view_name.str);
  2552. }
  2553. else if (thd->net.last_errno == ER_NO_DEFAULT_FOR_FIELD)
  2554. {
  2555. TABLE_LIST *top= top_table();
  2556. thd->clear_error();
  2557. // TODO: make correct error message
  2558. my_error(ER_NO_DEFAULT_FOR_VIEW_FIELD, MYF(0),
  2559. top->view_db.str, top->view_name.str);
  2560. }
  2561. }
  2562. /*
  2563. Find underlying base tables (TABLE_LIST) which represent given
  2564. table_to_find (TABLE)
  2565. SYNOPSIS
  2566. st_table_list::find_underlying_table()
  2567. table_to_find table to find
  2568. RETURN
  2569. 0 table is not found
  2570. found table reference
  2571. */
  2572. st_table_list *st_table_list::find_underlying_table(TABLE *table_to_find)
  2573. {
  2574. /* is this real table and table which we are looking for? */
  2575. if (table == table_to_find && merge_underlying_list == 0)
  2576. return this;
  2577. for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
  2578. {
  2579. TABLE_LIST *result;
  2580. if ((result= tbl->find_underlying_table(table_to_find)))
  2581. return result;
  2582. }
  2583. return 0;
  2584. }
  2585. /*
  2586. cleunup items belonged to view fields translation table
  2587. SYNOPSIS
  2588. st_table_list::cleanup_items()
  2589. */
  2590. void st_table_list::cleanup_items()
  2591. {
  2592. if (!field_translation)
  2593. return;
  2594. for (Field_translator *transl= field_translation;
  2595. transl < field_translation_end;
  2596. transl++)
  2597. transl->item->walk(&Item::cleanup_processor, 0, 0);
  2598. }
  2599. /*
  2600. check CHECK OPTION condition
  2601. SYNOPSIS
  2602. check_option()
  2603. ignore_failure ignore check option fail
  2604. RETURN
  2605. VIEW_CHECK_OK OK
  2606. VIEW_CHECK_ERROR FAILED
  2607. VIEW_CHECK_SKIP FAILED, but continue
  2608. */
  2609. int st_table_list::view_check_option(THD *thd, bool ignore_failure)
  2610. {
  2611. if (check_option && check_option->val_int() == 0)
  2612. {
  2613. TABLE_LIST *view= top_table();
  2614. if (ignore_failure)
  2615. {
  2616. push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
  2617. ER_VIEW_CHECK_FAILED, ER(ER_VIEW_CHECK_FAILED),
  2618. view->view_db.str, view->view_name.str);
  2619. return(VIEW_CHECK_SKIP);
  2620. }
  2621. else
  2622. {
  2623. my_error(ER_VIEW_CHECK_FAILED, MYF(0), view->view_db.str, view->view_name.str);
  2624. return(VIEW_CHECK_ERROR);
  2625. }
  2626. }
  2627. return(VIEW_CHECK_OK);
  2628. }
  2629. /*
  2630. Find table in underlying tables by mask and check that only this
  2631. table belong to given mask
  2632. SYNOPSIS
  2633. st_table_list::check_single_table()
  2634. table reference on variable where to store found table
  2635. (should be 0 on call, to find table, or point to table for
  2636. unique test)
  2637. map bit mask of tables
  2638. view view for which we are looking table
  2639. RETURN
  2640. FALSE table not found or found only one
  2641. TRUE found several tables
  2642. */
  2643. bool st_table_list::check_single_table(st_table_list **table, table_map map,
  2644. st_table_list *view)
  2645. {
  2646. for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
  2647. {
  2648. if (tbl->table)
  2649. {
  2650. if (tbl->table->map & map)
  2651. {
  2652. if (*table)
  2653. return TRUE;
  2654. *table= tbl;
  2655. tbl->check_option= view->check_option;
  2656. }
  2657. }
  2658. else if (tbl->check_single_table(table, map, view))
  2659. return TRUE;
  2660. }
  2661. return FALSE;
  2662. }
  2663. /*
  2664. Set insert_values buffer
  2665. SYNOPSIS
  2666. set_insert_values()
  2667. mem_root memory pool for allocating
  2668. RETURN
  2669. FALSE - OK
  2670. TRUE - out of memory
  2671. */
  2672. bool st_table_list::set_insert_values(MEM_ROOT *mem_root)
  2673. {
  2674. if (table)
  2675. {
  2676. if (!table->insert_values &&
  2677. !(table->insert_values= (byte *)alloc_root(mem_root,
  2678. table->s->rec_buff_length)))
  2679. return TRUE;
  2680. }
  2681. else
  2682. {
  2683. DBUG_ASSERT(view && merge_underlying_list);
  2684. for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
  2685. if (tbl->set_insert_values(mem_root))
  2686. return TRUE;
  2687. }
  2688. return FALSE;
  2689. }
  2690. /*
  2691. Test if this is a leaf with respect to name resolution.
  2692. SYNOPSIS
  2693. st_table_list::is_leaf_for_name_resolution()
  2694. DESCRIPTION
  2695. A table reference is a leaf with respect to name resolution if
  2696. it is either a leaf node in a nested join tree (table, view,
  2697. schema table, subquery), or an inner node that represents a
  2698. NATURAL/USING join, or a nested join with materialized join
  2699. columns.
  2700. RETURN
  2701. TRUE if a leaf, FALSE otherwise.
  2702. */
  2703. bool st_table_list::is_leaf_for_name_resolution()
  2704. {
  2705. return (view || is_natural_join || is_join_columns_complete ||
  2706. !nested_join);
  2707. }
  2708. /*
  2709. Retrieve the first (left-most) leaf in a nested join tree with
  2710. respect to name resolution.
  2711. SYNOPSIS
  2712. st_table_list::first_leaf_for_name_resolution()
  2713. DESCRIPTION
  2714. Given that 'this' is a nested table reference, recursively walk
  2715. down the left-most children of 'this' until we reach a leaf
  2716. table reference with respect to name resolution.
  2717. IMPLEMENTATION
  2718. The left-most child of a nested table reference is the last element
  2719. in the list of children because the children are inserted in
  2720. reverse order.
  2721. RETURN
  2722. If 'this' is a nested table reference - the left-most child of
  2723. the tree rooted in 'this',
  2724. else return 'this'
  2725. */
  2726. TABLE_LIST *st_table_list::first_leaf_for_name_resolution()
  2727. {
  2728. TABLE_LIST *cur_table_ref;
  2729. NESTED_JOIN *cur_nested_join;
  2730. LINT_INIT(cur_table_ref);
  2731. if (is_leaf_for_name_resolution())
  2732. return this;
  2733. DBUG_ASSERT(nested_join);
  2734. for (cur_nested_join= nested_join;
  2735. cur_nested_join;
  2736. cur_nested_join= cur_table_ref->nested_join)
  2737. {
  2738. List_iterator_fast<TABLE_LIST> it(cur_nested_join->join_list);
  2739. cur_table_ref= it++;
  2740. /*
  2741. If the current nested join is a RIGHT JOIN, the operands in
  2742. 'join_list' are in reverse order, thus the first operand is
  2743. already at the front of the list. Otherwise the first operand
  2744. is in the end of the list of join operands.
  2745. */
  2746. if (!(cur_table_ref->outer_join & JOIN_TYPE_RIGHT))
  2747. {
  2748. TABLE_LIST *next;
  2749. while ((next= it++))
  2750. cur_table_ref= next;
  2751. }
  2752. if (cur_table_ref->is_leaf_for_name_resolution())
  2753. break;
  2754. }
  2755. return cur_table_ref;
  2756. }
  2757. /*
  2758. Retrieve the last (right-most) leaf in a nested join tree with
  2759. respect to name resolution.
  2760. SYNOPSIS
  2761. st_table_list::last_leaf_for_name_resolution()
  2762. DESCRIPTION
  2763. Given that 'this' is a nested table reference, recursively walk
  2764. down the right-most children of 'this' until we reach a leaf
  2765. table reference with respect to name resolution.
  2766. IMPLEMENTATION
  2767. The right-most child of a nested table reference is the first
  2768. element in the list of children because the children are inserted
  2769. in reverse order.
  2770. RETURN
  2771. - If 'this' is a nested table reference - the right-most child of
  2772. the tree rooted in 'this',
  2773. - else - 'this'
  2774. */
  2775. TABLE_LIST *st_table_list::last_leaf_for_name_resolution()
  2776. {
  2777. TABLE_LIST *cur_table_ref= this;
  2778. NESTED_JOIN *cur_nested_join;
  2779. if (is_leaf_for_name_resolution())
  2780. return this;
  2781. DBUG_ASSERT(nested_join);
  2782. for (cur_nested_join= nested_join;
  2783. cur_nested_join;
  2784. cur_nested_join= cur_table_ref->nested_join)
  2785. {
  2786. cur_table_ref= cur_nested_join->join_list.head();
  2787. /*
  2788. If the current nested is a RIGHT JOIN, the operands in
  2789. 'join_list' are in reverse order, thus the last operand is in the
  2790. end of the list.
  2791. */
  2792. if ((cur_table_ref->outer_join & JOIN_TYPE_RIGHT))
  2793. {
  2794. List_iterator_fast<TABLE_LIST> it(cur_nested_join->join_list);
  2795. TABLE_LIST *next;
  2796. cur_table_ref= it++;
  2797. while ((next= it++))
  2798. cur_table_ref= next;
  2799. }
  2800. if (cur_table_ref->is_leaf_for_name_resolution())
  2801. break;
  2802. }
  2803. return cur_table_ref;
  2804. }
  2805. /*
  2806. Register access mode which we need for underlying tables
  2807. SYNOPSIS
  2808. register_want_access()
  2809. want_access Acess which we require
  2810. */
  2811. void st_table_list::register_want_access(ulong want_access)
  2812. {
  2813. /* Remove SHOW_VIEW_ACL, because it will be checked during making view */
  2814. want_access&= ~SHOW_VIEW_ACL;
  2815. if (belong_to_view)
  2816. {
  2817. grant.want_privilege= want_access;
  2818. if (table)
  2819. table->grant.want_privilege= want_access;
  2820. }
  2821. for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
  2822. tbl->register_want_access(want_access);
  2823. }
  2824. /*
  2825. Load security context information for this view
  2826. SYNOPSIS
  2827. st_table_list::prepare_view_securety_context()
  2828. thd [in] thread handler
  2829. RETURN
  2830. FALSE OK
  2831. TRUE Error
  2832. */
  2833. #ifndef NO_EMBEDDED_ACCESS_CHECKS
  2834. bool st_table_list::prepare_view_securety_context(THD *thd)
  2835. {
  2836. DBUG_ENTER("st_table_list::prepare_view_securety_context");
  2837. DBUG_PRINT("enter", ("table: %s", alias));
  2838. DBUG_ASSERT(!prelocking_placeholder && view);
  2839. if (view_suid)
  2840. {
  2841. DBUG_PRINT("info", ("This table is suid view => load contest"));
  2842. DBUG_ASSERT(view && view_sctx);
  2843. if (acl_getroot_no_password(view_sctx,
  2844. definer.user.str,
  2845. definer.host.str,
  2846. definer.host.str,
  2847. thd->db))
  2848. {
  2849. if (thd->lex->sql_command == SQLCOM_SHOW_CREATE)
  2850. {
  2851. push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
  2852. ER_NO_SUCH_USER,
  2853. ER(ER_NO_SUCH_USER),
  2854. definer.user.str, definer.host.str);
  2855. }
  2856. else
  2857. {
  2858. my_error(ER_NO_SUCH_USER, MYF(0), definer.user.str, definer.host.str);
  2859. DBUG_RETURN(TRUE);
  2860. }
  2861. }
  2862. }
  2863. DBUG_RETURN(FALSE);
  2864. }
  2865. #endif
  2866. /*
  2867. Find security context of current view
  2868. SYNOPSIS
  2869. st_table_list::find_view_security_context()
  2870. thd [in] thread handler
  2871. */
  2872. #ifndef NO_EMBEDDED_ACCESS_CHECKS
  2873. Security_context *st_table_list::find_view_security_context(THD *thd)
  2874. {
  2875. Security_context *sctx;
  2876. TABLE_LIST *upper_view= this;
  2877. DBUG_ENTER("st_table_list::find_view_security_context");
  2878. DBUG_ASSERT(view);
  2879. while (upper_view && !upper_view->view_suid)
  2880. {
  2881. DBUG_ASSERT(!upper_view->prelocking_placeholder);
  2882. upper_view= upper_view->referencing_view;
  2883. }
  2884. if (upper_view)
  2885. {
  2886. DBUG_PRINT("info", ("Securety context of view %s will be used",
  2887. upper_view->alias));
  2888. sctx= upper_view->view_sctx;
  2889. DBUG_ASSERT(sctx);
  2890. }
  2891. else
  2892. {
  2893. DBUG_PRINT("info", ("Current global context will be used"));
  2894. sctx= thd->security_ctx;
  2895. }
  2896. DBUG_RETURN(sctx);
  2897. }
  2898. #endif
  2899. /*
  2900. Prepare security context and load underlying tables priveleges for view
  2901. SYNOPSIS
  2902. st_table_list::prepare_security()
  2903. thd [in] thread handler
  2904. RETURN
  2905. FALSE OK
  2906. TRUE Error
  2907. */
  2908. bool st_table_list::prepare_security(THD *thd)
  2909. {
  2910. List_iterator_fast<TABLE_LIST> tb(*view_tables);
  2911. TABLE_LIST *tbl;
  2912. DBUG_ENTER("st_table_list::prepare_security");
  2913. #ifndef NO_EMBEDDED_ACCESS_CHECKS
  2914. Security_context *save_security_ctx= thd->security_ctx;
  2915. DBUG_ASSERT(!prelocking_placeholder);
  2916. if (prepare_view_securety_context(thd))
  2917. DBUG_RETURN(TRUE);
  2918. thd->security_ctx= find_view_security_context(thd);
  2919. while ((tbl= tb++))
  2920. {
  2921. DBUG_ASSERT(tbl->referencing_view);
  2922. char *db, *table_name;
  2923. if (tbl->view)
  2924. {
  2925. db= tbl->view_db.str;
  2926. table_name= tbl->view_name.str;
  2927. }
  2928. else
  2929. {
  2930. db= tbl->db;
  2931. table_name= tbl->table_name;
  2932. }
  2933. fill_effective_table_privileges(thd, &tbl->grant, db, table_name);
  2934. if (tbl->table)
  2935. tbl->table->grant= grant;
  2936. }
  2937. thd->security_ctx= save_security_ctx;
  2938. #else
  2939. while ((tbl= tb++))
  2940. tbl->grant.privilege= ~NO_ACCESS;
  2941. #endif
  2942. DBUG_RETURN(FALSE);
  2943. }
  2944. Natural_join_column::Natural_join_column(Field_translator *field_param,
  2945. TABLE_LIST *tab)
  2946. {
  2947. DBUG_ASSERT(tab->field_translation);
  2948. view_field= field_param;
  2949. table_field= NULL;
  2950. table_ref= tab;
  2951. is_common= FALSE;
  2952. }
  2953. Natural_join_column::Natural_join_column(Field *field_param,
  2954. TABLE_LIST *tab)
  2955. {
  2956. DBUG_ASSERT(tab->table == field_param->table);
  2957. table_field= field_param;
  2958. view_field= NULL;
  2959. table_ref= tab;
  2960. is_common= FALSE;
  2961. }
  2962. const char *Natural_join_column::name()
  2963. {
  2964. if (view_field)
  2965. {
  2966. DBUG_ASSERT(table_field == NULL);
  2967. return view_field->name;
  2968. }
  2969. return table_field->field_name;
  2970. }
  2971. Item *Natural_join_column::create_item(THD *thd)
  2972. {
  2973. if (view_field)
  2974. {
  2975. DBUG_ASSERT(table_field == NULL);
  2976. return create_view_field(thd, table_ref, &view_field->item,
  2977. view_field->name);
  2978. }
  2979. return new Item_field(thd, &thd->lex->current_select->context, table_field);
  2980. }
  2981. Field *Natural_join_column::field()
  2982. {
  2983. if (view_field)
  2984. {
  2985. DBUG_ASSERT(table_field == NULL);
  2986. return NULL;
  2987. }
  2988. return table_field;
  2989. }
  2990. const char *Natural_join_column::table_name()
  2991. {
  2992. return table_ref->alias;
  2993. }
  2994. const char *Natural_join_column::db_name()
  2995. {
  2996. if (view_field)
  2997. return table_ref->view_db.str;
  2998. /*
  2999. Test that TABLE_LIST::db is the same as st_table_share::db to
  3000. ensure consistency. An exception are I_S schema tables, which
  3001. are inconsistent in this respect.
  3002. */
  3003. DBUG_ASSERT(!strcmp(table_ref->db,
  3004. table_ref->table->s->db.str) ||
  3005. (table_ref->schema_table &&
  3006. table_ref->table->s->db.str[0] == 0));
  3007. return table_ref->db;
  3008. }
  3009. GRANT_INFO *Natural_join_column::grant()
  3010. {
  3011. if (view_field)
  3012. return &(table_ref->grant);
  3013. return &(table_ref->table->grant);
  3014. }
  3015. void Field_iterator_view::set(TABLE_LIST *table)
  3016. {
  3017. DBUG_ASSERT(table->field_translation);
  3018. view= table;
  3019. ptr= table->field_translation;
  3020. array_end= table->field_translation_end;
  3021. }
  3022. const char *Field_iterator_table::name()
  3023. {
  3024. return (*ptr)->field_name;
  3025. }
  3026. Item *Field_iterator_table::create_item(THD *thd)
  3027. {
  3028. return new Item_field(thd, &thd->lex->current_select->context, *ptr);
  3029. }
  3030. const char *Field_iterator_view::name()
  3031. {
  3032. return ptr->name;
  3033. }
  3034. Item *Field_iterator_view::create_item(THD *thd)
  3035. {
  3036. return create_view_field(thd, view, &ptr->item, ptr->name);
  3037. }
  3038. Item *create_view_field(THD *thd, TABLE_LIST *view, Item **field_ref,
  3039. const char *name)
  3040. {
  3041. bool save_wrapper= thd->lex->select_lex.no_wrap_view_item;
  3042. Item *field= *field_ref;
  3043. DBUG_ENTER("create_view_field");
  3044. if (view->schema_table_reformed)
  3045. {
  3046. /*
  3047. Translation table items are always Item_fields and already fixed
  3048. ('mysql_schema_table' function). So we can return directly the
  3049. field. This case happens only for 'show & where' commands.
  3050. */
  3051. DBUG_ASSERT(field && field->fixed);
  3052. DBUG_RETURN(field);
  3053. }
  3054. DBUG_ASSERT(field);
  3055. thd->lex->current_select->no_wrap_view_item= TRUE;
  3056. if (!field->fixed)
  3057. {
  3058. if (field->fix_fields(thd, field_ref))
  3059. {
  3060. thd->lex->current_select->no_wrap_view_item= save_wrapper;
  3061. DBUG_RETURN(0);
  3062. }
  3063. field= *field_ref;
  3064. }
  3065. thd->lex->current_select->no_wrap_view_item= save_wrapper;
  3066. if (save_wrapper)
  3067. {
  3068. DBUG_RETURN(field);
  3069. }
  3070. Item *item= new Item_direct_view_ref(&view->view->select_lex.context,
  3071. field_ref, view->alias,
  3072. name);
  3073. DBUG_RETURN(item);
  3074. }
  3075. void Field_iterator_natural_join::set(TABLE_LIST *table_ref)
  3076. {
  3077. DBUG_ASSERT(table_ref->join_columns);
  3078. column_ref_it.init(*(table_ref->join_columns));
  3079. cur_column_ref= column_ref_it++;
  3080. }
  3081. void Field_iterator_natural_join::next()
  3082. {
  3083. cur_column_ref= column_ref_it++;
  3084. DBUG_ASSERT(!cur_column_ref || ! cur_column_ref->table_field ||
  3085. cur_column_ref->table_ref->table ==
  3086. cur_column_ref->table_field->table);
  3087. }
  3088. void Field_iterator_table_ref::set_field_iterator()
  3089. {
  3090. DBUG_ENTER("Field_iterator_table_ref::set_field_iterator");
  3091. /*
  3092. If the table reference we are iterating over is a natural join, or it is
  3093. an operand of a natural join, and TABLE_LIST::join_columns contains all
  3094. the columns of the join operand, then we pick the columns from
  3095. TABLE_LIST::join_columns, instead of the orginial container of the
  3096. columns of the join operator.
  3097. */
  3098. if (table_ref->is_join_columns_complete)
  3099. {
  3100. /* Necesary, but insufficient conditions. */
  3101. DBUG_ASSERT(table_ref->is_natural_join ||
  3102. table_ref->nested_join ||
  3103. table_ref->join_columns &&
  3104. /* This is a merge view. */
  3105. ((table_ref->field_translation &&
  3106. table_ref->join_columns->elements ==
  3107. (ulong)(table_ref->field_translation_end -
  3108. table_ref->field_translation)) ||
  3109. /* This is stored table or a tmptable view. */
  3110. (!table_ref->field_translation &&
  3111. table_ref->join_columns->elements ==
  3112. table_ref->table->s->fields)));
  3113. field_it= &natural_join_it;
  3114. DBUG_PRINT("info",("field_it for '%s' is Field_iterator_natural_join",
  3115. table_ref->alias));
  3116. }
  3117. /* This is a merge view, so use field_translation. */
  3118. else if (table_ref->field_translation)
  3119. {
  3120. DBUG_ASSERT(table_ref->view &&
  3121. table_ref->effective_algorithm == VIEW_ALGORITHM_MERGE);
  3122. field_it= &view_field_it;
  3123. DBUG_PRINT("info", ("field_it for '%s' is Field_iterator_view",
  3124. table_ref->alias));
  3125. }
  3126. /* This is a base table or stored view. */
  3127. else
  3128. {
  3129. DBUG_ASSERT(table_ref->table || table_ref->view);
  3130. field_it= &table_field_it;
  3131. DBUG_PRINT("info", ("field_it for '%s' is Field_iterator_table",
  3132. table_ref->alias));
  3133. }
  3134. field_it->set(table_ref);
  3135. DBUG_VOID_RETURN;
  3136. }
  3137. void Field_iterator_table_ref::set(TABLE_LIST *table)
  3138. {
  3139. DBUG_ASSERT(table);
  3140. first_leaf= table->first_leaf_for_name_resolution();
  3141. last_leaf= table->last_leaf_for_name_resolution();
  3142. DBUG_ASSERT(first_leaf && last_leaf);
  3143. table_ref= first_leaf;
  3144. set_field_iterator();
  3145. }
  3146. void Field_iterator_table_ref::next()
  3147. {
  3148. /* Move to the next field in the current table reference. */
  3149. field_it->next();
  3150. /*
  3151. If all fields of the current table reference are exhausted, move to
  3152. the next leaf table reference.
  3153. */
  3154. if (field_it->end_of_fields() && table_ref != last_leaf)
  3155. {
  3156. table_ref= table_ref->next_name_resolution_table;
  3157. DBUG_ASSERT(table_ref);
  3158. set_field_iterator();
  3159. }
  3160. }
  3161. const char *Field_iterator_table_ref::table_name()
  3162. {
  3163. if (table_ref->view)
  3164. return table_ref->view_name.str;
  3165. else if (table_ref->is_natural_join)
  3166. return natural_join_it.column_ref()->table_name();
  3167. DBUG_ASSERT(!strcmp(table_ref->table_name,
  3168. table_ref->table->s->table_name.str));
  3169. return table_ref->table_name;
  3170. }
  3171. const char *Field_iterator_table_ref::db_name()
  3172. {
  3173. if (table_ref->view)
  3174. return table_ref->view_db.str;
  3175. else if (table_ref->is_natural_join)
  3176. return natural_join_it.column_ref()->db_name();
  3177. /*
  3178. Test that TABLE_LIST::db is the same as st_table_share::db to
  3179. ensure consistency. An exception are I_S schema tables, which
  3180. are inconsistent in this respect.
  3181. */
  3182. DBUG_ASSERT(!strcmp(table_ref->db, table_ref->table->s->db.str) ||
  3183. (table_ref->schema_table &&
  3184. table_ref->table->s->db.str[0] == 0));
  3185. return table_ref->db;
  3186. }
  3187. GRANT_INFO *Field_iterator_table_ref::grant()
  3188. {
  3189. if (table_ref->view)
  3190. return &(table_ref->grant);
  3191. else if (table_ref->is_natural_join)
  3192. return natural_join_it.column_ref()->grant();
  3193. return &(table_ref->table->grant);
  3194. }
  3195. /*
  3196. Create new or return existing column reference to a column of a
  3197. natural/using join.
  3198. SYNOPSIS
  3199. Field_iterator_table_ref::get_or_create_column_ref()
  3200. parent_table_ref the parent table reference over which the
  3201. iterator is iterating
  3202. DESCRIPTION
  3203. Create a new natural join column for the current field of the
  3204. iterator if no such column was created, or return an already
  3205. created natural join column. The former happens for base tables or
  3206. views, and the latter for natural/using joins. If a new field is
  3207. created, then the field is added to 'parent_table_ref' if it is
  3208. given, or to the original table referene of the field if
  3209. parent_table_ref == NULL.
  3210. NOTES
  3211. This method is designed so that when a Field_iterator_table_ref
  3212. walks through the fields of a table reference, all its fields
  3213. are created and stored as follows:
  3214. - If the table reference being iterated is a stored table, view or
  3215. natural/using join, store all natural join columns in a list
  3216. attached to that table reference.
  3217. - If the table reference being iterated is a nested join that is
  3218. not natural/using join, then do not materialize its result
  3219. fields. This is OK because for such table references
  3220. Field_iterator_table_ref iterates over the fields of the nested
  3221. table references (recursively). In this way we avoid the storage
  3222. of unnecessay copies of result columns of nested joins.
  3223. RETURN
  3224. # Pointer to a column of a natural join (or its operand)
  3225. NULL No memory to allocate the column
  3226. */
  3227. Natural_join_column *
  3228. Field_iterator_table_ref::get_or_create_column_ref(TABLE_LIST *parent_table_ref)
  3229. {
  3230. Natural_join_column *nj_col;
  3231. bool is_created= TRUE;
  3232. uint field_count;
  3233. TABLE_LIST *add_table_ref= parent_table_ref ?
  3234. parent_table_ref : table_ref;
  3235. LINT_INIT(field_count);
  3236. if (field_it == &table_field_it)
  3237. {
  3238. /* The field belongs to a stored table. */
  3239. Field *field= table_field_it.field();
  3240. nj_col= new Natural_join_column(field, table_ref);
  3241. field_count= table_ref->table->s->fields;
  3242. }
  3243. else if (field_it == &view_field_it)
  3244. {
  3245. /* The field belongs to a merge view or information schema table. */
  3246. Field_translator *translated_field= view_field_it.field_translator();
  3247. nj_col= new Natural_join_column(translated_field, table_ref);
  3248. field_count= table_ref->field_translation_end -
  3249. table_ref->field_translation;
  3250. }
  3251. else
  3252. {
  3253. /*
  3254. The field belongs to a NATURAL join, therefore the column reference was
  3255. already created via one of the two constructor calls above. In this case
  3256. we just return the already created column reference.
  3257. */
  3258. DBUG_ASSERT(table_ref->is_join_columns_complete);
  3259. is_created= FALSE;
  3260. nj_col= natural_join_it.column_ref();
  3261. DBUG_ASSERT(nj_col);
  3262. }
  3263. DBUG_ASSERT(!nj_col->table_field ||
  3264. nj_col->table_ref->table == nj_col->table_field->table);
  3265. /*
  3266. If the natural join column was just created add it to the list of
  3267. natural join columns of either 'parent_table_ref' or to the table
  3268. reference that directly contains the original field.
  3269. */
  3270. if (is_created)
  3271. {
  3272. /* Make sure not all columns were materialized. */
  3273. DBUG_ASSERT(!add_table_ref->is_join_columns_complete);
  3274. if (!add_table_ref->join_columns)
  3275. {
  3276. /* Create a list of natural join columns on demand. */
  3277. if (!(add_table_ref->join_columns= new List<Natural_join_column>))
  3278. return NULL;
  3279. add_table_ref->is_join_columns_complete= FALSE;
  3280. }
  3281. add_table_ref->join_columns->push_back(nj_col);
  3282. /*
  3283. If new fields are added to their original table reference, mark if
  3284. all fields were added. We do it here as the caller has no easy way
  3285. of knowing when to do it.
  3286. If the fields are being added to parent_table_ref, then the caller
  3287. must take care to mark when all fields are created/added.
  3288. */
  3289. if (!parent_table_ref &&
  3290. add_table_ref->join_columns->elements == field_count)
  3291. add_table_ref->is_join_columns_complete= TRUE;
  3292. }
  3293. return nj_col;
  3294. }
  3295. /*
  3296. Return an existing reference to a column of a natural/using join.
  3297. SYNOPSIS
  3298. Field_iterator_table_ref::get_natural_column_ref()
  3299. DESCRIPTION
  3300. The method should be called in contexts where it is expected that
  3301. all natural join columns are already created, and that the column
  3302. being retrieved is a Natural_join_column.
  3303. RETURN
  3304. # Pointer to a column of a natural join (or its operand)
  3305. NULL No memory to allocate the column
  3306. */
  3307. Natural_join_column *
  3308. Field_iterator_table_ref::get_natural_column_ref()
  3309. {
  3310. Natural_join_column *nj_col;
  3311. DBUG_ASSERT(field_it == &natural_join_it);
  3312. /*
  3313. The field belongs to a NATURAL join, therefore the column reference was
  3314. already created via one of the two constructor calls above. In this case
  3315. we just return the already created column reference.
  3316. */
  3317. nj_col= natural_join_it.column_ref();
  3318. DBUG_ASSERT(nj_col &&
  3319. (!nj_col->table_field ||
  3320. nj_col->table_ref->table == nj_col->table_field->table));
  3321. return nj_col;
  3322. }
  3323. /*****************************************************************************
  3324. Functions to handle column usage bitmaps (read_set, write_set etc...)
  3325. *****************************************************************************/
  3326. /* Reset all columns bitmaps */
  3327. void st_table::clear_column_bitmaps()
  3328. {
  3329. /*
  3330. Reset column read/write usage. It's identical to:
  3331. bitmap_clear_all(&table->def_read_set);
  3332. bitmap_clear_all(&table->def_write_set);
  3333. */
  3334. bzero((char*) def_read_set.bitmap, s->column_bitmap_size*2);
  3335. column_bitmaps_set(&def_read_set, &def_write_set);
  3336. }
  3337. /*
  3338. Tell handler we are going to call position() and rnd_pos() later.
  3339. NOTES:
  3340. This is needed for handlers that uses the primary key to find the
  3341. row. In this case we have to extend the read bitmap with the primary
  3342. key fields.
  3343. */
  3344. void st_table::prepare_for_position()
  3345. {
  3346. DBUG_ENTER("st_table::prepare_for_position");
  3347. if ((file->ha_table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX) &&
  3348. s->primary_key < MAX_KEY)
  3349. {
  3350. mark_columns_used_by_index_no_reset(s->primary_key, read_set);
  3351. /* signal change */
  3352. file->column_bitmaps_signal();
  3353. }
  3354. DBUG_VOID_RETURN;
  3355. }
  3356. /*
  3357. Mark that only fields from one key is used
  3358. NOTE:
  3359. This changes the bitmap to use the tmp bitmap
  3360. After this, you can't access any other columns in the table until
  3361. bitmaps are reset, for example with st_table::clear_column_bitmaps()
  3362. or st_table::restore_column_maps_after_mark_index()
  3363. */
  3364. void st_table::mark_columns_used_by_index(uint index)
  3365. {
  3366. MY_BITMAP *bitmap= &tmp_set;
  3367. DBUG_ENTER("st_table::mark_columns_used_by_index");
  3368. (void) file->extra(HA_EXTRA_KEYREAD);
  3369. bitmap_clear_all(bitmap);
  3370. mark_columns_used_by_index_no_reset(index, bitmap);
  3371. column_bitmaps_set(bitmap, bitmap);
  3372. DBUG_VOID_RETURN;
  3373. }
  3374. /*
  3375. Restore to use normal column maps after key read
  3376. NOTES
  3377. This reverse the change done by mark_columns_used_by_index
  3378. WARNING
  3379. For this to work, one must have the normal table maps in place
  3380. when calling mark_columns_used_by_index
  3381. */
  3382. void st_table::restore_column_maps_after_mark_index()
  3383. {
  3384. DBUG_ENTER("st_table::restore_column_maps_after_mark_index");
  3385. key_read= 0;
  3386. (void) file->extra(HA_EXTRA_NO_KEYREAD);
  3387. default_column_bitmaps();
  3388. file->column_bitmaps_signal();
  3389. DBUG_VOID_RETURN;
  3390. }
  3391. /*
  3392. mark columns used by key, but don't reset other fields
  3393. */
  3394. void st_table::mark_columns_used_by_index_no_reset(uint index,
  3395. MY_BITMAP *bitmap)
  3396. {
  3397. KEY_PART_INFO *key_part= key_info[index].key_part;
  3398. KEY_PART_INFO *key_part_end= (key_part +
  3399. key_info[index].key_parts);
  3400. for (;key_part != key_part_end; key_part++)
  3401. bitmap_set_bit(bitmap, key_part->fieldnr-1);
  3402. }
  3403. /*
  3404. Mark auto-increment fields as used fields in both read and write maps
  3405. NOTES
  3406. This is needed in insert & update as the auto-increment field is
  3407. always set and sometimes read.
  3408. */
  3409. void st_table::mark_auto_increment_column()
  3410. {
  3411. DBUG_ASSERT(found_next_number_field);
  3412. /*
  3413. We must set bit in read set as update_auto_increment() is using the
  3414. store() to check overflow of auto_increment values
  3415. */
  3416. bitmap_set_bit(read_set, found_next_number_field->field_index);
  3417. bitmap_set_bit(write_set, found_next_number_field->field_index);
  3418. if (s->next_number_key_offset)
  3419. mark_columns_used_by_index_no_reset(s->next_number_index, read_set);
  3420. file->column_bitmaps_signal();
  3421. }
  3422. /*
  3423. Mark columns needed for doing an delete of a row
  3424. DESCRIPTON
  3425. Some table engines don't have a cursor on the retrieve rows
  3426. so they need either to use the primary key or all columns to
  3427. be able to delete a row.
  3428. If the engine needs this, the function works as follows:
  3429. - If primary key exits, mark the primary key columns to be read.
  3430. - If not, mark all columns to be read
  3431. If the engine has HA_REQUIRES_KEY_COLUMNS_FOR_DELETE, we will
  3432. mark all key columns as 'to-be-read'. This allows the engine to
  3433. loop over the given record to find all keys and doesn't have to
  3434. retrieve the row again.
  3435. */
  3436. void st_table::mark_columns_needed_for_delete()
  3437. {
  3438. if (triggers)
  3439. {
  3440. if (triggers->bodies[TRG_EVENT_DELETE][TRG_ACTION_BEFORE] ||
  3441. triggers->bodies[TRG_EVENT_DELETE][TRG_ACTION_AFTER])
  3442. {
  3443. /* TODO: optimize to only add columns used by trigger */
  3444. use_all_columns();
  3445. return;
  3446. }
  3447. }
  3448. if (file->ha_table_flags() & HA_REQUIRES_KEY_COLUMNS_FOR_DELETE)
  3449. {
  3450. Field **reg_field;
  3451. for (reg_field= field ; *reg_field ; reg_field++)
  3452. {
  3453. if ((*reg_field)->flags & PART_KEY_FLAG)
  3454. bitmap_set_bit(read_set, (*reg_field)->field_index);
  3455. }
  3456. file->column_bitmaps_signal();
  3457. }
  3458. if (file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_DELETE)
  3459. {
  3460. /*
  3461. If the handler has no cursor capabilites, we have to read either
  3462. the primary key, the hidden primary key or all columns to be
  3463. able to do an delete
  3464. */
  3465. if (s->primary_key == MAX_KEY)
  3466. file->use_hidden_primary_key();
  3467. else
  3468. {
  3469. mark_columns_used_by_index_no_reset(s->primary_key, read_set);
  3470. file->column_bitmaps_signal();
  3471. }
  3472. }
  3473. }
  3474. /*
  3475. Mark columns needed for doing an update of a row
  3476. DESCRIPTON
  3477. Some engines needs to have all columns in an update (to be able to
  3478. build a complete row). If this is the case, we mark all not
  3479. updated columns to be read.
  3480. If this is no the case, we do like in the delete case and mark
  3481. if neeed, either the primary key column or all columns to be read.
  3482. (see mark_columns_needed_for_delete() for details)
  3483. If the engine has HA_REQUIRES_KEY_COLUMNS_FOR_DELETE, we will
  3484. mark all USED key columns as 'to-be-read'. This allows the engine to
  3485. loop over the given record to find all changed keys and doesn't have to
  3486. retrieve the row again.
  3487. */
  3488. void st_table::mark_columns_needed_for_update()
  3489. {
  3490. DBUG_ENTER("mark_columns_needed_for_update");
  3491. if (triggers)
  3492. {
  3493. if (triggers->bodies[TRG_EVENT_UPDATE][TRG_ACTION_BEFORE] ||
  3494. triggers->bodies[TRG_EVENT_UPDATE][TRG_ACTION_AFTER])
  3495. {
  3496. /* TODO: optimize to only add columns used by trigger */
  3497. use_all_columns();
  3498. DBUG_VOID_RETURN;
  3499. }
  3500. }
  3501. if (file->ha_table_flags() & HA_REQUIRES_KEY_COLUMNS_FOR_DELETE)
  3502. {
  3503. /* Mark all used key columns for read */
  3504. Field **reg_field;
  3505. for (reg_field= field ; *reg_field ; reg_field++)
  3506. {
  3507. /* Merge keys is all keys that had a column refered to in the query */
  3508. if (merge_keys.is_overlapping((*reg_field)->part_of_key))
  3509. bitmap_set_bit(read_set, (*reg_field)->field_index);
  3510. }
  3511. file->column_bitmaps_signal();
  3512. }
  3513. if (file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_DELETE)
  3514. {
  3515. /*
  3516. If the handler has no cursor capabilites, we have to read either
  3517. the primary key, the hidden primary key or all columns to be
  3518. able to do an update
  3519. */
  3520. if (s->primary_key == MAX_KEY)
  3521. file->use_hidden_primary_key();
  3522. else
  3523. {
  3524. mark_columns_used_by_index_no_reset(s->primary_key, read_set);
  3525. file->column_bitmaps_signal();
  3526. }
  3527. }
  3528. DBUG_VOID_RETURN;
  3529. }
  3530. /*
  3531. Mark columns the handler needs for doing an insert
  3532. For now, this is used to mark fields used by the trigger
  3533. as changed.
  3534. */
  3535. void st_table::mark_columns_needed_for_insert()
  3536. {
  3537. if (triggers)
  3538. {
  3539. if (triggers->bodies[TRG_EVENT_INSERT][TRG_ACTION_BEFORE] ||
  3540. triggers->bodies[TRG_EVENT_INSERT][TRG_ACTION_AFTER])
  3541. {
  3542. /* TODO: optimize to only add columns used by trigger */
  3543. use_all_columns();
  3544. return;
  3545. }
  3546. }
  3547. if (found_next_number_field)
  3548. mark_auto_increment_column();
  3549. }
  3550. /*****************************************************************************
  3551. ** Instansiate templates
  3552. *****************************************************************************/
  3553. #ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
  3554. template class List<String>;
  3555. template class List_iterator<String>;
  3556. #endif