diff options
Diffstat (limited to 'sql/handler.cc')
-rw-r--r-- | sql/handler.cc | 164 |
1 files changed, 144 insertions, 20 deletions
diff --git a/sql/handler.cc b/sql/handler.cc index e7e1c807306..8a480b0f131 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -111,7 +111,7 @@ TYPELIB tx_isolation_typelib= {array_elements(tx_isolation_names)-1,"", enum db_type ha_resolve_by_name(const char *name, uint namelen) { - THD *thd=current_thd; + THD *thd= current_thd; if (thd && !my_strcasecmp(&my_charset_latin1, name, "DEFAULT")) { return (enum db_type) thd->variables.table_type; } @@ -142,6 +142,7 @@ const char *ha_get_storage_engine(enum db_type db_type) enum db_type ha_checktype(enum db_type database_type) { show_table_type_st *types; + THD *thd= current_thd; for (types= sys_table_types; types->type; types++) { if ((database_type == types->db_type) && @@ -161,8 +162,8 @@ enum db_type ha_checktype(enum db_type database_type) } return - DB_TYPE_UNKNOWN != (enum db_type) current_thd->variables.table_type ? - (enum db_type) current_thd->variables.table_type : + DB_TYPE_UNKNOWN != (enum db_type) thd->variables.table_type ? + (enum db_type) thd->variables.table_type : DB_TYPE_UNKNOWN != (enum db_type) global_system_variables.table_type ? (enum db_type) global_system_variables.table_type : DB_TYPE_MYISAM; @@ -946,7 +947,7 @@ int handler::read_first_row(byte * buf, uint primary_key) void handler::update_timestamp(byte *record) { - long skr= (long) current_thd->query_start(); + long skr= (long) table->in_use->query_start(); #ifdef WORDS_BIGENDIAN if (table->db_low_byte_first) { @@ -958,42 +959,165 @@ void handler::update_timestamp(byte *record) return; } + /* - Updates field with field_type NEXT_NUMBER according to following: - if field = 0 change field to the next free key in database. + Generate the next auto-increment number based on increment and offset + + In most cases increment= offset= 1, in which case we get: + 1,2,3,4,5,... + If increment=10 and offset=5 and previous number is 1, we get: + 1,5,15,25,35,... +*/ + +inline ulonglong +next_insert_id(ulonglong nr,struct system_variables *variables) +{ + nr= (((nr+ variables->auto_increment_increment - + variables->auto_increment_offset)) / + (ulonglong) variables->auto_increment_increment); + return (nr* (ulonglong) variables->auto_increment_increment + + variables->auto_increment_offset); +} + + +/* + Updates columns with type NEXT_NUMBER if: + + - If column value is set to NULL (in which case + auto_increment_field_not_null is 0) + - If column is set to 0 and (sql_mode & MODE_NO_AUTO_VALUE_ON_ZERO) is not + set. In the future we will only set NEXT_NUMBER fields if one sets them + to NULL (or they are not included in the insert list). + + + There are two different cases when the above is true: + + - thd->next_insert_id == 0 (This is the normal case) + In this case we set the set the column for the first row to the value + next_insert_id(get_auto_increment(column))) which is normally + max-used-column-value +1. + + We call get_auto_increment() only for the first row in a multi-row + statement. For the following rows we generate new numbers based on the + last used number. + + - thd->next_insert_id != 0. This happens when we have read a statement + from the binary log or when one has used SET LAST_INSERT_ID=#. + + In this case we will set the column to the value of next_insert_id. + The next row will be given the id + next_insert_id(next_insert_id) + + The idea is the generated auto_increment values are predicatable and + independent of the column values in the table. This is needed to be + able to replicate into a table that alread has rows with a higher + auto-increment value than the one that is inserted. + + After we have already generated an auto-increment number and the users + inserts a column with a higher value than the last used one, we will + start counting from the inserted value. + + thd->next_insert_id is cleared after it's been used for a statement. */ void handler::update_auto_increment() { - longlong nr; - THD *thd; + ulonglong nr; + THD *thd= table->in_use; + struct system_variables *variables= &thd->variables; DBUG_ENTER("handler::update_auto_increment"); - if (table->next_number_field->val_int() != 0 || + + /* + We must save the previous value to be able to restore it if the + row was not inserted + */ + thd->prev_insert_id= thd->next_insert_id; + + if ((nr= table->next_number_field->val_int()) != 0 || table->auto_increment_field_not_null && - current_thd->variables.sql_mode & MODE_NO_AUTO_VALUE_ON_ZERO) + thd->variables.sql_mode & MODE_NO_AUTO_VALUE_ON_ZERO) { + /* Clear flag for next row */ table->auto_increment_field_not_null= FALSE; + /* Mark that we didn't generated a new value **/ auto_increment_column_changed=0; + + /* Update next_insert_id if we have already generated a value */ + if (thd->clear_next_insert_id && nr >= thd->next_insert_id) + { + if (variables->auto_increment_increment != 1) + nr= next_insert_id(nr, variables); + else + nr++; + thd->next_insert_id= nr; + DBUG_PRINT("info",("next_insert_id: %lu", (ulong) nr)); + } DBUG_VOID_RETURN; } table->auto_increment_field_not_null= FALSE; - thd=current_thd; - if ((nr=thd->next_insert_id)) - thd->next_insert_id=0; // Clear after use - else - nr=get_auto_increment(); - if (!table->next_number_field->store(nr)) + if (!(nr= thd->next_insert_id)) + { + nr= get_auto_increment(); + if (variables->auto_increment_increment != 1) + nr= next_insert_id(nr-1, variables); + /* + Update next row based on the found value. This way we don't have to + call the handler for every generated auto-increment value on a + multi-row statement + */ + thd->next_insert_id= nr; + } + + DBUG_PRINT("info",("auto_increment: %lu", (ulong) nr)); + + /* Mark that we should clear next_insert_id before next stmt */ + thd->clear_next_insert_id= 1; + + if (!table->next_number_field->store((longlong) nr)) thd->insert_id((ulonglong) nr); else thd->insert_id(table->next_number_field->val_int()); + + /* + We can't set next_insert_id if the auto-increment key is not the + first key part, as there is no gurantee that the first parts will be in + sequence + */ + if (!table->next_number_key_offset) + { + /* + Set next insert id to point to next auto-increment value to be able to + handle multi-row statements + This works even if auto_increment_increment > 1 + */ + thd->next_insert_id= next_insert_id(nr, variables); + } + else + thd->next_insert_id= 0; + + /* Mark that we generated a new value */ auto_increment_column_changed=1; DBUG_VOID_RETURN; } +/* + restore_auto_increment + + In case of error on write, we restore the last used next_insert_id value + because the previous value was not used. +*/ + +void handler::restore_auto_increment() +{ + THD *thd= table->in_use; + if (thd->next_insert_id) + thd->next_insert_id= thd->prev_insert_id; +} + -longlong handler::get_auto_increment() +ulonglong handler::get_auto_increment() { - longlong nr; + ulonglong nr; int error; (void) extra(HA_EXTRA_KEYREAD); @@ -1014,8 +1138,8 @@ longlong handler::get_auto_increment() if (error) nr=1; else - nr=(longlong) table->next_number_field-> - val_int_offset(table->rec_buff_length)+1; + nr=((ulonglong) table->next_number_field-> + val_int_offset(table->rec_buff_length)+1); index_end(); (void) extra(HA_EXTRA_NO_KEYREAD); return nr; |