From be6f2d302cd71677e1fafbeea9347c196f21e1bd Mon Sep 17 00:00:00 2001 From: Daniel Fiala Date: Sun, 19 Jun 2016 07:38:28 +0100 Subject: 0.1: SQL-level System Versioning --- sql/field.cc | 16 +++++ sql/field.h | 67 +++++++++++++++++++ sql/handler.h | 43 +++++++++++++ sql/item_timefunc.cc | 9 +++ sql/item_timefunc.h | 3 +- sql/lex.h | 4 ++ sql/share/errmsg-utf8.txt | 47 ++++++++++++++ sql/sql_base.cc | 19 +++++- sql/sql_class.h | 11 +++- sql/sql_delete.cc | 49 ++++++++++++-- sql/sql_insert.cc | 90 +++++++++++++++++++++++--- sql/sql_insert.h | 1 + sql/sql_lex.cc | 24 ++++++- sql/sql_lex.h | 6 ++ sql/sql_parse.cc | 64 +++++++++++++++++++ sql/sql_select.cc | 113 +++++++++++++++++++++++++++++++- sql/sql_show.cc | 25 ++++++++ sql/sql_table.cc | 132 +++++++++++++++++++++++++++++++++++++- sql/sql_trigger.h | 2 +- sql/sql_update.cc | 140 +++++++++++++++++++++++++++++++++------- sql/sql_view.cc | 4 +- sql/sql_yacc.yy | 159 ++++++++++++++++++++++++++++++++++++++++++++-- sql/table.cc | 124 ++++++++++++++++++++++++++++++++---- sql/table.h | 100 ++++++++++++++++++++++++++++- sql/unireg.cc | 80 +++++++++++++++++++++++ sql/unireg.h | 1 + 26 files changed, 1265 insertions(+), 68 deletions(-) (limited to 'sql') diff --git a/sql/field.cc b/sql/field.cc index 812c4dbb639..cb91e93ec5e 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -5423,6 +5423,22 @@ void Field_timestampf::store_TIME(my_time_t timestamp, ulong sec_part) my_timestamp_to_binary(&tm, ptr, dec); } +bool Field_timestampf::set_max_timestamp() +{ + DBUG_ENTER("Field_timestampf::set_max_timestamp"); + + mi_int4store(ptr, 0x7fffffff); + memset(ptr + 4, 0x0, value_length() - 4); + + DBUG_RETURN(FALSE); +} + +bool Field_timestampf::is_max_timestamp() +{ + DBUG_ENTER("Field_timestampf::is_max_timestamp"); + + DBUG_RETURN(mi_sint4korr(ptr) == 0x7fffffff); +} my_time_t Field_timestampf::get_timestamp(const uchar *pos, ulong *sec_part) const diff --git a/sql/field.h b/sql/field.h index 9559ba789b1..3513a773ef4 100644 --- a/sql/field.h +++ b/sql/field.h @@ -33,6 +33,8 @@ #include "compat56.h" #include "sql_type.h" /* Type_std_attributes */ +#include + class Send_field; class Copy_field; class Protocol; @@ -674,6 +676,19 @@ public: static void operator delete(void *ptr, MEM_ROOT *mem_root) { DBUG_ASSERT(0); } + /** + Is used by System Versioning. + */ + virtual bool set_max_timestamp() { + return true; + } + /** + Is used by System Versioning. + */ + virtual bool is_max_timestamp() { + return false; + } + uchar *ptr; // Position to field in record /** Byte where the @c NULL bit is stored inside a record. If this Field is a @@ -1391,6 +1406,56 @@ public: FIELD_FLAGS_COLUMN_FORMAT; } + /* + System versioning support. + */ + + bool is_generated() + { + return flags & (GENERATED_ROW_START_FLAG | GENERATED_ROW_END_FLAG); + } + + bool is_generated_row_start() + { + return flags & GENERATED_ROW_START_FLAG; + } + + bool is_generated_row_end() + { + return flags & GENERATED_ROW_END_FLAG; + } + + bool is_versioning_disabled() + { + return flags & WITHOUT_SYSTEM_VERSIONING_FLAG; + } + + /* Mark a field as auto-generated row start column. */ + void set_generated_row_start() + { + //DBUG_ASSERT((flags & GENERATED_ROW_END_FLAG) == 0); + flags |= GENERATED_ROW_START_FLAG; + } + + /* Mark a field as auto-generated row start column. */ + void set_generated_row_end() + { + //DBUG_ASSERT((flags & GENERATED_ROW_START_FLAG) == 0); + flags |= GENERATED_ROW_END_FLAG; + } + + /* Disable a field versioning for a versioned table. */ + void disable_versioning() + { + flags |= WITHOUT_SYSTEM_VERSIONING_FLAG; + } + + /* Inherit a field versioning status from the table. */ + void inherit_versioning() + { + flags &= ~WITHOUT_SYSTEM_VERSIONING_FLAG; + } + /* Validate a non-null field value stored in the given record according to the current thread settings, e.g. sql_mode. @@ -2517,6 +2582,8 @@ public: { return memcmp(a_ptr, b_ptr, pack_length()); } + virtual bool set_max_timestamp(); + virtual bool is_max_timestamp(); void store_TIME(my_time_t timestamp, ulong sec_part); my_time_t get_timestamp(const uchar *pos, ulong *sec_part) const; uint size_of() const { return sizeof(*this); } diff --git a/sql/handler.h b/sql/handler.h index e6615695ce7..2b911cd94c5 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -1660,6 +1660,36 @@ struct Schema_specification_st }; +struct System_versioning_info +{ + struct + { + String *start, *end; + } period_for_system_time; + + struct + { + String *start; + String *end; + } generated_as_row; + + void set_period_for_system_time(String *start, String *end) + { + period_for_system_time.start = start; + period_for_system_time.end = end; + } + void set_period_for_system_time() + { + set_period_for_system_time(NULL, NULL); + } + + /** User has added 'WITH SYSTEM VERSIONING' to table definition */ + bool declared_system_versioning; + + /** Table described by this structure have enabled system versioning */ + bool versioned; +}; + /** A helper struct for table DDL statements, e.g.: CREATE [OR REPLACE] [TEMPORARY] @@ -1734,6 +1764,8 @@ struct Table_scope_and_contents_source_st bool table_was_deleted; sequence_definition *seq_create_info; + System_versioning_info system_versioning_info; + void init() { bzero(this, sizeof(*this)); @@ -1744,6 +1776,17 @@ struct Table_scope_and_contents_source_st db_type= tmp_table() ? ha_default_tmp_handlerton(thd) : ha_default_handlerton(thd); } + + bool versioned() + { + return system_versioning_info.versioned; + } + const System_versioning_info *get_system_versioning_info() + { + if (!versioned()) + return NULL; + return &system_versioning_info; + } }; diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 174f8ffb9fa..a1a9ca5c8ae 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -1702,6 +1702,15 @@ void Item_func_curtime_utc::store_now_in_TIME(THD *thd, MYSQL_TIME *now_time) */ } + +Item_func_now::Item_func_now(THD *thd, uint dec) : + Item_datetimefunc(thd, new (thd->mem_root) Item_decimal(thd, dec, TRUE)), + last_query_id(0) +{ + decimals = dec; +} + + bool Item_func_now::fix_fields(THD *thd, Item **items) { if (decimals > TIME_SECOND_PART_DIGITS) diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index 6ee565236c3..f1a719fb775 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -718,8 +718,7 @@ class Item_func_now :public Item_datetimefunc MYSQL_TIME ltime; query_id_t last_query_id; public: - Item_func_now(THD *thd, uint dec): Item_datetimefunc(thd), last_query_id(0) - { decimals= dec; } + Item_func_now(THD *thd, uint dec); bool fix_fields(THD *, Item **); bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); virtual void store_now_in_TIME(THD *thd, MYSQL_TIME *now_time)=0; diff --git a/sql/lex.h b/sql/lex.h index bb6ab1ca34d..da5a288be99 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -458,6 +458,7 @@ static SYMBOL symbols[] = { { "PAGE_CHECKSUM", SYM(PAGE_CHECKSUM_SYM)}, { "PARSER", SYM(PARSER_SYM)}, { "PARSE_VCOL_EXPR", SYM(PARSE_VCOL_EXPR_SYM)}, + { "PERIOD", SYM(PERIOD_SYM)}, { "PARTIAL", SYM(PARTIAL)}, { "PARTITION", SYM(PARTITION_SYM)}, { "PARTITIONING", SYM(PARTITIONING_SYM)}, @@ -625,6 +626,8 @@ static SYMBOL symbols[] = { { "SUSPEND", SYM(SUSPEND_SYM)}, { "SWAPS", SYM(SWAPS_SYM)}, { "SWITCHES", SYM(SWITCHES_SYM)}, + { "SYSTEM", SYM(SYSTEM)}, + { "SYSTEM_TIME", SYM(SYSTEM_TIME_SYM)}, { "TABLE", SYM(TABLE_SYM)}, { "TABLE_NAME", SYM(TABLE_NAME_SYM)}, { "TABLES", SYM(TABLES)}, @@ -690,6 +693,7 @@ static SYMBOL symbols[] = { { "VIA", SYM(VIA_SYM)}, { "VIEW", SYM(VIEW_SYM)}, { "VIRTUAL", SYM(VIRTUAL_SYM)}, + { "VERSIONING", SYM(VERSIONING)}, { "WAIT", SYM(WAIT_SYM)}, { "WARNINGS", SYM(WARNINGS)}, { "WEEK", SYM(WEEK_SYM)}, diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 562d0acdd19..380d992a89b 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7484,3 +7484,50 @@ ER_UNKNOWN_SEQUENCES 42S02 eng "Unknown SEQUENCE: '%-.300s'" ER_UNKNOWN_VIEW 42S02 eng "Unknown VIEW: '%-.300s'" + +# MariaDB error numbers related to System Versioning + +ER_SYS_START_NOT_SPECIFIED + eng "'Generated as row start' not specified" + +ER_SYS_END_NOT_SPECIFIED + eng "'Generated as row end' not specified" + +ER_SYS_START_MORE_THAN_ONCE + eng "'Generated as row start' specified more than once" + +ER_SYS_END_MORE_THAN_ONCE + eng "Generated as row end specified more than once" + +ER_MISSING_PERIOD_FOR_SYSTEM_TIME + eng "'Period for system time' is missing" + +ER_TABLE_DOESNT_SUPPORT_SYSTEM_VERSIONING + eng "Table '%s' doesn't support system versioning" + +ER_MISSING_WITH_SYSTEM_VERSIONING + eng "'With system versioning' is missing" + +ER_PERIOD_FOR_SYSTEM_TIME_CONTAINS_WRONG_START_COLUMN + eng "First column in 'period for system time' must be equal to 'generated as row start' column" + +ER_PERIOD_FOR_SYSTEM_TIME_CONTAINS_WRONG_END_COLUMN + eng "Second column in 'period for system time' must be equal to 'generated as row end' column" + +ER_SYS_START_AND_SYS_END_SAME + eng "'Period for system_time' must contain two different columns" + +ER_GENERATED_FIELD_CANNOT_BE_SET_BY_USER + eng "Generated field for System Versioning cannot be set by user" + +ER_FOREIGN_KEY_ON_SYSTEM_VERSIONED + eng "Foreign key clause is not yet supported in conjunction with system versioning" + +ER_UPDATE_INFO_WITH_SYSTEM_VERSIONING + eng "Rows matched: %ld Changed: %ld Inserted: %ld Warnings: %ld" + +ER_SYS_START_FIELD_MUST_BE_TIMESTAMP + eng "System start field must be of type TIMESTAMP" + +ER_SYS_END_FIELD_MUST_BE_TIMESTAMP + eng "System end field must be of type TIMESTAMP" diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 5f9c6ceabc7..1af45c12331 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -7071,7 +7071,7 @@ bool setup_fields(THD *thd, Ref_ptr_array ref_pointer_array, thd->lex->current_select->is_item_list_lookup= 0; /* - To prevent fail on forward lookup we fill it with zerows, + To prevent fail on forward lookup we fill it with zeroes, then if we got pointer on zero after find_item_in_list we will know that it is forward lookup. @@ -7962,6 +7962,13 @@ fill_record(THD *thd, TABLE *table_arg, List &fields, List &values, ER_THD(thd, ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN), rfield->field_name, table->s->table_name.str); } + if (table->versioned() && rfield->is_generated() && + !ignore_errors) + { + my_error(ER_GENERATED_FIELD_CANNOT_BE_SET_BY_USER, MYF(0)); + goto err; + } + if (rfield->stored_in_db() && (value->save_in_field(rfield, 0)) < 0 && !ignore_errors) { @@ -8002,7 +8009,7 @@ void switch_to_nullable_trigger_fields(List &items, TABLE *table) Field** field= table->field_to_fill(); /* True if we have NOT NULL fields and BEFORE triggers */ - if (field != table->field) + if (field != table->field && field != table->non_generated_field) { List_iterator_fast it(items); Item *item; @@ -8209,6 +8216,13 @@ fill_record(THD *thd, TABLE *table, Field **ptr, List &values, } } + if (table->versioned() && field->is_generated() && + !ignore_errors) + { + my_error(ER_GENERATED_FIELD_CANNOT_BE_SET_BY_USER, MYF(0)); + goto err; + } + if (use_value) value->save_val(field); else @@ -8460,7 +8474,6 @@ int init_ftfuncs(THD *thd, SELECT_LEX *select_lex, bool no_order) { List_iterator li(*(select_lex->ftfunc_list)); Item_func_match *ifm; - DBUG_PRINT("info",("Performing FULLTEXT search")); while ((ifm=li++)) ifm->init_search(thd, no_order); diff --git a/sql/sql_class.h b/sql/sql_class.h index 62974bb021e..82d538b07fd 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -3906,7 +3906,12 @@ public: Lex_input_stream *lip= &m_parser_state->m_lip; if (!yytext) { - if (!(yytext= lip->get_tok_start())) + if (lip->lookahead_token >= 0) + yytext= lip->get_tok_start_prev(); + else + yytext= lip->get_tok_start(); + + if (!yytext) yytext= ""; } /* Push an error into the error stack */ @@ -5516,6 +5521,10 @@ class multi_update :public select_result_interceptor /* Need this to protect against multiple prepare() calls */ bool prepared; + + // For System Versioning (may need to insert new fields to a table). + ha_rows updated_sys_ver; + public: multi_update(THD *thd_arg, TABLE_LIST *ut, List *leaves_list, List *fields, List *values, diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index eb5f0d7a477..4983aafe9e9 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -343,7 +343,8 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, */ if (!with_select && !using_limit && const_cond_result && (!thd->is_current_stmt_binlog_format_row() && - !(table->triggers && table->triggers->has_delete_triggers()))) + !(table->triggers && table->triggers->has_delete_triggers())) + && !table->versioned()) { /* Update the table->file->stats.records number */ table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); @@ -559,6 +560,12 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, while (!(error=info.read_record(&info)) && !thd->killed && ! thd->is_error()) { + if (table->versioned() && + !table->vers_end_field()->is_max_timestamp()) + { + continue; + } + explain->tracker.on_record_read(); thd->inc_examined_row_count(1); if (table->vfield) @@ -580,7 +587,16 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, break; } - if (!(error= table->file->ha_delete_row(table->record[0]))) + if (!table->versioned()) + error= table->file->ha_delete_row(table->record[0]); + else + { + store_record(table,record[1]); + table->vers_end_field()->set_time(); + error= table->file->ha_update_row(table->record[1], + table->record[0]); + } + if (!error) { deleted++; if (table->triggers && @@ -1052,6 +1068,12 @@ int multi_delete::send_data(List &values) if (table->status & (STATUS_NULL_ROW | STATUS_DELETED)) continue; + if (table->versioned() && + !table->vers_end_field()->is_max_timestamp()) + { + continue; + } + table->file->position(table->record[0]); found++; @@ -1064,7 +1086,16 @@ int multi_delete::send_data(List &values) TRG_ACTION_BEFORE, FALSE)) DBUG_RETURN(1); table->status|= STATUS_DELETED; - if (!(error=table->file->ha_delete_row(table->record[0]))) + if (!table->versioned()) + error= table->file->ha_delete_row(table->record[0]); + else + { + store_record(table,record[1]); + table->vers_end_field()->set_time(); + error= table->file->ha_update_row(table->record[1], + table->record[0]); + } + if (!error) { deleted++; if (!table->file->has_transactions()) @@ -1243,8 +1274,16 @@ int multi_delete::do_table_deletes(TABLE *table, SORT_INFO *sort_info, local_error= 1; break; } - - local_error= table->file->ha_delete_row(table->record[0]); + + if (!table->versioned()) + local_error= table->file->ha_delete_row(table->record[0]); + else + { + store_record(table,record[1]); + table->vers_end_field()->set_time(); + local_error= table->file->ha_update_row(table->record[1], + table->record[0]); + } if (local_error && !ignore) { table->file->print_error(local_error, MYF(0)); diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index f97732860ad..4787356c8b6 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -222,7 +222,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list, table_list->view_db.str, table_list->view_name.str); DBUG_RETURN(-1); } - if (values.elements != table->s->fields) + if (values.elements != table->user_fields()) { my_error(ER_WRONG_VALUE_COUNT_ON_ROW, MYF(0), 1L); DBUG_RETURN(-1); @@ -1029,6 +1029,13 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, } } + if (table->versioned() && + table->vers_update_fields()) + { + error= 1; + break; + } + if ((res= table_list->view_check_option(thd, (values_list.elements == 1 ? 0 : @@ -1555,6 +1562,13 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, if (!table) table= table_list->table; + if (table->versioned() && duplic == DUP_REPLACE) + { + // Additional memory may be required to create historical items. + if (table_list->set_insert_values(thd->mem_root)) + DBUG_RETURN(TRUE); + } + if (!select_insert) { Item *fake_conds= 0; @@ -1605,6 +1619,31 @@ static int last_uniq_key(TABLE *table,uint keynr) } +/* + Inserts one historical row to a table. + + Copies content of the row from table->record[1] to table->record[0], + sets Sys_end to now() and calls ha_write_row() . +*/ + +int vers_insert_history_row(TABLE *table, ha_rows *inserted) +{ + DBUG_ASSERT(table->versioned()); + restore_record(table,record[1]); + + // Set Sys_end to now() + if (table->vers_end_field()->set_time()) + { + return 1; + } + + const int error= table->file->ha_write_row(table->record[0]); + if (!error) + ++*inserted; + + return error; +} + /* Write a record to table with optional deleting of conflicting records, invoke proper triggers if needed. @@ -1809,7 +1848,12 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) } if (error != HA_ERR_RECORD_IS_THE_SAME) + { info->updated++; + if (table->versioned() && + (error=vers_insert_history_row(table, &info->copied))) + goto err; + } else error= 0; /* @@ -1861,13 +1905,16 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) tables which have ON UPDATE but have no ON DELETE triggers, we just should not expose this fact to users by invoking ON UPDATE triggers. - */ - if (last_uniq_key(table,key_nr) && - !table->file->referenced_by_foreign_key() && - (!table->triggers || !table->triggers->has_delete_triggers())) + For system versioning wa also use path through delete since we would + save nothing through this cheating. + */ + if (last_uniq_key(table,key_nr) && + !table->file->referenced_by_foreign_key() && + (!table->triggers || !table->triggers->has_delete_triggers()) && + !table->versioned()) { if ((error=table->file->ha_update_row(table->record[1], - table->record[0])) && + table->record[0])) && error != HA_ERR_RECORD_IS_THE_SAME) goto err; if (error != HA_ERR_RECORD_IS_THE_SAME) @@ -1887,9 +1934,29 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) table->triggers->process_triggers(thd, TRG_EVENT_DELETE, TRG_ACTION_BEFORE, TRUE)) goto before_trg_err; - if ((error=table->file->ha_delete_row(table->record[1]))) + + if (!table->versioned()) + error= table->file->ha_delete_row(table->record[1]); + else + { + DBUG_ASSERT(table->insert_values); + store_record(table,insert_values); + restore_record(table,record[1]); + if (table->vers_end_field()->set_time()) + { + error= 1; + goto err; + } + error= table->file->ha_update_row(table->record[1], + table->record[0]); + restore_record(table,insert_values); + } + if (error) goto err; - info->deleted++; + if (!table->versioned()) + info->deleted++; + else + info->updated++; if (!table->file->has_transactions()) thd->transaction.stmt.modified_non_trans_table= TRUE; if (table->triggers && @@ -3732,6 +3799,9 @@ int select_insert::send_data(List &values) DBUG_RETURN(0); thd->count_cuted_fields= CHECK_FIELD_WARN; // Calculate cuted fields + if (table->versioned() && + table->vers_update_fields()) + DBUG_RETURN(1); store_values(values); if (table->default_field && table->update_default_fields(0, info.ignore)) DBUG_RETURN(1); @@ -3793,12 +3863,16 @@ int select_insert::send_data(List &values) void select_insert::store_values(List &values) { + DBUG_ENTER("select_insert::store_values"); + if (fields->elements) fill_record_n_invoke_before_triggers(thd, table, *fields, values, 1, TRG_EVENT_INSERT); else fill_record_n_invoke_before_triggers(thd, table, table->field_to_fill(), values, 1, TRG_EVENT_INSERT); + + DBUG_VOID_RETURN; } bool select_insert::prepare_eof() diff --git a/sql/sql_insert.h b/sql/sql_insert.h index aea0dac6b0d..a8794e7414b 100644 --- a/sql/sql_insert.h +++ b/sql/sql_insert.h @@ -37,6 +37,7 @@ void upgrade_lock_type_for_insert(THD *thd, thr_lock_type *lock_type, bool is_multi_insert); int check_that_all_fields_are_given_values(THD *thd, TABLE *entry, TABLE_LIST *table_list); +int vers_insert_history_row(TABLE *table, ha_rows *inserted); int write_record(THD *thd, TABLE *table, COPY_INFO *info); void kill_delayed_threads(void); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index a6796dcb901..c1281a2e022 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1343,6 +1343,27 @@ int MYSQLlex(YYSTYPE *yylval, THD *thd) return WITH; } break; + case FOR_SYM: + /* + * Additional look-ahead to resolve doubtful cases like: + * SELECT ... FOR UPDATE + * SELECT ... FOR SYSTEM_TIME ... . + */ + token= lex_one_token(yylval, thd); + lip->add_digest_token(token, yylval); + switch(token) { + case SYSTEM_TIME_SYM: + return FOR_SYSTEM_TIME_SYM; + default: + /* + Save the token following 'FOR_SYM' + */ + lip->lookahead_yylval= lip->yylval; + lip->yylval= NULL; + lip->lookahead_token= token; + return FOR_SYM; + } + break; default: break; } @@ -2998,8 +3019,7 @@ void Query_tables_list::destroy_query_tables_list() */ LEX::LEX() - : explain(NULL), - result(0), arena_for_set_stmt(0), mem_root_for_set_stmt(0), + : explain(NULL), result(0), arena_for_set_stmt(0), mem_root_for_set_stmt(0), option_type(OPT_DEFAULT), context_analysis_only(0), sphead(0), is_lex_started(0), limit_rows_examined_cnt(ULONGLONG_MAX) { diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 4257e77e2e8..a7fe77ca5a7 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -3560,6 +3560,12 @@ public: SELECT_LEX *exclude_last_select(); bool add_unit_in_brackets(SELECT_LEX *nselect); void check_automatic_up(enum sub_select_type type); + + System_versioning_info *vers_get_info() + { + create_info.system_versioning_info.versioned = true; + return &create_info.system_versioning_info; + } }; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 077c57a953e..5484467509a 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -138,6 +138,7 @@ static void sql_kill_user(THD *thd, LEX_USER *user, killed_state state); static bool lock_tables_precheck(THD *thd, TABLE_LIST *tables); static bool execute_show_status(THD *, TABLE_LIST *); static bool check_rename_table(THD *, TABLE_LIST *, TABLE_LIST *); +static bool check_system_versioning(Table_scope_and_contents_source_st *); const char *any_db="*any*"; // Special symbol for check_access @@ -3849,6 +3850,9 @@ mysql_execute_command(THD *thd) */ Alter_info alter_info(lex->alter_info, thd->mem_root); + if (check_system_versioning(&create_info)) + goto end_with_restore_list; + if (thd->is_fatal_error) { /* If out of memory when creating a copy of alter_info. */ @@ -7319,6 +7323,66 @@ bool check_fk_parent_table_access(THD *thd, } + +/**************************************************************************** + Checks related to system versioning +****************************************************************************/ + +static bool check_system_versioning(Table_scope_and_contents_source_st *create_info) +{ + const System_versioning_info *versioning_info = &create_info->system_versioning_info; + + if (!versioning_info->versioned) + return false; + + bool r = false; + + if (!versioning_info->declared_system_versioning) + { + r = true; + my_error(ER_MISSING_WITH_SYSTEM_VERSIONING, MYF(0)); + } + + if (!versioning_info->generated_as_row.start) + { + r = true; + my_error(ER_SYS_START_NOT_SPECIFIED, MYF(0)); + } + + if (!versioning_info->generated_as_row.end) + { + r = true; + my_error(ER_SYS_END_NOT_SPECIFIED, MYF(0)); + } + + if (!versioning_info->period_for_system_time.start || !versioning_info->period_for_system_time.end) + { + r = true; + my_error(ER_MISSING_PERIOD_FOR_SYSTEM_TIME, MYF(0)); + } + + if (!r) + { + if (my_strcasecmp(system_charset_info, + versioning_info->generated_as_row.start->c_ptr(), + versioning_info->period_for_system_time.start->c_ptr())) + { + r = true; + my_error(ER_PERIOD_FOR_SYSTEM_TIME_CONTAINS_WRONG_START_COLUMN, MYF(0)); + } + + if (my_strcasecmp(system_charset_info, + versioning_info->generated_as_row.end->c_ptr(), + versioning_info->period_for_system_time.end->c_ptr())) + { + r = true; + my_error(ER_PERIOD_FOR_SYSTEM_TIME_CONTAINS_WRONG_END_COLUMN, MYF(0)); + } + } + + return r; // false means no error +} + /**************************************************************************** Check stack size; Send error if there isn't enough stack to continue ****************************************************************************/ diff --git a/sql/sql_select.cc b/sql/sql_select.cc index a2e3d68e5dd..17e36390146 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -588,7 +588,7 @@ void remove_redundant_subquery_clauses(st_select_lex *subq_select_lex) subq_select_lex->group_list.empty(); DBUG_PRINT("info", ("GROUP BY removed")); } - + /* TODO: This would prevent processing quries with ORDER BY ... LIMIT therefore we disable this optimization for now. @@ -666,6 +666,77 @@ setup_without_group(THD *thd, Ref_ptr_array ref_pointer_array, DBUG_RETURN(res); } +static int +setup_for_system_time(THD *thd, TABLE_LIST *tables, COND **conds, SELECT_LEX *select_lex) +{ + DBUG_ENTER("setup_for_system_time"); + + TABLE_LIST *table; + int versioned_tables= 0; + + for (table= tables; table; table= table->next_local) + { + if (table->table && table->table->versioned()) + versioned_tables++; + else if (table->system_versioning.type != FOR_SYSTEM_TIME_UNSPECIFIED) + { + my_error(ER_TABLE_DOESNT_SUPPORT_SYSTEM_VERSIONING, MYF(0), table->table_name); + DBUG_RETURN(-1); + } + } + + if (versioned_tables == 0) + DBUG_RETURN(0); + + for (table= tables; table; table= table->next_local) + { + if (table->table && table->table->versioned()) + { + Field *fstart= table->table->vers_start_field(); + Field *fend= table->table->vers_end_field(); + Item *istart= new (thd->mem_root) Item_field(thd, fstart); + Item *iend= new (thd->mem_root) Item_field(thd, fend); + Item *cond1= 0, *cond2= 0, *curr = 0; + switch (table->system_versioning.type) + { + case FOR_SYSTEM_TIME_UNSPECIFIED: + curr= new (thd->mem_root) Item_func_now_local(thd, 6); + cond1= new (thd->mem_root) Item_func_le(thd, istart, curr); + cond2= new (thd->mem_root) Item_func_gt(thd, iend, curr); + break; + case FOR_SYSTEM_TIME_AS_OF: + cond1= new (thd->mem_root) Item_func_le(thd, istart, + table->system_versioning.start); + cond2= new (thd->mem_root) Item_func_gt(thd, iend, + table->system_versioning.start); + break; + case FOR_SYSTEM_TIME_FROM_TO: + cond1= new (thd->mem_root) Item_func_lt(thd, istart, + table->system_versioning.end); + cond2= new (thd->mem_root) Item_func_ge(thd, iend, + table->system_versioning.start); + break; + case FOR_SYSTEM_TIME_BETWEEN: + cond1= new (thd->mem_root) Item_func_le(thd, istart, + table->system_versioning.end); + cond2= new (thd->mem_root) Item_func_ge(thd, iend, + table->system_versioning.start); + break; + default: + DBUG_ASSERT(0); + } + if (cond1 && cond2) + { + COND *system_time_cond= new (thd->mem_root) Item_cond_and(thd, cond1, cond2); + thd->change_item_tree(conds, and_items(thd, *conds, system_time_cond)); + table->system_versioning.is_moved_to_where= true; + } + } + } + + DBUG_RETURN(0); +} + /***************************************************************************** Check fields, find best join, do the select and output fields. mysql_select assumes that all tables are already opened @@ -740,7 +811,11 @@ JOIN::prepare(TABLE_LIST *tables_init, { remove_redundant_subquery_clauses(select_lex); } - + + /* Handle FOR SYSTEM_TIME clause. */ + if (setup_for_system_time(thd, tables_list, &conds, select_lex) < 0) + DBUG_RETURN(-1); + /* TRUE if the SELECT list mixes elements with and without grouping, and there is no GROUP BY clause. Mixing non-aggregated fields with @@ -24784,6 +24859,38 @@ bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result) DBUG_RETURN(res || thd->is_error()); } +void TABLE_LIST::print_system_versioning(THD *thd, table_map eliminated_tables, + String *str, enum_query_type query_type) +{ + if (system_versioning.is_moved_to_where) + return; + + // system versioning + if (system_versioning.type != FOR_SYSTEM_TIME_UNSPECIFIED) + { + switch (system_versioning.type) + { + case FOR_SYSTEM_TIME_AS_OF: + str->append(STRING_WITH_LEN(" for system_time as of ")); + system_versioning.start->print(str, query_type); + break; + case FOR_SYSTEM_TIME_FROM_TO: + str->append(STRING_WITH_LEN(" for system_time from timestamp ")); + system_versioning.start->print(str, query_type); + str->append(STRING_WITH_LEN(" to ")); + system_versioning.end->print(str, query_type); + break; + case FOR_SYSTEM_TIME_BETWEEN: + str->append(STRING_WITH_LEN(" for system_time between timestamp ")); + system_versioning.start->print(str, query_type); + str->append(STRING_WITH_LEN(" and ")); + system_versioning.end->print(str, query_type); + break; + default: + DBUG_ASSERT(0); + } + } +} static void print_table_array(THD *thd, table_map eliminated_tables, @@ -25119,6 +25226,8 @@ void TABLE_LIST::print(THD *thd, table_map eliminated_tables, String *str, append_identifier(thd, str, t_alias, strlen(t_alias)); } + print_system_versioning(thd, eliminated_tables, str, query_type); + if (index_hints) { List_iterator it(*index_hints); diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 01f474a73fb..e3613c1e0e7 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1666,6 +1666,7 @@ static bool get_field_default_value(THD *thd, Field *field, String *def_value, has_default= (field->default_value || (!(field->flags & NO_DEFAULT_VALUE_FLAG) && + !field->is_generated() && field->unireg_check != Field::NEXT_NUMBER)); def_value->length(0); @@ -2123,6 +2124,14 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, packet->append(STRING_WITH_LEN(" DEFAULT ")); packet->append(def_value.ptr(), def_value.length(), system_charset_info); } + else if (field->is_generated_row_start()) + { + packet->append(STRING_WITH_LEN(" GENERATED AS ROW START")); + } + else if (field->is_generated_row_end()) + { + packet->append(STRING_WITH_LEN(" GENERATED AS ROW END")); + } if (!limited_mysql_mode && print_on_update_clause(field, &def_value, false)) @@ -2215,6 +2224,17 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, hton->index_options); } + if (table->versioned()) + { + const Field *fs = table->vers_start_field(); + const Field *fe = table->vers_end_field(); + packet->append(STRING_WITH_LEN(",\n PERIOD FOR SYSTEM_TIME (")); + append_identifier(thd,packet,fs->field_name, strlen(fs->field_name)); + packet->append(STRING_WITH_LEN(", ")); + append_identifier(thd,packet,fe->field_name, strlen(fe->field_name)); + packet->append(STRING_WITH_LEN(")")); + } + /* Get possible foreign key definitions stored in InnoDB and append them to the CREATE TABLE statement @@ -2253,6 +2273,11 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, add_table_options(thd, table, create_info_arg, table_list->schema_table != 0, 0, packet); + if (table->versioned()) + { + packet->append(STRING_WITH_LEN(" WITH SYSTEM VERSIONING")); + } + #ifdef WITH_PARTITION_STORAGE_ENGINE { if (table->part_info && diff --git a/sql/sql_table.cc b/sql/sql_table.cc index bfc198a1a68..3913b14ccc8 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -3046,10 +3046,12 @@ void promote_first_timestamp_column(List *column_definitions) if (is_timestamp_type(column_definition->sql_type) || // TIMESTAMP column_definition->unireg_check == Field::TIMESTAMP_OLD_FIELD) // Legacy { + DBUG_PRINT("info", ("field-ptr:%p", column_definition->field)); if ((column_definition->flags & NOT_NULL_FLAG) != 0 && // NOT NULL, column_definition->default_value == NULL && // no constant default, column_definition->unireg_check == Field::NONE && // no function default - column_definition->vcol_info == NULL) + column_definition->vcol_info == NULL && + !(column_definition->flags & (GENERATED_ROW_START_FLAG | GENERATED_ROW_END_FLAG))) // column isn't generated { DBUG_PRINT("info", ("First TIMESTAMP column '%s' was promoted to " "DEFAULT CURRENT_TIMESTAMP ON UPDATE " @@ -3144,6 +3146,39 @@ static void check_duplicate_key(THD *thd, Key *key, KEY *key_info, } } +static void +copy_info_about_generated_fields(Alter_info *dst_alter_info, + HA_CREATE_INFO *src_create_info) +{ + if (!src_create_info->versioned()) + return; + + const System_versioning_info *versioning_info = + src_create_info->get_system_versioning_info(); + DBUG_ASSERT(versioning_info); + const char *row_start_field = versioning_info->generated_as_row.start->c_ptr(); + DBUG_ASSERT(row_start_field); + const char *row_end_field = versioning_info->generated_as_row.end->c_ptr(); + DBUG_ASSERT(row_end_field); + + List_iterator it(dst_alter_info->create_list); + Create_field *column_definition = NULL; + while ( (column_definition = it++) ) + { + if (!my_strcasecmp(system_charset_info, + row_start_field, + column_definition->field_name)) + { + column_definition->flags |= GENERATED_ROW_START_FLAG; + } + else if (!my_strcasecmp(system_charset_info, + row_end_field, + column_definition->field_name)) + { + column_definition->flags |= GENERATED_ROW_END_FLAG; + } + } +} /* Preparation for table creation @@ -3212,6 +3247,9 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, } select_field_pos= alter_info->create_list.elements - select_field_count; + const System_versioning_info *versioning_info = + create_info->get_system_versioning_info(); + for (field_no=0; (sql_field=it++) ; field_no++) { CHARSET_INFO *save_cs; @@ -3437,6 +3475,23 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, */ if (sql_field->stored_in_db()) record_offset+= sql_field->pack_length; + + if (versioning_info) + { + const bool is_generated_as_row_start = + !my_strcasecmp(system_charset_info, + versioning_info->generated_as_row.start->c_ptr(), + sql_field->field_name); + const bool is_generated_as_row_end = + !my_strcasecmp(system_charset_info, + versioning_info->generated_as_row.end->c_ptr(), + sql_field->field_name); + if (is_generated_as_row_start && is_generated_as_row_end) + { + my_error(ER_SYS_START_AND_SYS_END_SAME, MYF(0), sql_field->field_name); + DBUG_RETURN(TRUE); + } + } } /* Update virtual fields' offset*/ it.rewind(); @@ -4285,6 +4340,58 @@ bool Column_definition::sp_prepare_create_field(THD *thd, MEM_ROOT *mem_root) } +static bool +prepare_keys_for_sys_ver(THD *thd, + HA_CREATE_INFO *create_info, + Alter_info *alter_info, + KEY **key_info, + uint key_count) +{ + if (!create_info->versioned()) + return false; + + const System_versioning_info *versioning_info= + create_info->get_system_versioning_info(); + + DBUG_ASSERT(versioning_info); + const char *row_start_field= versioning_info->generated_as_row.start->c_ptr(); + DBUG_ASSERT(row_start_field); + const char *row_end_field= versioning_info->generated_as_row.end->c_ptr(); + DBUG_ASSERT(row_end_field); + + List_iterator key_it(alter_info->key_list); + Key *key= NULL; + while ((key=key_it++)) + { + if (key->type != Key::PRIMARY && key->type != Key::UNIQUE) + continue; + + Key_part_spec *key_part= NULL; + List_iterator part_it(key->columns); + while ((key_part=part_it++)) + { + if (!my_strcasecmp(system_charset_info, + row_start_field, + key_part->field_name.str) || + + !my_strcasecmp(system_charset_info, + row_end_field, + key_part->field_name.str)) + break; + } + if (key_part) + continue; // Key already contains Sys_start or Sys_end + + const LEX_STRING &lex_sys_end= + versioning_info->generated_as_row.end->lex_string(); + Key_part_spec *key_part_sys_end_col= + new(thd->mem_root) Key_part_spec(lex_sys_end, 0); + key->columns.push_back(key_part_sys_end_col); + } + + return false; +} + handler *mysql_create_frm_image(THD *thd, const char *db, const char *table_name, HA_CREATE_INFO *create_info, @@ -4522,6 +4629,27 @@ handler *mysql_create_frm_image(THD *thd, } #endif + if (create_info->versioned()) + { + // FIXME: This test doesn't detect foreign key relationship on the side of + // parent table and System Time support will not work correctly for such + // table either. But this cannot be implemented without changes to innodb + // that are postponed for later time. + List_iterator_fast key_iterator(alter_info->key_list); + Key *key; + while ((key= key_iterator++)) + { + if (key->type == Key::FOREIGN_KEY) + { + my_error(ER_FOREIGN_KEY_ON_SYSTEM_VERSIONED, MYF(0)); + goto err; + } + } + if(prepare_keys_for_sys_ver(thd, create_info, alter_info, key_info, + *key_count)) + goto err; + } + if (mysql_prepare_create_table(thd, create_info, alter_info, &db_options, file, key_info, key_count, create_table_mode)) @@ -4952,6 +5080,8 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table, else create_table_mode= C_ASSISTED_DISCOVERY; + copy_info_about_generated_fields(alter_info, create_info); + if (!opt_explicit_defaults_for_timestamp) promote_first_timestamp_column(&alter_info->create_list); diff --git a/sql/sql_trigger.h b/sql/sql_trigger.h index 9d1c79cc7cf..f1e2135ce93 100644 --- a/sql/sql_trigger.h +++ b/sql/sql_trigger.h @@ -309,7 +309,7 @@ private: inline Field **TABLE::field_to_fill() { return triggers && triggers->nullable_fields() ? triggers->nullable_fields() - : field; + : non_generated_field ? non_generated_field : field; } diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 35e1fe24b97..79cabf180a9 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -44,6 +44,9 @@ // mysql_derived_filling +#include "sql_insert.h" // For vers_insert_history_row() that may be + // needed for System Versioning. + /** True if the table's input and output record buffers are comparable using compare_record(TABLE*). @@ -280,6 +283,10 @@ int mysql_update(THD *thd, Explain_update *explain; query_plan.index= MAX_KEY; query_plan.using_filesort= FALSE; + + // For System Versioning (may need to insert new fields to a table). + ha_rows updated_sys_ver= 0; + DBUG_ENTER("mysql_update"); create_explain_query(thd->lex, thd->mem_root); @@ -354,6 +361,9 @@ int mysql_update(THD *thd, DBUG_RETURN(1); } + if (table->default_field) + table->mark_default_fields_for_write(false); + #ifndef NO_EMBEDDED_ACCESS_CHECKS /* Check values */ table_list->grant.want_privilege= table->grant.want_privilege= @@ -734,6 +744,11 @@ int mysql_update(THD *thd, while (!(error=info.read_record(&info)) && !thd->killed) { + if (table->versioned() && !table->vers_end_field()->is_max_timestamp()) + { + continue; + } + explain->tracker.on_record_read(); thd->inc_examined_row_count(1); if (!select || select->skip_record(thd) > 0) @@ -743,10 +758,17 @@ int mysql_update(THD *thd, explain->tracker.on_record_after_where(); store_record(table,record[1]); + if (fill_record_n_invoke_before_triggers(thd, table, fields, values, 0, TRG_EVENT_UPDATE)) break; /* purecov: inspected */ + if (table->versioned() && table->vers_update_fields()) + { + error= 1; + break; + } + found++; if (!can_compare_record || compare_record(table)) @@ -805,19 +827,29 @@ int mysql_update(THD *thd, else { /* Non-batched update */ - error= table->file->ha_update_row(table->record[1], + error= table->file->ha_update_row(table->record[1], table->record[0]); } - if (!error || error == HA_ERR_RECORD_IS_THE_SAME) - { - if (error != HA_ERR_RECORD_IS_THE_SAME) - updated++; - else - error= 0; - } - else if (!ignore || + if (error == HA_ERR_RECORD_IS_THE_SAME) + { + error= 0; + } + else if (!error) + { + updated++; + + if (table->versioned()) + { + store_record(table, record[2]); + if ((error = vers_insert_history_row(table, &updated_sys_ver))) + break; + + restore_record(table, record[2]); + } + } + else if (!ignore || table->file->is_fatal_error(error, HA_CHECK_ALL)) - { + { /* If (ignore && error is ignorable) we don't have to do anything; otherwise... @@ -1007,9 +1039,15 @@ int mysql_update(THD *thd, if (error < 0 && !thd->lex->analyze_stmt) { char buff[MYSQL_ERRMSG_SIZE]; - my_snprintf(buff, sizeof(buff), ER_THD(thd, ER_UPDATE_INFO), (ulong) found, - (ulong) updated, - (ulong) thd->get_stmt_da()->current_statement_warn_count()); + if (!table->versioned()) + my_snprintf(buff, sizeof(buff), ER_THD(thd, ER_UPDATE_INFO), (ulong) found, + (ulong) updated, + (ulong) thd->get_stmt_da()->current_statement_warn_count()); + else + my_snprintf(buff, sizeof(buff), + ER_THD(thd, ER_UPDATE_INFO_WITH_SYSTEM_VERSIONING), + (ulong) found, (ulong) updated, (ulong) updated_sys_ver, + (ulong) thd->get_stmt_da()->current_statement_warn_count()); my_ok(thd, (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated, id, buff); DBUG_PRINT("info",("%ld records updated", (long) updated)); @@ -1627,8 +1665,10 @@ multi_update::multi_update(THD *thd_arg, TABLE_LIST *table_list, tmp_tables(0), updated(0), found(0), fields(field_list), values(value_list), table_count(0), copy_field(0), handle_duplicates(handle_duplicates_arg), do_update(1), trans_safe(1), - transactional_tables(0), ignore(ignore_arg), error_handled(0), prepared(0) -{} + transactional_tables(0), ignore(ignore_arg), error_handled(0), prepared(0), + updated_sys_ver(0) +{ +} /* @@ -1877,7 +1917,7 @@ static bool safe_update_on_fly(THD *thd, JOIN_TAB *join_tab, return !is_key_used(table, table->s->primary_key, table->write_set); return TRUE; default: - break; // Avoid compler warning + break; // Avoid compiler warning } return FALSE; @@ -2097,6 +2137,11 @@ int multi_update::send_data(List ¬_used_values) if (table->status & (STATUS_NULL_ROW | STATUS_UPDATED)) continue; + if (table->versioned() && !table->vers_end_field()->is_max_timestamp()) + { + continue; + } + if (table == table_to_update) { /* @@ -2109,6 +2154,7 @@ int multi_update::send_data(List ¬_used_values) table->status|= STATUS_UPDATED; store_record(table,record[1]); + if (fill_record_n_invoke_before_triggers(thd, table, *fields_for_table[offset], *values_for_table[offset], 0, @@ -2127,6 +2173,13 @@ int multi_update::send_data(List ¬_used_values) if (table->default_field && table->update_default_fields(1, ignore)) DBUG_RETURN(1); + if (table->versioned() && + table->vers_update_fields()) + { + error= 1; + break; + } + if ((error= cur_table->view_check_option(thd, ignore)) != VIEW_CHECK_OK) { @@ -2174,6 +2227,23 @@ int multi_update::send_data(List ¬_used_values) error= 0; updated--; } + else if (table->versioned()) + { + restore_record(table,record[1]); + + // Set end time to now() + if (table->vers_end_field()->set_time()) + { + error= 1; + break; + } + + if ( (error= vers_insert_history_row(table, &updated_sys_ver)) ) + { + error= 1; + break; + } + } /* non-transactional or transactional table got modified */ /* either multi_update class' flag is raised in its branch */ if (table->file->has_transactions()) @@ -2200,6 +2270,7 @@ int multi_update::send_data(List ¬_used_values) */ uint field_num= 0; List_iterator_fast tbl_it(unupdated_check_opt_tables); + /* Set first tbl = table and then tbl to tables from tbl_it */ TABLE *tbl= table; do { @@ -2262,10 +2333,6 @@ void multi_update::abort_result_set() if (do_update && table_count > 1) { /* Add warning here */ - /* - todo/fixme: do_update() is never called with the arg 1. - should it change the signature to become argless? - */ (void) do_updates(); } } @@ -2447,19 +2514,44 @@ int multi_update::do_updates() goto err2; } } - if ((local_error=table->file->ha_update_row(table->record[1], - table->record[0])) && + if (table->versioned() && + table->vers_update_fields()) + { + goto err2; + } + + if ((local_error=table->file->ha_update_row(table->record[1], + table->record[0])) && local_error != HA_ERR_RECORD_IS_THE_SAME) { if (!ignore || table->file->is_fatal_error(local_error, HA_CHECK_ALL)) { err_table= table; - goto err; + goto err; } - } + } if (local_error != HA_ERR_RECORD_IS_THE_SAME) + { updated++; + + if (table->versioned()) + { + restore_record(table,record[1]); + + // Set end time to now() + if (table->vers_end_field()->set_time()) + { + goto err2; + } + + if ( (local_error= vers_insert_history_row(table, &updated_sys_ver)) ) + { + err_table = table; + goto err; + } + } + } else local_error= 0; } diff --git a/sql/sql_view.cc b/sql/sql_view.cc index fb4c2c3d835..08f42e9ce70 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -2010,7 +2010,7 @@ bool check_key_in_view(THD *thd, TABLE_LIST *view) RETURN FALSE OK - TRUE error (is not sent to cliet) + TRUE error (is not sent to client) */ bool insert_view_fields(THD *thd, List *list, TABLE_LIST *view) @@ -2038,7 +2038,7 @@ bool insert_view_fields(THD *thd, List *list, TABLE_LIST *view) } /* - checking view md5 check suum + checking view md5 check sum SINOPSYS view_checksum() diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 47c3882bba1..6b09055e323 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -758,6 +758,7 @@ Virtual_column_info *add_virtual_expression(THD *thd, Item *expr) uint sp_instr_addr; /* structs */ + system_versioning_for_select system_versioning; LEX_STRING lex_str; LEX_SYMBOL symbol; Lex_string_with_metadata_st lex_string_with_metadata; @@ -859,10 +860,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %parse-param { THD *thd } %lex-param { THD *thd } /* - Currently there are 103 shift/reduce conflicts. + Currently there are 106 shift/reduce conflicts. We should not introduce new conflicts any more. */ -%expect 103 +%expect 106 /* Comments for TOKENS. @@ -878,6 +879,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); INTERNAL : Not a real token, lex optimization OPERATOR : SQL operator FUTURE-USE : Reserved for future use + 32N2439 : Reserver keywords per ISO/IEC PDTR 19075-2, + http://jtc1sc32.org/doc/N2401-2450/32N2439-text_for_ballot-PDTR_19075-2.pdf + System Versioned Tables This makes the code grep-able, and helps maintenance. */ @@ -1090,6 +1094,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token FORCE_SYM %token FOREIGN /* SQL-2003-R */ %token FOR_SYM /* SQL-2003-R */ +%token FOR_SYSTEM_TIME_SYM /* internal */ %token FORMAT_SYM %token FOUND_SYM /* SQL-2003-R */ %token FROM @@ -1320,6 +1325,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token PARTITIONING_SYM %token PASSWORD_SYM %token PERCENT_RANK_SYM +%token PERIOD_SYM /* 32N2439 */ %token PERSISTENT_SYM %token PHASE_SYM %token PLUGINS_SYM @@ -1484,6 +1490,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token SWAPS_SYM %token SWITCHES_SYM %token SYSDATE +%token SYSTEM /* 32N2439 */ +%token SYSTEM_TIME_SYM /* 32N2439 */ %token TABLES %token TABLESPACE %token TABLE_REF_PRIORITY @@ -1552,6 +1560,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token VARIANCE_SYM %token VARYING /* SQL-2003-R */ %token VAR_SAMP_SYM +%token VERSIONING /* 32N2439 */ %token VIA_SYM %token VIEW_SYM /* SQL-2003-N */ %token VIRTUAL_SYM @@ -1638,6 +1647,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type text_string hex_or_bin_String opt_gconcat_separator + period_for_system_time_column_id %type int_type real_type @@ -1885,6 +1895,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); keep_gcc_happy key_using_alg part_column_list + period_for_system_time server_def server_options_list server_option definer_opt no_definer definer get_diagnostics parse_vcol_expr vcol_opt_specifier vcol_opt_attribute @@ -1908,6 +1919,7 @@ END_OF_INPUT %type sp_decl_idents sp_decl_idents_init_vars %type sp_handler_type sp_hcond_list +%type start_or_end %type sp_cond sp_hcond sqlstate signal_value opt_signal_value %type sp_decls sp_decl sp_decl_body sp_decl_variable_list %type sp_name @@ -1942,7 +1954,7 @@ END_OF_INPUT %type window_frame_extent; %type opt_window_frame_exclusion; %type window_frame_start window_frame_bound; - +%type opt_for_system_time_clause; %type '-' '+' '*' '/' '%' '(' ')' @@ -5837,6 +5849,13 @@ create_table_option: { Lex->create_info.used_fields|= HA_CREATE_USED_SEQUENCE; Lex->create_info.sequence= $3; + } + | WITH SYSTEM VERSIONING + { + System_versioning_info *info = Lex->vers_get_info(); + if (!info) + MYSQL_YYABORT; + info->declared_system_versioning = true; } ; @@ -5937,6 +5956,7 @@ field_list_item: column_def { } | key_def | constraint_def + | period_for_system_time ; column_def: @@ -6036,6 +6056,22 @@ constraint_def: } ; +period_for_system_time: + // If FOR_SYM is followed by SYSTEM_TIME_SYM then they are merged to: FOR_SYSTEM_TIME_SYM . + PERIOD_SYM FOR_SYSTEM_TIME_SYM '(' period_for_system_time_column_id ',' period_for_system_time_column_id ')' + { + System_versioning_info *info = Lex->vers_get_info(); + if (!info) + MYSQL_YYABORT; + if (!my_strcasecmp(system_charset_info, $4->c_ptr(), $6->c_ptr())) + { + my_error(ER_SYS_START_AND_SYS_END_SAME, MYF(0), $4->c_ptr()); + MYSQL_YYABORT; + } + info->set_period_for_system_time($4, $6); + } + ; + opt_check_constraint: /* empty */ { $$= (Virtual_column_info*) 0; } | check_constraint { $$= $1;} @@ -6130,6 +6166,46 @@ field_def: Lex->last_field->flags&= ~NOT_NULL_FLAG; // undo automatic NOT NULL for timestamps } vcol_opt_specifier vcol_opt_attribute + | opt_generated_always AS ROW_SYM start_or_end + { + System_versioning_info *info = + Lex->vers_get_info(); + if (!info) + MYSQL_YYABORT; + String *field_name = new (thd->mem_root) + String((const char*)Lex->last_field->field_name, system_charset_info); + if (!field_name) + MYSQL_YYABORT; + + String **p = NULL; + int err_nr = 0; + switch ($4) + { + case 1: + p = &info->generated_as_row.start; + err_nr = ER_SYS_START_MORE_THAN_ONCE; + break; + case 0: + p = &info->generated_as_row.end; + err_nr = ER_SYS_END_MORE_THAN_ONCE; + break; + default: + /* Not Reachable */ + MYSQL_YYABORT; + break; + } + if (*p) + { + my_error(err_nr, MYF(0), field_name->c_ptr()); + MYSQL_YYABORT; + } + *p = field_name; + } + ; + +start_or_end: + START_SYM { $$ = 1; } + | END { $$ = 0; } ; opt_generated_always: @@ -7535,6 +7611,9 @@ alter_list_item: Lex->create_last_non_select_table= Lex->last_table(); Lex->alter_info.flags|= Alter_info::ALTER_ADD_INDEX; } + | ADD period_for_system_time + { + } | add_column '(' create_field_list ')' { Lex->alter_info.flags|= Alter_info::ALTER_ADD_COLUMN | @@ -8587,6 +8666,60 @@ select_options: } ; +opt_for_system_time_clause: + /* empty */ + { + $$.init(); + } + | FOR_SYSTEM_TIME_SYM + AS OF_SYM + TIMESTAMP TEXT_STRING + { + Item *item= create_temporal_literal(thd, $5.str, $5.length, YYCSCL, + MYSQL_TYPE_DATETIME, true); + if (item == NULL) + MYSQL_YYABORT; + $$.init(FOR_SYSTEM_TIME_AS_OF, item); + } + | FOR_SYSTEM_TIME_SYM + AS OF_SYM + NOW_SYM + { + Item *item= new (thd->mem_root) Item_func_now_local(thd, 6); + if (item == NULL) + MYSQL_YYABORT; + $$.init(FOR_SYSTEM_TIME_AS_OF, item); + } + | FOR_SYSTEM_TIME_SYM + FROM + TIMESTAMP TEXT_STRING + TO_SYM + TIMESTAMP TEXT_STRING + { + Item *item1= create_temporal_literal(thd, $4.str, $4.length, YYCSCL, + MYSQL_TYPE_DATETIME, true); + Item *item2= create_temporal_literal(thd, $7.str, $7.length, YYCSCL, + MYSQL_TYPE_DATETIME, true); + if (item1 == NULL || item2 == NULL) + MYSQL_YYABORT; + $$.init(FOR_SYSTEM_TIME_FROM_TO, item1, item2); + } + | FOR_SYSTEM_TIME_SYM + BETWEEN_SYM + TIMESTAMP TEXT_STRING + AND_SYM + TIMESTAMP TEXT_STRING + { + Item *item1= create_temporal_literal(thd, $4.str, $4.length, YYCSCL, + MYSQL_TYPE_DATETIME, true); + Item *item2= create_temporal_literal(thd, $7.str, $7.length, YYCSCL, + MYSQL_TYPE_DATETIME, true); + if (item1 == NULL || item2 == NULL) + MYSQL_YYABORT; + $$.init(FOR_SYSTEM_TIME_BETWEEN, item1, item2); + } + ; + select_option_list: select_option_list select_option | select_option @@ -10994,7 +11127,7 @@ table_primary_ident: SELECT_LEX *sel= Select; sel->table_join_options= 0; } - table_ident opt_use_partition opt_table_alias opt_key_definition + table_ident opt_use_partition opt_table_alias opt_key_definition opt_for_system_time_clause { if (!($$= Select->add_table_to_list(thd, $2, $4, Select->get_table_join_options(), @@ -11004,6 +11137,7 @@ table_primary_ident: $3))) MYSQL_YYABORT; Select->add_joined_table($$); + $$->system_versioning= $6; } ; @@ -15891,6 +16025,16 @@ column_list: | column_list_id ; +period_for_system_time_column_id: + ident + { + String *new_str= new (thd->mem_root) String((const char*) $1.str,$1.length,system_charset_info); + if (new_str == NULL) + MYSQL_YYABORT; + $$= new_str; + } + ; + column_list_id: ident { @@ -16537,7 +16681,12 @@ trigger_tail: FOR_SYM remember_name /* $13 */ { /* $14 */ - Lex->raw_trg_on_table_name_end= YYLIP->get_tok_start(); + /* + FOR token is already passed through (see 'case FOR_SYM' in sql_lex.cc), + so we use _prev() to get it back. + */ + DBUG_ASSERT(YYLIP->lookahead_token >= 0); + Lex->raw_trg_on_table_name_end= YYLIP->get_tok_start_prev(); } EACH_SYM ROW_SYM diff --git a/sql/table.cc b/sql/table.cc index fae17fd8f20..742fad71134 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -1197,6 +1197,8 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, uint len; uint ext_key_parts= 0; plugin_ref se_plugin= 0; + const uchar *system_period = 0; + MEM_ROOT *old_root= thd->mem_root; Virtual_column_info **table_check_constraints; DBUG_ENTER("TABLE_SHARE::init_from_binary_frm_image"); @@ -1290,6 +1292,11 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, } #endif /*HAVE_SPATIAL*/ break; + case EXTRA2_PERIOD_FOR_SYSTEM_TIME: + if (system_period || length != 2 * sizeof(uint16)) + goto err; + system_period = extra2; + break; default: /* abort frm parsing if it's an unknown but important extra2 value */ if (type >= EXTRA2_ENGINE_IMPORTANT) @@ -2470,6 +2477,35 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, } } + /* Set system versioning information. */ + if (system_period == NULL) + { + share->disable_system_versioning(); + } + else + { + DBUG_PRINT("info", ("Setting system versioning informations")); + uint16 row_start = uint2korr(system_period); + uint16 row_end = uint2korr(system_period + sizeof(uint16)); + if (row_start >= share->fields || row_end >= share->fields) + goto err; + DBUG_PRINT("info", ("Columns with system versioning: [%d, %d]", row_start, row_end)); + share->enable_system_versioning(row_start, row_end); + vers_start_field()->set_generated_row_start(); + vers_end_field()->set_generated_row_end(); + + if (vers_start_field()->type() != MYSQL_TYPE_TIMESTAMP) + { + my_error(ER_SYS_START_FIELD_MUST_BE_TIMESTAMP, MYF(0), share->table_name); + goto err; + } + if (vers_end_field()->type() != MYSQL_TYPE_TIMESTAMP) + { + my_error(ER_SYS_END_FIELD_MUST_BE_TIMESTAMP, MYF(0), share->table_name); + goto err; + } + } + /* the correct null_bytes can now be set, since bitfields have been taken into account @@ -3013,25 +3049,30 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share, records=0; if ((db_stat & HA_OPEN_KEYFILE) || (prgflag & DELAYED_OPEN)) records=1; - if (prgflag & (READ_ALL+EXTRA_RECORD)) + if (prgflag & (READ_ALL + EXTRA_RECORD)) + { records++; - - if (!(record= (uchar*) alloc_root(&outparam->mem_root, - share->rec_buff_length * records))) - goto err; /* purecov: inspected */ + if (share->versioned) + records++; + } if (records == 0) { /* We are probably in hard repair, and the buffers should not be used */ - outparam->record[0]= outparam->record[1]= share->default_values; + record= share->default_values; } else { - outparam->record[0]= record; - if (records > 1) - outparam->record[1]= record+ share->rec_buff_length; - else - outparam->record[1]= outparam->record[0]; // Safety + if (!(record= (uchar*) alloc_root(&outparam->mem_root, + share->rec_buff_length * records))) + goto err; /* purecov: inspected */ + } + + for (i= 0; i < 3;) + { + outparam->record[i]= record; + if (++i < records) + record+= share->rec_buff_length; } if (!(field_ptr = (Field **) alloc_root(&outparam->mem_root, @@ -3056,6 +3097,26 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share, } (*field_ptr)= 0; // End marker + if (share->versioned) + { + Field **fptr = NULL; + if (!(fptr = (Field **) alloc_root(&outparam->mem_root, + (uint) ((share->fields+1)* + sizeof(Field*))))) + goto err; + + outparam->non_generated_field = fptr; + for (i=0 ; i < share->fields; i++) + { + if (outparam->field[i]->is_generated()) + continue; + *fptr++ = outparam->field[i]; + } + (*fptr)= 0; // End marker + } + else + outparam->non_generated_field= NULL; + if (share->found_next_number_field) outparam->found_next_number_field= outparam->field[(uint) (share->found_next_number_field - share->field)]; @@ -6225,6 +6286,15 @@ void TABLE::mark_columns_needed_for_delete() if (need_signal) file->column_bitmaps_signal(); + + /* + For System Versioning we have to write and read Sys_end. + */ + if (s->versioned) + { + bitmap_set_bit(read_set, s->vers_end_field()->field_index); + bitmap_set_bit(write_set, s->vers_end_field()->field_index); + } } @@ -6301,6 +6371,15 @@ void TABLE::mark_columns_needed_for_update() need_signal= true; } } + /* + For System Versioning we have to read all columns since we will store + a copy of previous row with modified Sys_end column back to a table. + */ + if (s->versioned) + { + // We will copy old columns to a new row. + use_all_columns(); + } if (check_constraints) { mark_check_constraint_columns_for_read(); @@ -7485,6 +7564,29 @@ int TABLE::update_default_fields(bool update_command, bool ignore_errors) DBUG_RETURN(res); } +bool TABLE::vers_update_fields() +{ + DBUG_ENTER("vers_update_fields"); + + if (!versioned()) + DBUG_RETURN(FALSE); + + if (vers_start_field()->set_time()) + { + DBUG_RETURN(TRUE); + } + + if (vers_end_field()->set_max_timestamp()) + { + DBUG_RETURN(TRUE); + } + + bitmap_set_bit(write_set, vers_start_field()->field_index); + bitmap_set_bit(write_set, vers_end_field()->field_index); + + DBUG_RETURN(FALSE); +} + /** Reset markers that fields are being updated */ diff --git a/sql/table.h b/sql/table.h index f4ef2f05a9b..0e746d6c5fb 100644 --- a/sql/table.h +++ b/sql/table.h @@ -741,6 +741,38 @@ struct TABLE_SHARE plugin_ref default_part_plugin; #endif + /** + System versioning support. + */ + + bool versioned; + uint16 row_start_field; + uint16 row_end_field; + + void enable_system_versioning(uint16 row_start, uint row_end) + { + versioned = true; + row_start_field = row_start; + row_end_field = row_end; + } + + void disable_system_versioning() + { + versioned = false; + row_start_field = 0; + row_end_field = 0; + } + + Field *vers_start_field() + { + return field[row_start_field]; + } + + Field *vers_end_field() + { + return field[row_end_field]; + } + /** Cache the checked structure of this table. @@ -1051,7 +1083,7 @@ public: uint32 instance; /** Table cache instance this TABLE is belonging to */ THD *in_use; /* Which thread uses this */ - uchar *record[2]; /* Pointer to records */ + uchar *record[3]; /* Pointer to records */ uchar *write_row_record; /* Used as optimisation in THD::write_row */ uchar *insert_values; /* used by INSERT ... UPDATE */ @@ -1084,6 +1116,8 @@ public: Field **default_field; /* Fields with non-constant DEFAULT */ Field *next_number_field; /* Set if next_number is activated */ Field *found_next_number_field; /* Set on open */ + Field **non_generated_field; /* Like **field but without generated + fields */ Virtual_column_info **check_constraints; /* Table's triggers, 0 if there are no of them */ @@ -1431,6 +1465,7 @@ public: int update_virtual_field(Field *vf); int update_virtual_fields(handler *h, enum_vcol_update_mode update_mode); int update_default_fields(bool update, bool ignore_errors); + bool vers_update_fields(); void reset_default_fields(); inline ha_rows stat_records() { return used_stat_records; } @@ -1447,6 +1482,37 @@ public: bool with_cleanup); Field *find_field_by_name(const char *str) const; bool export_structure(THD *thd, class Row_definition_list *defs); + + /** + System versioning support. + */ + + bool versioned() const + { + return s->versioned; + } + + Field *vers_start_field() const + { + DBUG_ASSERT(versioned()); + return field[s->row_start_field]; + } + + Field *vers_end_field() const + { + DBUG_ASSERT(versioned()); + return field[s->row_end_field]; + } + +/** Number of additional fields used in versioned tables */ +#define VERSIONING_FIELDS 2 + + uint user_fields() const + { + return versioned() ? + s->fields - VERSIONING_FIELDS : + s->fields; + } }; @@ -1737,6 +1803,31 @@ class Item_in_subselect; 4) jtbm semi-join (jtbm_subselect != NULL) */ +enum for_system_time_type +{ + FOR_SYSTEM_TIME_UNSPECIFIED, FOR_SYSTEM_TIME_AS_OF, + FOR_SYSTEM_TIME_FROM_TO, FOR_SYSTEM_TIME_BETWEEN +}; + +/** System versioning support. */ +struct system_versioning_for_select +{ + enum for_system_time_type type; + Item *start, *end; + bool is_moved_to_where; + + void init( + const enum for_system_time_type t=FOR_SYSTEM_TIME_UNSPECIFIED, + Item * const s=NULL, + Item * const e=NULL) + { + type= t; + start= s; + end= e; + is_moved_to_where= false; + } +}; + struct LEX; class Index_hint; struct TABLE_LIST @@ -2192,6 +2283,10 @@ struct TABLE_LIST TABLE_LIST *find_underlying_table(TABLE *table); TABLE_LIST *first_leaf_for_name_resolution(); TABLE_LIST *last_leaf_for_name_resolution(); + + /** System versioning support. */ + system_versioning_for_select system_versioning; + /** @brief Find the bottom in the chain of embedded table VIEWs. @@ -2390,6 +2485,9 @@ struct TABLE_LIST void check_pushable_cond_for_table(Item *cond); Item *build_pushable_cond_for_table(THD *thd, Item *cond); + void print_system_versioning(THD *thd, table_map eliminated_tables, + String *str, enum_query_type query_type); + private: bool prep_check_option(THD *thd, uint8 check_opt_type); bool prep_where(THD *thd, Item **conds, bool no_where_clause); diff --git a/sql/unireg.cc b/sql/unireg.cc index 0bb8a4e77c6..0465626c132 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -87,6 +87,72 @@ static uchar *extra2_write(uchar *pos, enum extra2_frm_value_type type, return extra2_write(pos, type, reinterpret_cast(str)); } +static bool +versioned(HA_CREATE_INFO *create_info) +{ + return create_info->versioned(); +} + +static uint16 +get_row_start_field(HA_CREATE_INFO *create_info, List &create_fields) +{ + DBUG_ASSERT(versioned(create_info)); + + List_iterator it(create_fields); + Create_field*sql_field = NULL; + + const System_versioning_info *versioning_info = + create_info->get_system_versioning_info(); + DBUG_ASSERT(versioning_info); + + const char *row_start_field = versioning_info->generated_as_row.start->c_ptr(); + DBUG_ASSERT(row_start_field); + + for (unsigned field_no = 0; (sql_field = it++); ++field_no) + { + if (!my_strcasecmp(system_charset_info, + row_start_field, + sql_field->field_name)) + { + DBUG_ASSERT(field_no <= uint16(~0U)); + return uint16(field_no); + } + } + + DBUG_ASSERT(0); /* Not Reachable */ + return 0; +} + +static uint16 +get_row_end_field(HA_CREATE_INFO *create_info, List &create_fields) +{ + DBUG_ASSERT(versioned(create_info)); + + List_iterator it(create_fields); + Create_field*sql_field = NULL; + + const System_versioning_info *versioning_info = + create_info->get_system_versioning_info(); + DBUG_ASSERT(versioning_info); + + const char *row_end_field = versioning_info->generated_as_row.end->c_ptr(); + DBUG_ASSERT(row_end_field); + + for (unsigned field_no = 0; (sql_field = it++); ++field_no) + { + if (!my_strcasecmp(system_charset_info, + row_end_field, + sql_field->field_name)) + { + DBUG_ASSERT(field_no <= uint16(~0U)); + return uint16(field_no); + } + } + + DBUG_ASSERT(0); /* Not Reachable */ + return 0; +} + /** Create a frm (table definition) file @@ -219,6 +285,10 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table, if (gis_extra2_len) extra2_size+= 1 + (gis_extra2_len > 255 ? 3 : 1) + gis_extra2_len; + if (versioned(create_info)) + { + extra2_size+= 1 + 1 + 2 * sizeof(uint16); + } key_buff_length= uint4korr(fileinfo+47); @@ -275,6 +345,16 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table, } #endif /*HAVE_SPATIAL*/ + if (versioned(create_info)) + { + *pos++= EXTRA2_PERIOD_FOR_SYSTEM_TIME; + *pos++= 2 * sizeof(uint16); + int2store(pos, get_row_start_field(create_info, create_fields)); + pos+= sizeof(uint16); + int2store(pos, get_row_end_field(create_info, create_fields)); + pos+= sizeof(uint16); + } + int4store(pos, filepos); // end of the extra2 segment pos+= 4; diff --git a/sql/unireg.h b/sql/unireg.h index b1cab841092..475945311e4 100644 --- a/sql/unireg.h +++ b/sql/unireg.h @@ -172,6 +172,7 @@ enum extra2_frm_value_type { EXTRA2_TABLEDEF_VERSION=0, EXTRA2_DEFAULT_PART_ENGINE=1, EXTRA2_GIS=2, + EXTRA2_PERIOD_FOR_SYSTEM_TIME=4, #define EXTRA2_ENGINE_IMPORTANT 128 -- cgit v1.2.1 From 8936abcd87acf8b31a605baceeee2a97129dd8b1 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Sat, 17 Sep 2016 14:35:48 +0000 Subject: Delete: code duplication fix --- sql/sql_delete.cc | 47 ++++++++++++++++++++--------------------------- sql/table.h | 2 ++ 2 files changed, 22 insertions(+), 27 deletions(-) (limited to 'sql') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 4983aafe9e9..b739197ca45 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -212,6 +212,22 @@ void Update_plan::save_explain_data_intern(MEM_ROOT *mem_root, } +inline +int TABLE::delete_row() +{ + int error; + if (!versioned()) + error= file->ha_delete_row(record[0]); + else + { + store_record(this, record[1]); + vers_end_field()->set_time(); + error= file->ha_update_row(record[1], record[0]); + } + return error; +} + + /** Implement DELETE SQL word. @@ -587,15 +603,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, break; } - if (!table->versioned()) - error= table->file->ha_delete_row(table->record[0]); - else - { - store_record(table,record[1]); - table->vers_end_field()->set_time(); - error= table->file->ha_update_row(table->record[1], - table->record[0]); - } + error= table->delete_row(); if (!error) { deleted++; @@ -1086,15 +1094,8 @@ int multi_delete::send_data(List &values) TRG_ACTION_BEFORE, FALSE)) DBUG_RETURN(1); table->status|= STATUS_DELETED; - if (!table->versioned()) - error= table->file->ha_delete_row(table->record[0]); - else - { - store_record(table,record[1]); - table->vers_end_field()->set_time(); - error= table->file->ha_update_row(table->record[1], - table->record[0]); - } + + error= table->delete_row(); if (!error) { deleted++; @@ -1275,15 +1276,7 @@ int multi_delete::do_table_deletes(TABLE *table, SORT_INFO *sort_info, break; } - if (!table->versioned()) - local_error= table->file->ha_delete_row(table->record[0]); - else - { - store_record(table,record[1]); - table->vers_end_field()->set_time(); - local_error= table->file->ha_update_row(table->record[1], - table->record[0]); - } + local_error= table->delete_row(); if (local_error && !ignore) { table->file->print_error(local_error, MYF(0)); diff --git a/sql/table.h b/sql/table.h index 0e746d6c5fb..b66f48708eb 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1504,6 +1504,8 @@ public: return field[s->row_end_field]; } + int delete_row(); + /** Number of additional fields used in versioned tables */ #define VERSIONING_FIELDS 2 -- cgit v1.2.1 From 013345d1191cbb98eb6b40997d93570746289c77 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Sun, 18 Sep 2016 04:43:05 +0000 Subject: vers_update_fields: assert instead of return --- sql/table.cc | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) (limited to 'sql') diff --git a/sql/table.cc b/sql/table.cc index 742fad71134..a09772d2f09 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -7568,18 +7568,9 @@ bool TABLE::vers_update_fields() { DBUG_ENTER("vers_update_fields"); - if (!versioned()) - DBUG_RETURN(FALSE); - - if (vers_start_field()->set_time()) - { - DBUG_RETURN(TRUE); - } - - if (vers_end_field()->set_max_timestamp()) - { - DBUG_RETURN(TRUE); - } + DBUG_ASSERT(versioned()); + DBUG_ASSERT(!vers_start_field()->set_time()); + DBUG_ASSERT(!vers_end_field()->set_max_timestamp()); bitmap_set_bit(write_set, vers_start_field()->field_index); bitmap_set_bit(write_set, vers_end_field()->field_index); -- cgit v1.2.1 From d8c8d7b9462e3a1c07c97a4cc8e0cb3e5c2ccbae Mon Sep 17 00:00:00 2001 From: Kosov Eugene Date: Wed, 21 Sep 2016 23:30:52 +0300 Subject: added implicitly generated fields in versioned tables support and refactored code a bit --- sql/handler.cc | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ sql/handler.h | 13 +++++++++++-- sql/sql_lex.h | 5 ++--- sql/sql_parse.cc | 11 +++++++---- sql/sql_yacc.yy | 36 ++++++++++++++++-------------------- 5 files changed, 86 insertions(+), 29 deletions(-) (limited to 'sql') diff --git a/sql/handler.cc b/sql/handler.cc index 5d0ec99e978..b27dc224c48 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -6552,3 +6552,53 @@ int del_global_index_stat(THD *thd, TABLE* table, KEY* key_info) mysql_mutex_unlock(&LOCK_global_index_stats); DBUG_RETURN(res); } + +static bool create_string(MEM_ROOT *mem_root, String **s, const char *value) +{ + *s= new (mem_root) String(value, system_charset_info); + return *s == NULL; +} + +static bool create_sys_trx_field_if_missing(THD *thd, const char *field_name, + Alter_info *alter_info, String **s) +{ + Create_field *f= new (thd->mem_root) Create_field(); + if (!f) + return true; + + f->field_name= field_name; + f->charset= system_charset_info; + f->sql_type= MYSQL_TYPE_TIMESTAMP; + f->length= 6; + f->decimals= 0; + + if (f->check(thd)) + return true; + + if (create_string(thd->mem_root, s, field_name)) + return true; + + alter_info->create_list.push_back(f); + return false; +} + +bool System_versioning_info::add_implicit_fields(THD *thd, + Alter_info *alter_info) +{ + if (!declared_system_versioning) + return false; + + // If user specified some of these he must specify the others too. Do nothing. + if (generated_as_row.start || generated_as_row.end || + period_for_system_time.start || period_for_system_time.end) + return false; + + return create_sys_trx_field_if_missing(thd, "sys_trx_start", alter_info, + &generated_as_row.start) || + create_sys_trx_field_if_missing(thd, "sys_trx_end", alter_info, + &generated_as_row.end) || + create_string(thd->mem_root, &period_for_system_time.start, + "sys_trx_start") || + create_string(thd->mem_root, &period_for_system_time.end, + "sys_trx_end"); +} diff --git a/sql/handler.h b/sql/handler.h index 2b911cd94c5..e88579f9d9d 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -1683,6 +1683,9 @@ struct System_versioning_info set_period_for_system_time(NULL, NULL); } + /** Returns true on failure */ + bool add_implicit_fields(THD *thd, Alter_info *alter_info); + /** User has added 'WITH SYSTEM VERSIONING' to table definition */ bool declared_system_versioning; @@ -1777,11 +1780,17 @@ struct Table_scope_and_contents_source_st : ha_default_handlerton(thd); } - bool versioned() + bool versioned() const { return system_versioning_info.versioned; } - const System_versioning_info *get_system_versioning_info() + const System_versioning_info *get_system_versioning_info() const + { + if (!versioned()) + return NULL; + return &system_versioning_info; + } + System_versioning_info *get_system_versioning_info() { if (!versioned()) return NULL; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index a7fe77ca5a7..9b625d3b7b3 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -3561,10 +3561,9 @@ public: bool add_unit_in_brackets(SELECT_LEX *nselect); void check_automatic_up(enum sub_select_type type); - System_versioning_info *vers_get_info() + System_versioning_info &vers_get_info() { - create_info.system_versioning_info.versioned = true; - return &create_info.system_versioning_info; + return create_info.system_versioning_info; } }; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 5484467509a..9bae9ecc658 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3849,10 +3849,6 @@ mysql_execute_command(THD *thd) copy. */ Alter_info alter_info(lex->alter_info, thd->mem_root); - - if (check_system_versioning(&create_info)) - goto end_with_restore_list; - if (thd->is_fatal_error) { /* If out of memory when creating a copy of alter_info. */ @@ -3860,6 +3856,13 @@ mysql_execute_command(THD *thd) goto end_with_restore_list; } + if (System_versioning_info *info= create_info.get_system_versioning_info()) + if (info->add_implicit_fields(thd, &alter_info)) + goto end_with_restore_list; + + if (check_system_versioning(&create_info)) + goto end_with_restore_list; + /* Check privileges */ if ((res= create_table_precheck(thd, select_tables, create_table))) goto end_with_restore_list; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 6b09055e323..7e8ef6ec8fb 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -5852,10 +5852,9 @@ create_table_option: } | WITH SYSTEM VERSIONING { - System_versioning_info *info = Lex->vers_get_info(); - if (!info) - MYSQL_YYABORT; - info->declared_system_versioning = true; + System_versioning_info &info= Lex->vers_get_info(); + info.declared_system_versioning= true; + info.versioned= true; } ; @@ -6060,15 +6059,14 @@ period_for_system_time: // If FOR_SYM is followed by SYSTEM_TIME_SYM then they are merged to: FOR_SYSTEM_TIME_SYM . PERIOD_SYM FOR_SYSTEM_TIME_SYM '(' period_for_system_time_column_id ',' period_for_system_time_column_id ')' { - System_versioning_info *info = Lex->vers_get_info(); - if (!info) - MYSQL_YYABORT; + System_versioning_info &info= Lex->vers_get_info(); if (!my_strcasecmp(system_charset_info, $4->c_ptr(), $6->c_ptr())) { my_error(ER_SYS_START_AND_SYS_END_SAME, MYF(0), $4->c_ptr()); MYSQL_YYABORT; } - info->set_period_for_system_time($4, $6); + info.set_period_for_system_time($4, $6); + info.versioned= true; } ; @@ -6168,26 +6166,24 @@ field_def: vcol_opt_specifier vcol_opt_attribute | opt_generated_always AS ROW_SYM start_or_end { - System_versioning_info *info = - Lex->vers_get_info(); - if (!info) - MYSQL_YYABORT; - String *field_name = new (thd->mem_root) + System_versioning_info &info= Lex->vers_get_info(); + info.versioned= true; + String *field_name= new (thd->mem_root) String((const char*)Lex->last_field->field_name, system_charset_info); if (!field_name) MYSQL_YYABORT; - String **p = NULL; - int err_nr = 0; + String **p= NULL; + int err_nr= 0; switch ($4) { case 1: - p = &info->generated_as_row.start; - err_nr = ER_SYS_START_MORE_THAN_ONCE; + p= &info.generated_as_row.start; + err_nr= ER_SYS_START_MORE_THAN_ONCE; break; case 0: - p = &info->generated_as_row.end; - err_nr = ER_SYS_END_MORE_THAN_ONCE; + p= &info.generated_as_row.end; + err_nr= ER_SYS_END_MORE_THAN_ONCE; break; default: /* Not Reachable */ @@ -6199,7 +6195,7 @@ field_def: my_error(err_nr, MYF(0), field_name->c_ptr()); MYSQL_YYABORT; } - *p = field_name; + *p= field_name; } ; -- cgit v1.2.1 From bd0b21d22cd76a0f52fde3942721cc2331d4be46 Mon Sep 17 00:00:00 2001 From: Kosov Eugene Date: Thu, 22 Sep 2016 15:03:05 +0300 Subject: SQL: fix for lost code in debug macros --- sql/sql_insert.cc | 13 ++++--------- sql/sql_update.cc | 22 ++++++---------------- sql/table.cc | 11 +++++++---- sql/table.h | 2 +- 4 files changed, 18 insertions(+), 30 deletions(-) (limited to 'sql') diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 4787356c8b6..39a8c61c231 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1029,12 +1029,8 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, } } - if (table->versioned() && - table->vers_update_fields()) - { - error= 1; - break; - } + if (table->versioned()) + table->vers_update_fields(); if ((res= table_list->view_check_option(thd, (values_list.elements == 1 ? @@ -3799,9 +3795,8 @@ int select_insert::send_data(List &values) DBUG_RETURN(0); thd->count_cuted_fields= CHECK_FIELD_WARN; // Calculate cuted fields - if (table->versioned() && - table->vers_update_fields()) - DBUG_RETURN(1); + if (table->versioned()) + table->vers_update_fields(); store_values(values); if (table->default_field && table->update_default_fields(0, info.ignore)) DBUG_RETURN(1); diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 79cabf180a9..da01bd26873 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -763,11 +763,8 @@ int mysql_update(THD *thd, TRG_EVENT_UPDATE)) break; /* purecov: inspected */ - if (table->versioned() && table->vers_update_fields()) - { - error= 1; - break; - } + if (table->versioned()) + table->vers_update_fields(); found++; @@ -2173,12 +2170,8 @@ int multi_update::send_data(List ¬_used_values) if (table->default_field && table->update_default_fields(1, ignore)) DBUG_RETURN(1); - if (table->versioned() && - table->vers_update_fields()) - { - error= 1; - break; - } + if (table->versioned()) + table->vers_update_fields(); if ((error= cur_table->view_check_option(thd, ignore)) != VIEW_CHECK_OK) @@ -2514,11 +2507,8 @@ int multi_update::do_updates() goto err2; } } - if (table->versioned() && - table->vers_update_fields()) - { - goto err2; - } + if (table->versioned()) + table->vers_update_fields(); if ((local_error=table->file->ha_update_row(table->record[1], table->record[0])) && diff --git a/sql/table.cc b/sql/table.cc index a09772d2f09..ca449d628d1 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -7564,18 +7564,21 @@ int TABLE::update_default_fields(bool update_command, bool ignore_errors) DBUG_RETURN(res); } -bool TABLE::vers_update_fields() +void TABLE::vers_update_fields() { DBUG_ENTER("vers_update_fields"); DBUG_ASSERT(versioned()); - DBUG_ASSERT(!vers_start_field()->set_time()); - DBUG_ASSERT(!vers_end_field()->set_max_timestamp()); + bool res= !vers_start_field()->set_time(); + DBUG_ASSERT(res); + res= !vers_end_field()->set_max_timestamp(); + DBUG_ASSERT(res); + (void)res; bitmap_set_bit(write_set, vers_start_field()->field_index); bitmap_set_bit(write_set, vers_end_field()->field_index); - DBUG_RETURN(FALSE); + DBUG_VOID_RETURN; } /** diff --git a/sql/table.h b/sql/table.h index b66f48708eb..7416f27756d 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1465,7 +1465,7 @@ public: int update_virtual_field(Field *vf); int update_virtual_fields(handler *h, enum_vcol_update_mode update_mode); int update_default_fields(bool update, bool ignore_errors); - bool vers_update_fields(); + void vers_update_fields(); void reset_default_fields(); inline ha_rows stat_records() { return used_stat_records; } -- cgit v1.2.1 From 84e1971128156b366ac1f2e8476a8001008e36c7 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Thu, 22 Sep 2016 05:56:34 +0000 Subject: IB: 0.2 part I * SYS_VTQ internal InnoDB table; * I_S.INNODB_SYS_VTQ table; * vers_notify_vtq(): add record to SYS_VTQ on versioned DML; * SYS_VTQ columns filled: TRX_ID, BEGIN_TS. --- sql/handler.h | 1 + sql/sql_class.cc | 6 ++++++ 2 files changed, 7 insertions(+) (limited to 'sql') diff --git a/sql/handler.h b/sql/handler.h index e88579f9d9d..986896cc48b 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -401,6 +401,7 @@ enum enum_alter_inplace_result { #define HA_LEX_CREATE_TMP_TABLE 1U #define HA_CREATE_TMP_ALTER 8U #define HA_LEX_CREATE_SEQUENCE 16U +#define HA_VERSIONED_TABLE 32U #define HA_MAX_REC_LENGTH 65535 diff --git a/sql/sql_class.cc b/sql/sql_class.cc index abd680d749f..0ed78b2dfa8 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -4627,6 +4627,12 @@ extern "C" int thd_rpl_is_parallel(const MYSQL_THD thd) return thd->rgi_slave && thd->rgi_slave->is_parallel_exec; } +/* Returns high resolution timestamp for the start + of the current query. */ +extern "C" time_t thd_start_time(const MYSQL_THD thd) +{ + return thd->start_time; +} /* Returns high resolution timestamp for the start of the current query. */ -- cgit v1.2.1 From 9186cae449eb0fa381b1d3be30e214f6dc5456c6 Mon Sep 17 00:00:00 2001 From: Kosov Eugene Date: Fri, 23 Sep 2016 18:03:02 +0300 Subject: Style: related to DBUG_ASSERT usage --- sql/table.cc | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'sql') diff --git a/sql/table.cc b/sql/table.cc index ca449d628d1..c9729d17d9c 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -7568,12 +7568,10 @@ void TABLE::vers_update_fields() { DBUG_ENTER("vers_update_fields"); - DBUG_ASSERT(versioned()); - bool res= !vers_start_field()->set_time(); - DBUG_ASSERT(res); - res= !vers_end_field()->set_max_timestamp(); - DBUG_ASSERT(res); - (void)res; + if (vers_start_field()->set_time()) + DBUG_ASSERT(0); + if (vers_end_field()->set_max_timestamp()) + DBUG_ASSERT(0); bitmap_set_bit(write_set, vers_start_field()->field_index); bitmap_set_bit(write_set, vers_end_field()->field_index); -- cgit v1.2.1 From 87507451e7beb3ae76f711f2796c407cbf7090f7 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Fri, 23 Sep 2016 16:01:14 +0000 Subject: SQL: fractions in I_S TIMESTAMP fields --- sql/item.h | 4 ++-- sql/sql_show.cc | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'sql') diff --git a/sql/item.h b/sql/item.h index 7eea60e8213..e5a3215d13e 100644 --- a/sql/item.h +++ b/sql/item.h @@ -3622,10 +3622,10 @@ class Item_return_date_time :public Item_partition_func_safe_string enum_field_types date_time_field_type; public: Item_return_date_time(THD *thd, const char *name_arg, uint length_arg, - enum_field_types field_type_arg): + enum_field_types field_type_arg, uint dec_arg= 0): Item_partition_func_safe_string(thd, name_arg, length_arg, &my_charset_bin), date_time_field_type(field_type_arg) - { decimals= 0; } + { decimals= dec_arg; } enum_field_types field_type() const { return date_time_field_type; } }; diff --git a/sql/sql_show.cc b/sql/sql_show.cc index e3613c1e0e7..084e98b143f 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -7730,7 +7730,8 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list) if (!(item=new (mem_root) Item_return_date_time(thd, fields_info->field_name, strlen(fields_info->field_name), - fields_info->field_type))) + fields_info->field_type, + fields_info->field_length))) DBUG_RETURN(0); item->decimals= fields_info->field_length; break; -- cgit v1.2.1 From bdb12d149992910e54051c8eb800ff2610712987 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Mon, 26 Sep 2016 08:37:53 +0000 Subject: IB: 0.2 part II * moved vers_notify_vtq() to commit phase; * low_level insert (load test passed); * rest of SYS_VTQ columns filled: COMMIT_TS, CONCURR_TRX; * savepoints support; * I_S.INNODB_SYS_VTQ adjustments: - limit to I_S_SYS_VTQ_LIMIT(10000) of most recent records; - CONCURR_TRX limit to I_S_MAX_CONCURR_TRX(100) with '...' truncation marker; - TIMESTAMP fields show fractions of seconds. --- sql/sql_time.cc | 13 +++++++++++++ sql/sql_time.h | 10 ++++++++++ 2 files changed, 23 insertions(+) (limited to 'sql') diff --git a/sql/sql_time.cc b/sql/sql_time.cc index cad4bae03e8..6f37f97feef 100644 --- a/sql/sql_time.cc +++ b/sql/sql_time.cc @@ -475,6 +475,19 @@ void localtime_to_TIME(MYSQL_TIME *to, struct tm *from) to->second= (int) from->tm_sec; } + +/* + Convert seconds since Epoch to TIME +*/ + +void unix_time_to_TIME(MYSQL_TIME *to, time_t secs, suseconds_t usecs) +{ + struct tm tm_time; + localtime_r(&secs, &tm_time); + localtime_to_TIME(to, &tm_time); + to->second_part = usecs; +} + void calc_time_from_sec(MYSQL_TIME *to, long seconds, long microseconds) { long t_seconds; diff --git a/sql/sql_time.h b/sql/sql_time.h index e0cab5cfa66..fd0c0273dcf 100644 --- a/sql/sql_time.h +++ b/sql/sql_time.h @@ -171,6 +171,16 @@ bool calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2, int lsign, MYSQL_TIME *l_time3, ulonglong fuzzydate); int my_time_compare(const MYSQL_TIME *a, const MYSQL_TIME *b); void localtime_to_TIME(MYSQL_TIME *to, struct tm *from); +void unix_time_to_TIME(MYSQL_TIME *to, time_t secs, suseconds_t usecs); + +inline +longlong unix_time_to_packed(time_t secs, suseconds_t usecs) +{ + MYSQL_TIME mysql_time; + unix_time_to_TIME(&mysql_time, secs, usecs); + return pack_time(&mysql_time); +} + void calc_time_from_sec(MYSQL_TIME *to, long seconds, long microseconds); uint calc_week(MYSQL_TIME *l_time, uint week_behaviour, uint *year); -- cgit v1.2.1 From 3b64fed50401a461101b7d3d603b5cce846e3698 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Wed, 28 Sep 2016 08:56:02 +0000 Subject: Style: renamed prepare_keys_for_sys_ver() --- sql/sql_table.cc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'sql') diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 3913b14ccc8..620f580d688 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -4341,14 +4341,13 @@ bool Column_definition::sp_prepare_create_field(THD *thd, MEM_ROOT *mem_root) static bool -prepare_keys_for_sys_ver(THD *thd, +vers_prepare_keys(THD *thd, HA_CREATE_INFO *create_info, Alter_info *alter_info, KEY **key_info, uint key_count) { - if (!create_info->versioned()) - return false; + DBUG_ASSERT(create_info->versioned()); const System_versioning_info *versioning_info= create_info->get_system_versioning_info(); @@ -4645,7 +4644,7 @@ handler *mysql_create_frm_image(THD *thd, goto err; } } - if(prepare_keys_for_sys_ver(thd, create_info, alter_info, key_info, + if(vers_prepare_keys(thd, create_info, alter_info, key_info, *key_count)) goto err; } -- cgit v1.2.1 From 1ec7dbe1766f00c542971784b967cb6a738e3a57 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Thu, 29 Sep 2016 11:12:46 +0000 Subject: IB: 0.2 part III * versioned DML: INSERT, UPDATE, DELETE; * general refactoring and fixes. Warning: breaks 'insert' and 'update' tests since they require part IV. --- sql/field.cc | 24 ++++++++++--- sql/field.h | 22 ++++++------ sql/handler.h | 12 +++++++ sql/share/errmsg-utf8.txt | 6 ++++ sql/sql_base.cc | 4 +-- sql/sql_delete.cc | 10 +++--- sql/sql_insert.cc | 49 ++++++++++++++----------- sql/sql_insert.h | 2 +- sql/sql_select.cc | 4 +-- sql/sql_show.cc | 4 +-- sql/sql_update.cc | 71 ++++++++++++++++++------------------ sql/table.cc | 92 +++++++++++++++++++++++++++++------------------ sql/table.h | 17 ++++++--- 13 files changed, 195 insertions(+), 122 deletions(-) (limited to 'sql') diff --git a/sql/field.cc b/sql/field.cc index cb91e93ec5e..604965be254 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -4356,6 +4356,20 @@ void Field_longlong::sql_type(String &res) const add_zerofill_and_unsigned(res); } +bool Field_longlong::set_max() +{ + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + int8store(ptr, ULONGLONG_MAX); + return FALSE; +} + +bool Field_longlong::is_max() +{ + ASSERT_COLUMN_MARKED_FOR_READ; + ulonglong j; + j = sint8korr(ptr); + return j == ULONGLONG_MAX; +} /* Floating-point numbers @@ -5423,9 +5437,10 @@ void Field_timestampf::store_TIME(my_time_t timestamp, ulong sec_part) my_timestamp_to_binary(&tm, ptr, dec); } -bool Field_timestampf::set_max_timestamp() +bool Field_timestampf::set_max() { - DBUG_ENTER("Field_timestampf::set_max_timestamp"); + DBUG_ENTER("Field_timestampf::set_max"); + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; mi_int4store(ptr, 0x7fffffff); memset(ptr + 4, 0x0, value_length() - 4); @@ -5433,9 +5448,10 @@ bool Field_timestampf::set_max_timestamp() DBUG_RETURN(FALSE); } -bool Field_timestampf::is_max_timestamp() +bool Field_timestampf::is_max() { - DBUG_ENTER("Field_timestampf::is_max_timestamp"); + DBUG_ENTER("Field_timestampf::is_max"); + ASSERT_COLUMN_MARKED_FOR_READ; DBUG_RETURN(mi_sint4korr(ptr) == 0x7fffffff); } diff --git a/sql/field.h b/sql/field.h index 3513a773ef4..4b6607fa099 100644 --- a/sql/field.h +++ b/sql/field.h @@ -677,17 +677,16 @@ public: { DBUG_ASSERT(0); } /** - Is used by System Versioning. + Used by System Versioning. */ - virtual bool set_max_timestamp() { - return true; - } + virtual bool set_max() + { DBUG_ASSERT(0); return false; } + /** - Is used by System Versioning. + Used by System Versioning. */ - virtual bool is_max_timestamp() { - return false; - } + virtual bool is_max() + { DBUG_ASSERT(0); return false; } uchar *ptr; // Position to field in record /** @@ -2173,6 +2172,9 @@ public: { return unpack_int64(to, from, from_end); } + + bool set_max(); + bool is_max(); }; @@ -2582,8 +2584,8 @@ public: { return memcmp(a_ptr, b_ptr, pack_length()); } - virtual bool set_max_timestamp(); - virtual bool is_max_timestamp(); + bool set_max(); + bool is_max(); void store_TIME(my_time_t timestamp, ulong sec_part); my_time_t get_timestamp(const uchar *pos, ulong *sec_part) const; uint size_of() const { return sizeof(*this); } diff --git a/sql/handler.h b/sql/handler.h index 986896cc48b..b50e1b82ed2 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -1385,6 +1385,11 @@ struct handlerton */ int (*discover_table_structure)(handlerton *hton, THD* thd, TABLE_SHARE *share, HA_CREATE_INFO *info); + + /* + Engine supports System Versioning + */ + bool versioned(); }; @@ -1432,6 +1437,7 @@ handlerton *ha_default_tmp_handlerton(THD *thd); */ #define HTON_NO_BINLOG_ROW_OPT (1 << 9) #define HTON_SUPPORTS_EXTENDED_KEYS (1 <<10) //supports extended keys +#define HTON_SUPPORTS_SYS_VERSIONING (1 << 11) //Engine supports System Versioning // MySQL compatibility. Unused. #define HTON_SUPPORTS_FOREIGN_KEYS (1 << 0) //Foreign key constraint supported. @@ -4485,4 +4491,10 @@ void print_keydup_error(TABLE *table, KEY *key, myf errflag); int del_global_index_stat(THD *thd, TABLE* table, KEY* key_info); int del_global_table_stat(THD *thd, LEX_STRING *db, LEX_STRING *table); + +inline +bool handlerton::versioned() +{ + return flags & HTON_SUPPORTS_SYS_VERSIONING; +} #endif /* HANDLER_INCLUDED */ diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 380d992a89b..f3859b5ba4a 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7531,3 +7531,9 @@ ER_SYS_START_FIELD_MUST_BE_TIMESTAMP ER_SYS_END_FIELD_MUST_BE_TIMESTAMP eng "System end field must be of type TIMESTAMP" + +ER_SYS_START_FIELD_MUST_BE_BIGINT + eng "System start field must be of type BIGINT UNSIGNED" + +ER_SYS_END_FIELD_MUST_BE_BIGINT + eng "System end field must be of type BIGINT UNSIGNED" diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 1af45c12331..89f1798d36d 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -7962,7 +7962,7 @@ fill_record(THD *thd, TABLE *table_arg, List &fields, List &values, ER_THD(thd, ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN), rfield->field_name, table->s->table_name.str); } - if (table->versioned() && rfield->is_generated() && + if (table->versioned_by_sql() && rfield->is_generated() && !ignore_errors) { my_error(ER_GENERATED_FIELD_CANNOT_BE_SET_BY_USER, MYF(0)); @@ -8216,7 +8216,7 @@ fill_record(THD *thd, TABLE *table, Field **ptr, List &values, } } - if (table->versioned() && field->is_generated() && + if (table->versioned_by_sql() && field->is_generated() && !ignore_errors) { my_error(ER_GENERATED_FIELD_CANNOT_BE_SET_BY_USER, MYF(0)); diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index b739197ca45..5af9b326eba 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -216,7 +216,7 @@ inline int TABLE::delete_row() { int error; - if (!versioned()) + if (!versioned_by_sql()) error= file->ha_delete_row(record[0]); else { @@ -360,7 +360,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (!with_select && !using_limit && const_cond_result && (!thd->is_current_stmt_binlog_format_row() && !(table->triggers && table->triggers->has_delete_triggers())) - && !table->versioned()) + && !table->versioned_by_sql()) { /* Update the table->file->stats.records number */ table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); @@ -576,8 +576,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, while (!(error=info.read_record(&info)) && !thd->killed && ! thd->is_error()) { - if (table->versioned() && - !table->vers_end_field()->is_max_timestamp()) + if (table->versioned() && !table->vers_end_field()->is_max()) { continue; } @@ -1076,8 +1075,7 @@ int multi_delete::send_data(List &values) if (table->status & (STATUS_NULL_ROW | STATUS_DELETED)) continue; - if (table->versioned() && - !table->vers_end_field()->is_max_timestamp()) + if (table->versioned() && !table->vers_end_field()->is_max()) { continue; } diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 39a8c61c231..4a588ea0cc2 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -222,7 +222,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list, table_list->view_db.str, table_list->view_name.str); DBUG_RETURN(-1); } - if (values.elements != table->user_fields()) + if (values.elements != table->vers_user_fields()) { my_error(ER_WRONG_VALUE_COUNT_ON_ROW, MYF(0), 1L); DBUG_RETURN(-1); @@ -1029,7 +1029,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, } } - if (table->versioned()) + if (table->versioned_by_sql()) table->vers_update_fields(); if ((res= table_list->view_check_option(thd, @@ -1558,7 +1558,7 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, if (!table) table= table_list->table; - if (table->versioned() && duplic == DUP_REPLACE) + if (table->versioned_by_sql() && duplic == DUP_REPLACE) { // Additional memory may be required to create historical items. if (table_list->set_insert_values(thd->mem_root)) @@ -1622,22 +1622,16 @@ static int last_uniq_key(TABLE *table,uint keynr) sets Sys_end to now() and calls ha_write_row() . */ -int vers_insert_history_row(TABLE *table, ha_rows *inserted) +int vers_insert_history_row(TABLE *table) { - DBUG_ASSERT(table->versioned()); + DBUG_ASSERT(table->versioned_by_sql()); restore_record(table,record[1]); // Set Sys_end to now() if (table->vers_end_field()->set_time()) - { - return 1; - } - - const int error= table->file->ha_write_row(table->record[0]); - if (!error) - ++*inserted; + DBUG_ASSERT(0); - return error; + return table->file->ha_write_row(table->record[0]); } /* @@ -1846,9 +1840,20 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) if (error != HA_ERR_RECORD_IS_THE_SAME) { info->updated++; - if (table->versioned() && - (error=vers_insert_history_row(table, &info->copied))) - goto err; + if (table->versioned()) + { + if (table->versioned_by_sql()) + { + store_record(table, record[2]); + if ((error= vers_insert_history_row(table))) + { + restore_record(table, record[2]); + goto err; + } + restore_record(table, record[2]); + } + info->copied++; + } } else error= 0; @@ -1907,7 +1912,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) if (last_uniq_key(table,key_nr) && !table->file->referenced_by_foreign_key() && (!table->triggers || !table->triggers->has_delete_triggers()) && - !table->versioned()) + !table->versioned_by_sql()) { if ((error=table->file->ha_update_row(table->record[1], table->record[0])) && @@ -1931,7 +1936,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) TRG_ACTION_BEFORE, TRUE)) goto before_trg_err; - if (!table->versioned()) + if (!table->versioned_by_sql()) error= table->file->ha_delete_row(table->record[1]); else { @@ -1949,7 +1954,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) } if (error) goto err; - if (!table->versioned()) + if (!table->versioned_by_sql()) info->deleted++; else info->updated++; @@ -2040,7 +2045,9 @@ int check_that_all_fields_are_given_values(THD *thd, TABLE *entry, TABLE_LIST *t for (Field **field=entry->field ; *field ; field++) { if (!bitmap_is_set(write_set, (*field)->field_index) && - has_no_default_value(thd, *field, table_list)) + has_no_default_value(thd, *field, table_list) && + !((*field)->flags & (GENERATED_ROW_START_FLAG | GENERATED_ROW_END_FLAG)) && + ((*field)->real_type() != MYSQL_TYPE_ENUM)) err=1; } return thd->abort_on_warning ? err : 0; @@ -3795,7 +3802,7 @@ int select_insert::send_data(List &values) DBUG_RETURN(0); thd->count_cuted_fields= CHECK_FIELD_WARN; // Calculate cuted fields - if (table->versioned()) + if (table->versioned_by_sql()) table->vers_update_fields(); store_values(values); if (table->default_field && table->update_default_fields(0, info.ignore)) diff --git a/sql/sql_insert.h b/sql/sql_insert.h index a8794e7414b..6efd680d188 100644 --- a/sql/sql_insert.h +++ b/sql/sql_insert.h @@ -37,7 +37,7 @@ void upgrade_lock_type_for_insert(THD *thd, thr_lock_type *lock_type, bool is_multi_insert); int check_that_all_fields_are_given_values(THD *thd, TABLE *entry, TABLE_LIST *table_list); -int vers_insert_history_row(TABLE *table, ha_rows *inserted); +int vers_insert_history_row(TABLE *table); int write_record(THD *thd, TABLE *table, COPY_INFO *info); void kill_delayed_threads(void); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 17e36390146..94f8d78fa74 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -676,7 +676,7 @@ setup_for_system_time(THD *thd, TABLE_LIST *tables, COND **conds, SELECT_LEX *se for (table= tables; table; table= table->next_local) { - if (table->table && table->table->versioned()) + if (table->table && table->table->versioned_by_sql()) versioned_tables++; else if (table->system_versioning.type != FOR_SYSTEM_TIME_UNSPECIFIED) { @@ -690,7 +690,7 @@ setup_for_system_time(THD *thd, TABLE_LIST *tables, COND **conds, SELECT_LEX *se for (table= tables; table; table= table->next_local) { - if (table->table && table->table->versioned()) + if (table->table && table->table->versioned_by_sql()) { Field *fstart= table->table->vers_start_field(); Field *fend= table->table->vers_end_field(); diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 084e98b143f..2bcf0199429 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2224,7 +2224,7 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, hton->index_options); } - if (table->versioned()) + if (table->versioned_by_sql()) { const Field *fs = table->vers_start_field(); const Field *fe = table->vers_end_field(); @@ -2273,7 +2273,7 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, add_table_options(thd, table, create_info_arg, table_list->schema_table != 0, 0, packet); - if (table->versioned()) + if (table->versioned_by_sql()) { packet->append(STRING_WITH_LEN(" WITH SYSTEM VERSIONING")); } diff --git a/sql/sql_update.cc b/sql/sql_update.cc index da01bd26873..acbbf559c63 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -744,7 +744,7 @@ int mysql_update(THD *thd, while (!(error=info.read_record(&info)) && !thd->killed) { - if (table->versioned() && !table->vers_end_field()->is_max_timestamp()) + if (table->versioned() && !table->vers_end_field()->is_max()) { continue; } @@ -763,7 +763,7 @@ int mysql_update(THD *thd, TRG_EVENT_UPDATE)) break; /* purecov: inspected */ - if (table->versioned()) + if (table->versioned_by_sql()) table->vers_update_fields(); found++; @@ -837,11 +837,17 @@ int mysql_update(THD *thd, if (table->versioned()) { - store_record(table, record[2]); - if ((error = vers_insert_history_row(table, &updated_sys_ver))) - break; - - restore_record(table, record[2]); + if (table->versioned_by_sql()) + { + store_record(table, record[2]); + if ((error = vers_insert_history_row(table))) + { + restore_record(table, record[2]); + break; + } + restore_record(table, record[2]); + } + updated_sys_ver++; } } else if (!ignore || @@ -1036,7 +1042,7 @@ int mysql_update(THD *thd, if (error < 0 && !thd->lex->analyze_stmt) { char buff[MYSQL_ERRMSG_SIZE]; - if (!table->versioned()) + if (!table->versioned_by_sql()) my_snprintf(buff, sizeof(buff), ER_THD(thd, ER_UPDATE_INFO), (ulong) found, (ulong) updated, (ulong) thd->get_stmt_da()->current_statement_warn_count()); @@ -2134,7 +2140,7 @@ int multi_update::send_data(List ¬_used_values) if (table->status & (STATUS_NULL_ROW | STATUS_UPDATED)) continue; - if (table->versioned() && !table->vers_end_field()->is_max_timestamp()) + if (table->versioned() && !table->vers_end_field()->is_max()) { continue; } @@ -2170,7 +2176,7 @@ int multi_update::send_data(List ¬_used_values) if (table->default_field && table->update_default_fields(1, ignore)) DBUG_RETURN(1); - if (table->versioned()) + if (table->versioned_by_sql()) table->vers_update_fields(); if ((error= cur_table->view_check_option(thd, ignore)) != @@ -2222,20 +2228,18 @@ int multi_update::send_data(List ¬_used_values) } else if (table->versioned()) { - restore_record(table,record[1]); - - // Set end time to now() - if (table->vers_end_field()->set_time()) - { - error= 1; - break; - } - - if ( (error= vers_insert_history_row(table, &updated_sys_ver)) ) + if (table->versioned_by_sql()) { - error= 1; - break; + store_record(table, record[2]); + if (vers_insert_history_row(table)) + { + restore_record(table, record[2]); + error= 1; + break; + } + restore_record(table, record[2]); } + updated_sys_ver++; } /* non-transactional or transactional table got modified */ /* either multi_update class' flag is raised in its branch */ @@ -2507,7 +2511,7 @@ int multi_update::do_updates() goto err2; } } - if (table->versioned()) + if (table->versioned_by_sql()) table->vers_update_fields(); if ((local_error=table->file->ha_update_row(table->record[1], @@ -2527,19 +2531,18 @@ int multi_update::do_updates() if (table->versioned()) { - restore_record(table,record[1]); - - // Set end time to now() - if (table->vers_end_field()->set_time()) - { - goto err2; - } - - if ( (local_error= vers_insert_history_row(table, &updated_sys_ver)) ) + if (table->versioned_by_sql()) { - err_table = table; - goto err; + store_record(table, record[2]); + if ((local_error= vers_insert_history_row(table))) + { + restore_record(table, record[2]); + err_table = table; + goto err; + } + restore_record(table, record[2]); } + updated_sys_ver++; } } else diff --git a/sql/table.cc b/sql/table.cc index c9729d17d9c..80a7046be1a 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -2477,35 +2477,6 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, } } - /* Set system versioning information. */ - if (system_period == NULL) - { - share->disable_system_versioning(); - } - else - { - DBUG_PRINT("info", ("Setting system versioning informations")); - uint16 row_start = uint2korr(system_period); - uint16 row_end = uint2korr(system_period + sizeof(uint16)); - if (row_start >= share->fields || row_end >= share->fields) - goto err; - DBUG_PRINT("info", ("Columns with system versioning: [%d, %d]", row_start, row_end)); - share->enable_system_versioning(row_start, row_end); - vers_start_field()->set_generated_row_start(); - vers_end_field()->set_generated_row_end(); - - if (vers_start_field()->type() != MYSQL_TYPE_TIMESTAMP) - { - my_error(ER_SYS_START_FIELD_MUST_BE_TIMESTAMP, MYF(0), share->table_name); - goto err; - } - if (vers_end_field()->type() != MYSQL_TYPE_TIMESTAMP) - { - my_error(ER_SYS_END_FIELD_MUST_BE_TIMESTAMP, MYF(0), share->table_name); - goto err; - } - } - /* the correct null_bytes can now be set, since bitfields have been taken into account @@ -2547,19 +2518,70 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, bitmap_clear_all(share->check_set); } - delete handler_file; #ifndef DBUG_OFF if (use_hash) (void) my_hash_check(&share->name_hash); #endif share->db_plugin= se_plugin; + + /* Set system versioning information. */ + if (system_period == NULL) + { + share->disable_system_versioning(); + } + else + { + DBUG_PRINT("info", ("Setting system versioning informations")); + uint16 row_start = uint2korr(system_period); + uint16 row_end = uint2korr(system_period + sizeof(uint16)); + if (row_start >= share->fields || row_end >= share->fields) + goto err; + DBUG_PRINT("info", ("Columns with system versioning: [%d, %d]", row_start, row_end)); + share->enable_system_versioning(row_start, row_end); + vers_start_field()->set_generated_row_start(); + vers_end_field()->set_generated_row_end(); + + DBUG_ASSERT(db_type()); + if (db_type()->versioned()) + { + if (vers_start_field()->type() != MYSQL_TYPE_LONGLONG + || !(vers_start_field()->flags & UNSIGNED_FLAG)) + { + my_error(ER_SYS_START_FIELD_MUST_BE_BIGINT, MYF(0), share->table_name); + goto err; + } + if (vers_end_field()->type() != MYSQL_TYPE_LONGLONG + || !(vers_end_field()->flags & UNSIGNED_FLAG)) + { + my_error(ER_SYS_END_FIELD_MUST_BE_BIGINT, MYF(0), share->table_name); + goto err; + } + } + else + { + if (vers_start_field()->type() != MYSQL_TYPE_TIMESTAMP) + { + my_error(ER_SYS_START_FIELD_MUST_BE_TIMESTAMP, MYF(0), share->table_name); + goto err; + } + if (vers_end_field()->type() != MYSQL_TYPE_TIMESTAMP) + { + my_error(ER_SYS_END_FIELD_MUST_BE_TIMESTAMP, MYF(0), share->table_name); + goto err; + } + } // if (db_type()->versioned()) + } // if (system_period == NULL) + + delete handler_file; + share->error= OPEN_FRM_OK; thd->status_var.opened_shares++; thd->mem_root= old_root; DBUG_RETURN(0); - err: +err: + share->db_plugin= NULL; share->error= OPEN_FRM_CORRUPTED; share->open_errno= my_errno; delete handler_file; @@ -7568,14 +7590,14 @@ void TABLE::vers_update_fields() { DBUG_ENTER("vers_update_fields"); + bitmap_set_bit(write_set, vers_start_field()->field_index); + bitmap_set_bit(write_set, vers_end_field()->field_index); + if (vers_start_field()->set_time()) DBUG_ASSERT(0); - if (vers_end_field()->set_max_timestamp()) + if (vers_end_field()->set_max()) DBUG_ASSERT(0); - bitmap_set_bit(write_set, vers_start_field()->field_index); - bitmap_set_bit(write_set, vers_end_field()->field_index); - DBUG_VOID_RETURN; } diff --git a/sql/table.h b/sql/table.h index 7416f27756d..ad62c06838b 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1484,7 +1484,7 @@ public: bool export_structure(THD *thd, class Row_definition_list *defs); /** - System versioning support. + System Versioning support */ bool versioned() const @@ -1492,15 +1492,22 @@ public: return s->versioned; } + /* Versioned by SQL layer */ + bool versioned_by_sql() const + { + DBUG_ASSERT(s->db_type()); + return s->versioned && !s->db_type()->versioned(); + } + Field *vers_start_field() const { - DBUG_ASSERT(versioned()); + DBUG_ASSERT(s->versioned); return field[s->row_start_field]; } Field *vers_end_field() const { - DBUG_ASSERT(versioned()); + DBUG_ASSERT(s->versioned); return field[s->row_end_field]; } @@ -1509,9 +1516,9 @@ public: /** Number of additional fields used in versioned tables */ #define VERSIONING_FIELDS 2 - uint user_fields() const + uint vers_user_fields() const { - return versioned() ? + return s->versioned ? s->fields - VERSIONING_FIELDS : s->fields; } -- cgit v1.2.1 From a72259353a1520dab2b5fdda84224e7ff21c0993 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Mon, 3 Oct 2016 02:48:08 +0000 Subject: Cleanup: garbage hunk --- sql/sql_select.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 94f8d78fa74..db50d471183 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -588,7 +588,7 @@ void remove_redundant_subquery_clauses(st_select_lex *subq_select_lex) subq_select_lex->group_list.empty(); DBUG_PRINT("info", ("GROUP BY removed")); } - + /* TODO: This would prevent processing quries with ORDER BY ... LIMIT therefore we disable this optimization for now. -- cgit v1.2.1 From 78c5d1d79c1ea155189b1ed6a59aab8d635d1535 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Mon, 3 Oct 2016 07:03:04 +0000 Subject: SQL: respect signed in set_max(), is_max() --- sql/field.cc | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'sql') diff --git a/sql/field.cc b/sql/field.cc index 604965be254..78ff1fded5e 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -4359,16 +4359,22 @@ void Field_longlong::sql_type(String &res) const bool Field_longlong::set_max() { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; - int8store(ptr, ULONGLONG_MAX); + int8store(ptr, unsigned_flag ? ULONGLONG_MAX : LONGLONG_MAX); return FALSE; } bool Field_longlong::is_max() { ASSERT_COLUMN_MARKED_FOR_READ; - ulonglong j; - j = sint8korr(ptr); - return j == ULONGLONG_MAX; + if (unsigned_flag) + { + ulonglong j; + j= uint8korr(ptr); + return j == ULONGLONG_MAX; + } + longlong j; + j= sint8korr(ptr); + return j == LONGLONG_MAX; } /* -- cgit v1.2.1 From a7df73063642ab5a788b36f175a748a17f63670b Mon Sep 17 00:00:00 2001 From: Kosov Eugene Date: Tue, 4 Oct 2016 15:56:06 +0300 Subject: SQL: fix timestamp type for generated fields --- sql/handler.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/handler.cc b/sql/handler.cc index b27dc224c48..0c2dea8b00e 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -6568,7 +6568,7 @@ static bool create_sys_trx_field_if_missing(THD *thd, const char *field_name, f->field_name= field_name; f->charset= system_charset_info; - f->sql_type= MYSQL_TYPE_TIMESTAMP; + f->sql_type= MYSQL_TYPE_TIMESTAMP2; f->length= 6; f->decimals= 0; -- cgit v1.2.1 From f13bf7178d9d924634762d0bb52d658e5d341451 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 11 Oct 2016 06:14:45 +0000 Subject: Parser: expressions instead string literals in TIMESTAMP clauses --- sql/sql_yacc.yy | 32 ++++++++------------------------ 1 file changed, 8 insertions(+), 24 deletions(-) (limited to 'sql') diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 7e8ef6ec8fb..f681ed25596 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -8669,13 +8669,9 @@ opt_for_system_time_clause: } | FOR_SYSTEM_TIME_SYM AS OF_SYM - TIMESTAMP TEXT_STRING + TIMESTAMP simple_expr { - Item *item= create_temporal_literal(thd, $5.str, $5.length, YYCSCL, - MYSQL_TYPE_DATETIME, true); - if (item == NULL) - MYSQL_YYABORT; - $$.init(FOR_SYSTEM_TIME_AS_OF, item); + $$.init(FOR_SYSTEM_TIME_AS_OF, $5); } | FOR_SYSTEM_TIME_SYM AS OF_SYM @@ -8688,31 +8684,19 @@ opt_for_system_time_clause: } | FOR_SYSTEM_TIME_SYM FROM - TIMESTAMP TEXT_STRING + TIMESTAMP simple_expr TO_SYM - TIMESTAMP TEXT_STRING + TIMESTAMP simple_expr { - Item *item1= create_temporal_literal(thd, $4.str, $4.length, YYCSCL, - MYSQL_TYPE_DATETIME, true); - Item *item2= create_temporal_literal(thd, $7.str, $7.length, YYCSCL, - MYSQL_TYPE_DATETIME, true); - if (item1 == NULL || item2 == NULL) - MYSQL_YYABORT; - $$.init(FOR_SYSTEM_TIME_FROM_TO, item1, item2); + $$.init(FOR_SYSTEM_TIME_FROM_TO, $4, $7); } | FOR_SYSTEM_TIME_SYM BETWEEN_SYM - TIMESTAMP TEXT_STRING + TIMESTAMP simple_expr AND_SYM - TIMESTAMP TEXT_STRING + TIMESTAMP simple_expr { - Item *item1= create_temporal_literal(thd, $4.str, $4.length, YYCSCL, - MYSQL_TYPE_DATETIME, true); - Item *item2= create_temporal_literal(thd, $7.str, $7.length, YYCSCL, - MYSQL_TYPE_DATETIME, true); - if (item1 == NULL || item2 == NULL) - MYSQL_YYABORT; - $$.init(FOR_SYSTEM_TIME_BETWEEN, item1, item2); + $$.init(FOR_SYSTEM_TIME_BETWEEN, $4, $7); } ; -- cgit v1.2.1 From 53a892fcfd5aa1551f0284767186632b9560d838 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Fri, 30 Sep 2016 13:15:08 +0000 Subject: IB: 0.2 part IV * BEGIN_TS(), COMMIT_TS() SQL functions; * VTQ instead of packed stores secs + usecs like my_timestamp_to_binary() does; * versioned SELECT to IB is translated with COMMIT_TS(); * SQL fixes: - FOR_SYSTEM_TIME_UNSPECIFIED condition compares to TIMESTAMP_MAX_VALUE; - segfault fix #36: multiple execute of prepared stmt; - different tables to same stored procedure fix (#39) * Fixes of previous parts: ON DUPLICATE KEY, other misc fixes. --- sql/field.cc | 2 +- sql/handler.h | 8 ++-- sql/item.cc | 1 + sql/item.h | 2 +- sql/item_create.cc | 88 ++++++++++++++++++++++++++++++++++++++++ sql/item_timefunc.cc | 68 +++++++++++++++++++++++++++++++ sql/item_timefunc.h | 22 ++++++++++ sql/sp_head.cc | 1 + sql/sql_base.cc | 4 +- sql/sql_class.cc | 6 +++ sql/sql_class.h | 11 ++++- sql/sql_lex.cc | 1 + sql/sql_lex.h | 1 + sql/sql_plugin.cc | 14 +++++++ sql/sql_plugin.h | 2 + sql/sql_select.cc | 111 ++++++++++++++++++++++++++++++++++++++++++--------- sql/sql_show.cc | 4 +- sql/sql_time.cc | 12 ------ sql/sql_time.h | 9 ----- sql/tztime.h | 1 + sql/vtq.h | 25 ++++++++++++ 21 files changed, 342 insertions(+), 51 deletions(-) create mode 100755 sql/vtq.h (limited to 'sql') diff --git a/sql/field.cc b/sql/field.cc index 78ff1fded5e..c22ae9e2df3 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -5448,7 +5448,7 @@ bool Field_timestampf::set_max() DBUG_ENTER("Field_timestampf::set_max"); ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; - mi_int4store(ptr, 0x7fffffff); + mi_int4store(ptr, TIMESTAMP_MAX_VALUE); memset(ptr + 4, 0x0, value_length() - 4); DBUG_RETURN(FALSE); diff --git a/sql/handler.h b/sql/handler.h index b50e1b82ed2..8b7b6f516a1 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -35,6 +35,7 @@ #include "structs.h" /* SHOW_COMP_OPTION */ #include "sql_array.h" /* Dynamic_array<> */ #include "mdl.h" +#include "vtq.h" #include "sql_analyze_stmt.h" // for Exec_time_tracker @@ -1387,9 +1388,10 @@ struct handlerton TABLE_SHARE *share, HA_CREATE_INFO *info); /* - Engine supports System Versioning + System Versioning */ - bool versioned(); + bool versioned() const; + bool (*vers_get_vtq_ts)(THD* thd, MYSQL_TIME *out, ulonglong trx_id, vtq_field_t field); }; @@ -4493,7 +4495,7 @@ int del_global_index_stat(THD *thd, TABLE* table, KEY* key_info); int del_global_table_stat(THD *thd, LEX_STRING *db, LEX_STRING *table); inline -bool handlerton::versioned() +bool handlerton::versioned() const { return flags & HTON_SUPPORTS_SYS_VERSIONING; } diff --git a/sql/item.cc b/sql/item.cc index fe51b2966ad..a76152c14e2 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -5621,6 +5621,7 @@ bool Item_field::fix_fields(THD *thd, Item **reference) expression to 'reference', i.e. it substitute that expression instead of this Item_field */ + DBUG_ASSERT(context); if ((from_field= find_field_in_tables(thd, this, context->first_name_resolution_table, context->last_name_resolution_table, diff --git a/sql/item.h b/sql/item.h index e5a3215d13e..35b76a3278e 100644 --- a/sql/item.h +++ b/sql/item.h @@ -3913,7 +3913,7 @@ public: class Item_datetime_literal: public Item_temporal_literal { public: - Item_datetime_literal(THD *thd, MYSQL_TIME *ltime, uint dec_arg): + Item_datetime_literal(THD *thd, MYSQL_TIME *ltime, uint dec_arg= 0): Item_temporal_literal(thd, ltime, dec_arg) { max_length= MAX_DATETIME_WIDTH + (decimals ? decimals + 1 : 0); diff --git a/sql/item_create.cc b/sql/item_create.cc index d488b5f26c0..48055ccb11e 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -6678,6 +6678,92 @@ Create_func_year_week::create_native(THD *thd, LEX_STRING name, } +/* System Versioning: BEGIN_TS(), COMMIT_TS() */ + +class Create_func_begin_ts : public Create_native_func +{ +public: + virtual Item *create_native(THD *thd, LEX_STRING name, List *item_list); + + static Create_func_begin_ts s_singleton; + +protected: + Create_func_begin_ts() {} + virtual ~Create_func_begin_ts() {} +}; + +Create_func_begin_ts Create_func_begin_ts::s_singleton; + +Item* +Create_func_begin_ts::create_native(THD *thd, LEX_STRING name, + List *item_list) +{ + Item *func= NULL; + int arg_count= 0; + + if (item_list != NULL) + arg_count= item_list->elements; + + switch (arg_count) { + case 1: + { + Item *param_1= item_list->pop(); + func= new (thd->mem_root) Item_func_vtq_ts(thd, param_1, VTQ_BEGIN_TS); + break; + } + default: + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + break; + } + } + + return func; +} + +class Create_func_commit_ts : public Create_native_func +{ +public: + virtual Item *create_native(THD *thd, LEX_STRING name, List *item_list); + + static Create_func_commit_ts s_singleton; + +protected: + Create_func_commit_ts() {} + virtual ~Create_func_commit_ts() {} +}; + +Create_func_commit_ts Create_func_commit_ts::s_singleton; + +Item* +Create_func_commit_ts::create_native(THD *thd, LEX_STRING name, + List *item_list) +{ + Item *func= NULL; + int arg_count= 0; + + if (item_list != NULL) + arg_count= item_list->elements; + + switch (arg_count) { + case 1: + { + Item *param_1= item_list->pop(); + func= new (thd->mem_root) Item_func_vtq_ts(thd, param_1, VTQ_COMMIT_TS); + break; + } + default: + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + break; + } + } + + return func; +} + + + struct Native_func_registry { LEX_STRING name; @@ -6718,6 +6804,7 @@ static Native_func_registry func_array[] = { { C_STRING_WITH_LEN("ASWKT") }, GEOM_BUILDER(Create_func_as_wkt)}, { { C_STRING_WITH_LEN("ATAN") }, BUILDER(Create_func_atan)}, { { C_STRING_WITH_LEN("ATAN2") }, BUILDER(Create_func_atan)}, + { { C_STRING_WITH_LEN("BEGIN_TS") }, BUILDER(Create_func_begin_ts)}, { { C_STRING_WITH_LEN("BENCHMARK") }, BUILDER(Create_func_benchmark)}, { { C_STRING_WITH_LEN("BIN") }, BUILDER(Create_func_bin)}, { { C_STRING_WITH_LEN("BINLOG_GTID_POS") }, BUILDER(Create_func_binlog_gtid_pos)}, @@ -6735,6 +6822,7 @@ static Native_func_registry func_array[] = { { C_STRING_WITH_LEN("COLUMN_EXISTS") }, BUILDER(Create_func_dyncol_exists)}, { { C_STRING_WITH_LEN("COLUMN_LIST") }, BUILDER(Create_func_dyncol_list)}, { { C_STRING_WITH_LEN("COLUMN_JSON") }, BUILDER(Create_func_dyncol_json)}, + { { C_STRING_WITH_LEN("COMMIT_TS") }, BUILDER(Create_func_commit_ts)}, { { C_STRING_WITH_LEN("COMPRESS") }, BUILDER(Create_func_compress)}, { { C_STRING_WITH_LEN("CONCAT") }, BUILDER(Create_func_concat)}, { { C_STRING_WITH_LEN("CONCAT_WS") }, BUILDER(Create_func_concat_ws)}, diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index a1a9ca5c8ae..3ceb87246ce 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -3273,3 +3273,71 @@ bool Item_func_last_day::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) ltime->time_type= MYSQL_TIMESTAMP_DATE; return (null_value= 0); } + +Item_func_vtq_ts::Item_func_vtq_ts( + THD *thd, + Item* a, + vtq_field_t _vtq_field, + handlerton* _hton) : + Item_datetimefunc(thd, a), + vtq_field(_vtq_field), + hton(_hton) +{ + decimals= 6; + null_value= true; + DBUG_ASSERT(arg_count == 1 && args[0]); +} + +Item_func_vtq_ts::Item_func_vtq_ts( + THD *thd, + Item* a, + vtq_field_t _vtq_field) : + Item_datetimefunc(thd, a), + vtq_field(_vtq_field), + hton(NULL) +{ + decimals= 6; + null_value= true; + DBUG_ASSERT(arg_count == 1 && args[0]); +} + +bool Item_func_vtq_ts::get_date(MYSQL_TIME *res, ulonglong fuzzy_date) +{ + THD *thd= current_thd; // can it differ from constructor's? + DBUG_ASSERT(thd); + ulonglong trx_id= args[0]->val_uint(); + if (trx_id == ULONGLONG_MAX) + { + null_value= false; + thd->variables.time_zone->gmt_sec_to_TIME(res, TIMESTAMP_MAX_VALUE); + return false; + } + + if (!hton) + { + if (args[0]->type() == Item::FIELD_ITEM) + { + Item_field *f= + static_cast(args[0]); + DBUG_ASSERT( + f->field && + f->field->table && + f->field->table->s && + f->field->table->s->db_plugin); + hton= plugin_hton(f->field->table->s->db_plugin); + DBUG_ASSERT(hton); + } + else if (innodb_plugin) + { + hton= plugin_hton(plugin_int_to_ref(innodb_plugin)); + DBUG_ASSERT(hton); + } + } + + if (!hton) + return true; + + null_value= !hton->vers_get_vtq_ts(thd, res, trx_id, vtq_field); + + return false; +} diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index f1a719fb775..a8281124b38 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -1286,4 +1286,26 @@ public: { return get_item_copy(thd, mem_root, this); } }; +#include "vtq.h" + +class Item_func_vtq_ts :public Item_datetimefunc +{ + vtq_field_t vtq_field; + handlerton *hton; +public: + Item_func_vtq_ts(THD *thd, Item* a, vtq_field_t _vtq_field, handlerton *hton); + Item_func_vtq_ts(THD *thd, Item* a, vtq_field_t _vtq_field); + const char *func_name() const + { + if (vtq_field == VTQ_BEGIN_TS) + { + return "begin_ts"; + } + return "commit_ts"; + } + bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy(thd, mem_root, this); } +}; + #endif /* ITEM_TIMEFUNC_INCLUDED */ diff --git a/sql/sp_head.cc b/sql/sp_head.cc index aeaca82649f..f470ca3f283 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -2529,6 +2529,7 @@ sp_head::restore_thd_mem_root(THD *thd) Item *flist= free_list; // The old list set_query_arena(thd); // Get new free_list and mem_root state= STMT_INITIALIZED_FOR_SP; + is_stored_procedure= true; DBUG_PRINT("info", ("mem_root 0x%lx returned from thd mem root 0x%lx", (ulong) &mem_root, (ulong) &thd->mem_root)); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 89f1798d36d..1af45c12331 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -7962,7 +7962,7 @@ fill_record(THD *thd, TABLE *table_arg, List &fields, List &values, ER_THD(thd, ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN), rfield->field_name, table->s->table_name.str); } - if (table->versioned_by_sql() && rfield->is_generated() && + if (table->versioned() && rfield->is_generated() && !ignore_errors) { my_error(ER_GENERATED_FIELD_CANNOT_BE_SET_BY_USER, MYF(0)); @@ -8216,7 +8216,7 @@ fill_record(THD *thd, TABLE *table, Field **ptr, List &values, } } - if (table->versioned_by_sql() && field->is_generated() && + if (table->versioned() && field->is_generated() && !ignore_errors) { my_error(ER_GENERATED_FIELD_CANNOT_BE_SET_BY_USER, MYF(0)); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 0ed78b2dfa8..06a592b8d51 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -706,6 +706,11 @@ extern "C" void thd_kill_timeout(THD* thd) mysql_mutex_unlock(&thd->LOCK_thd_data); } +Time_zone * thd_get_timezone(THD * thd) +{ + DBUG_ASSERT(thd && thd->variables.time_zone); + return thd->variables.time_zone; +} THD::THD(my_thread_id id, bool is_wsrep_applier) :Statement(&main_lex, &main_mem_root, STMT_CONVENTIONAL_EXECUTION, @@ -3587,6 +3592,7 @@ void Query_arena::set_query_arena(Query_arena *set) mem_root= set->mem_root; free_list= set->free_list; state= set->state; + is_stored_procedure= set->is_stored_procedure; } diff --git a/sql/sql_class.h b/sql/sql_class.h index 82d538b07fd..1b920b23a2d 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -940,6 +940,11 @@ public: enum_state state; +protected: + friend class sp_head; + bool is_stored_procedure; + +public: /* We build without RTTI, so dynamic_cast can't be used. */ enum Type { @@ -947,7 +952,8 @@ public: }; Query_arena(MEM_ROOT *mem_root_arg, enum enum_state state_arg) : - free_list(0), mem_root(mem_root_arg), state(state_arg) + free_list(0), mem_root(mem_root_arg), state(state_arg), + is_stored_procedure(state_arg == STMT_INITIALIZED_FOR_SP ? true : false) { INIT_ARENA_DBUG_INFO; } /* This constructor is used only when Query_arena is created as @@ -967,6 +973,8 @@ public: { return state == STMT_PREPARED || state == STMT_EXECUTED; } inline bool is_conventional() const { return state == STMT_CONVENTIONAL_EXECUTION; } + inline bool is_sp_execute() const + { return is_stored_procedure; } inline void* alloc(size_t size) { return alloc_root(mem_root,size); } inline void* calloc(size_t size) @@ -6021,5 +6029,4 @@ public: #endif /* MYSQL_SERVER */ - #endif /* SQL_CLASS_INCLUDED */ diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index c1281a2e022..a45fc044f55 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -2200,6 +2200,7 @@ void st_select_lex::init_query() join= 0; having= prep_having= where= prep_where= 0; cond_pushed_into_where= cond_pushed_into_having= 0; + saved_conds= 0; olap= UNSPECIFIED_OLAP_TYPE; having_fix_field= 0; context.select_lex= this; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 9b625d3b7b3..d3665844a69 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -810,6 +810,7 @@ public: Item *prep_having;/* saved HAVING clause for prepared statement processing */ Item *cond_pushed_into_where; /* condition pushed into the select's WHERE */ Item *cond_pushed_into_having; /* condition pushed into the select's HAVING */ + Item *saved_conds; /* Saved values of the WHERE and HAVING clauses*/ Item::cond_result cond_value, having_value; /* point on lex in which it was created, used in view subquery detection */ diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index c307bc4f425..82fbb7c62cc 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -233,6 +233,8 @@ static int plugin_array_version=0; static bool initialized= 0; ulong dlopen_count; +st_plugin_int* innodb_plugin= NULL; + /* write-lock on LOCK_system_variables_hash is required before modifying @@ -1422,6 +1424,18 @@ static int plugin_initialize(MEM_ROOT *tmp_root, struct st_plugin_int *plugin, } state= PLUGIN_IS_READY; // plugin->init() succeeded + { + static const char * INNODB= "InnoDB"; + static const uint INNODB_LEN= strlen(INNODB); + + if (!my_strnncoll(&my_charset_latin1, + (const uchar *) plugin->name.str, plugin->name.length, + (const uchar *) INNODB, INNODB_LEN)) + { + innodb_plugin= plugin; + } + } + if (plugin->plugin->status_vars) { /* diff --git a/sql/sql_plugin.h b/sql/sql_plugin.h index 7b89246a9f9..e399fec8339 100644 --- a/sql/sql_plugin.h +++ b/sql/sql_plugin.h @@ -160,6 +160,8 @@ extern ulong plugin_maturity; extern TYPELIB plugin_maturity_values; extern const char *plugin_maturity_names[]; +extern st_plugin_int* innodb_plugin; + extern int plugin_init(int *argc, char **argv, int init_flags); extern void plugin_shutdown(void); void add_plugin_options(DYNAMIC_ARRAY *options, MEM_ROOT *mem_root); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index db50d471183..207ce5c0139 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -55,6 +55,7 @@ #include "sql_statistics.h" #include "sql_cte.h" #include "sql_window.h" +#include "tztime.h" #include "debug_sync.h" // DEBUG_SYNC #include @@ -673,10 +674,19 @@ setup_for_system_time(THD *thd, TABLE_LIST *tables, COND **conds, SELECT_LEX *se TABLE_LIST *table; int versioned_tables= 0; + Query_arena *arena= 0, backup; + bool is_prepare= thd->stmt_arena->is_stmt_prepare(); + + if (!thd->stmt_arena->is_conventional() + && !is_prepare + && !thd->stmt_arena->is_sp_execute()) + { + DBUG_RETURN(0); + } for (table= tables; table; table= table->next_local) { - if (table->table && table->table->versioned_by_sql()) + if (table->table && table->table->versioned()) versioned_tables++; else if (table->system_versioning.type != FOR_SYSTEM_TIME_UNSPECIFIED) { @@ -688,52 +698,115 @@ setup_for_system_time(THD *thd, TABLE_LIST *tables, COND **conds, SELECT_LEX *se if (versioned_tables == 0) DBUG_RETURN(0); + /* For prepared statements we create items on statement arena, + because they must outlive execution phase for multiple executions. */ + arena= thd->activate_stmt_arena_if_needed(&backup); + + if (select_lex->saved_conds) + { + DBUG_ASSERT(thd->stmt_arena->is_sp_execute()); + *conds= select_lex->saved_conds; + } + else if (thd->stmt_arena->is_sp_execute()) + { + if (thd->stmt_arena->is_stmt_execute()) + *conds= 0; + else if (*conds) + select_lex->saved_conds= (*conds)->copy_andor_structure(thd); + } + for (table= tables; table; table= table->next_local) { - if (table->table && table->table->versioned_by_sql()) + if (table->table && table->table->versioned()) { Field *fstart= table->table->vers_start_field(); Field *fend= table->table->vers_end_field(); - Item *istart= new (thd->mem_root) Item_field(thd, fstart); - Item *iend= new (thd->mem_root) Item_field(thd, fend); - Item *cond1= 0, *cond2= 0, *curr = 0; + + DBUG_ASSERT(select_lex->parent_lex); + Name_resolution_context *context= select_lex->parent_lex->current_context(); + DBUG_ASSERT(context); + + Item *row_start= new (thd->mem_root) Item_field(thd, context, fstart); + Item *row_end= new (thd->mem_root) Item_field(thd, context, fend); + Item *row_end2= row_end; + + if (!table->table->versioned_by_sql()) + { + DBUG_ASSERT(table->table->s && table->table->s->db_plugin); + row_start= new (thd->mem_root) Item_func_vtq_ts( + thd, + row_start, + VTQ_COMMIT_TS, + plugin_hton(table->table->s->db_plugin)); + row_end= new (thd->mem_root) Item_func_vtq_ts( + thd, + row_end, + VTQ_COMMIT_TS, + plugin_hton(table->table->s->db_plugin)); + } + + Item *cond1= 0, *cond2= 0, *curr= 0; switch (table->system_versioning.type) { case FOR_SYSTEM_TIME_UNSPECIFIED: - curr= new (thd->mem_root) Item_func_now_local(thd, 6); - cond1= new (thd->mem_root) Item_func_le(thd, istart, curr); - cond2= new (thd->mem_root) Item_func_gt(thd, iend, curr); + if (table->table->versioned_by_sql()) + { + MYSQL_TIME max_time; + thd->variables.time_zone->gmt_sec_to_TIME(&max_time, TIMESTAMP_MAX_VALUE); + curr= new (thd->mem_root) Item_datetime_literal(thd, &max_time); + cond1= new (thd->mem_root) Item_func_eq(thd, row_end, curr); + } + else + { + curr= new (thd->mem_root) Item_int(thd, ULONGLONG_MAX); + cond1= new (thd->mem_root) Item_func_eq(thd, row_end2, curr); + } break; case FOR_SYSTEM_TIME_AS_OF: - cond1= new (thd->mem_root) Item_func_le(thd, istart, - table->system_versioning.start); - cond2= new (thd->mem_root) Item_func_gt(thd, iend, - table->system_versioning.start); + cond1= new (thd->mem_root) Item_func_le(thd, row_start, + table->system_versioning.start); + cond2= new (thd->mem_root) Item_func_gt(thd, row_end, + table->system_versioning.start); break; case FOR_SYSTEM_TIME_FROM_TO: - cond1= new (thd->mem_root) Item_func_lt(thd, istart, + cond1= new (thd->mem_root) Item_func_lt(thd, row_start, table->system_versioning.end); - cond2= new (thd->mem_root) Item_func_ge(thd, iend, + cond2= new (thd->mem_root) Item_func_ge(thd, row_end, table->system_versioning.start); break; case FOR_SYSTEM_TIME_BETWEEN: - cond1= new (thd->mem_root) Item_func_le(thd, istart, + cond1= new (thd->mem_root) Item_func_le(thd, row_start, table->system_versioning.end); - cond2= new (thd->mem_root) Item_func_ge(thd, iend, + cond2= new (thd->mem_root) Item_func_ge(thd, row_end, table->system_versioning.start); break; default: DBUG_ASSERT(0); } - if (cond1 && cond2) + + if (cond1) { - COND *system_time_cond= new (thd->mem_root) Item_cond_and(thd, cond1, cond2); - thd->change_item_tree(conds, and_items(thd, *conds, system_time_cond)); + cond1= and_items(thd, + *conds, + and_items(thd, + cond2, + cond1)); + + if (arena) + *conds= cond1; + else + thd->change_item_tree(conds, cond1); + table->system_versioning.is_moved_to_where= true; } } } + if (arena) + { + thd->restore_active_arena(arena, &backup); + } + DBUG_RETURN(0); } diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 2bcf0199429..084e98b143f 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2224,7 +2224,7 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, hton->index_options); } - if (table->versioned_by_sql()) + if (table->versioned()) { const Field *fs = table->vers_start_field(); const Field *fe = table->vers_end_field(); @@ -2273,7 +2273,7 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, add_table_options(thd, table, create_info_arg, table_list->schema_table != 0, 0, packet); - if (table->versioned_by_sql()) + if (table->versioned()) { packet->append(STRING_WITH_LEN(" WITH SYSTEM VERSIONING")); } diff --git a/sql/sql_time.cc b/sql/sql_time.cc index 6f37f97feef..ee40dbff876 100644 --- a/sql/sql_time.cc +++ b/sql/sql_time.cc @@ -476,18 +476,6 @@ void localtime_to_TIME(MYSQL_TIME *to, struct tm *from) } -/* - Convert seconds since Epoch to TIME -*/ - -void unix_time_to_TIME(MYSQL_TIME *to, time_t secs, suseconds_t usecs) -{ - struct tm tm_time; - localtime_r(&secs, &tm_time); - localtime_to_TIME(to, &tm_time); - to->second_part = usecs; -} - void calc_time_from_sec(MYSQL_TIME *to, long seconds, long microseconds) { long t_seconds; diff --git a/sql/sql_time.h b/sql/sql_time.h index fd0c0273dcf..e6246d91fa5 100644 --- a/sql/sql_time.h +++ b/sql/sql_time.h @@ -171,15 +171,6 @@ bool calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2, int lsign, MYSQL_TIME *l_time3, ulonglong fuzzydate); int my_time_compare(const MYSQL_TIME *a, const MYSQL_TIME *b); void localtime_to_TIME(MYSQL_TIME *to, struct tm *from); -void unix_time_to_TIME(MYSQL_TIME *to, time_t secs, suseconds_t usecs); - -inline -longlong unix_time_to_packed(time_t secs, suseconds_t usecs) -{ - MYSQL_TIME mysql_time; - unix_time_to_TIME(&mysql_time, secs, usecs); - return pack_time(&mysql_time); -} void calc_time_from_sec(MYSQL_TIME *to, long seconds, long microseconds); uint calc_week(MYSQL_TIME *l_time, uint week_behaviour, uint *year); diff --git a/sql/tztime.h b/sql/tztime.h index eb7d85c48b2..d3f19fa2fd3 100644 --- a/sql/tztime.h +++ b/sql/tztime.h @@ -89,6 +89,7 @@ extern my_time_t sec_since_epoch_TIME(MYSQL_TIME *t); static const int MY_TZ_TABLES_COUNT= 4; +extern Time_zone* thd_get_timezone(THD* thd); #endif /* !defined(TESTTIME) && !defined(TZINFO2SQL) */ #endif /* TZTIME_INCLUDED */ diff --git a/sql/vtq.h b/sql/vtq.h new file mode 100755 index 00000000000..3a235352853 --- /dev/null +++ b/sql/vtq.h @@ -0,0 +1,25 @@ +#ifndef VTQ_INCLUDED +#define VTQ_INCLUDED +/* Copyright (c) 2016, MariaDB Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ + +enum vtq_field_t +{ + VTQ_BEGIN_TS = 0, + VTQ_COMMIT_TS +}; + +#endif /* VTQ_INCLUDED */ + -- cgit v1.2.1 From 2db17e662437268e09913a310ab0ec369df73e15 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Thu, 13 Oct 2016 13:30:22 +0000 Subject: Parser: versioned JOIN fix Moved opt_for_system_time_clause from table identifier (which binds it to single table) to table expression. --- sql/sql_lex.cc | 1 + sql/sql_lex.h | 3 +++ sql/sql_select.cc | 42 +++++++++++++++++++----------------------- sql/sql_yacc.yy | 18 +++++++----------- sql/table.h | 26 ++++++++++++++++---------- 5 files changed, 46 insertions(+), 44 deletions(-) (limited to 'sql') diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index a45fc044f55..34b35d60c14 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -2278,6 +2278,7 @@ void st_select_lex::init_select() with_dep= 0; join= 0; lock_type= TL_READ_DEFAULT; + vers_conditions.empty(); } /* diff --git a/sql/sql_lex.h b/sql/sql_lex.h index d3665844a69..64e0c9ddaa9 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -991,6 +991,9 @@ public: /* it is for correct printing SELECT options */ thr_lock_type lock_type; + /* System Versioning conditions */ + vers_select_conds_t vers_conditions; + void init_query(); void init_select(); st_select_lex_unit* master_unit() { return (st_select_lex_unit*) master; } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 207ce5c0139..331421ecb6d 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -688,11 +688,6 @@ setup_for_system_time(THD *thd, TABLE_LIST *tables, COND **conds, SELECT_LEX *se { if (table->table && table->table->versioned()) versioned_tables++; - else if (table->system_versioning.type != FOR_SYSTEM_TIME_UNSPECIFIED) - { - my_error(ER_TABLE_DOESNT_SUPPORT_SYSTEM_VERSIONING, MYF(0), table->table_name); - DBUG_RETURN(-1); - } } if (versioned_tables == 0) @@ -746,7 +741,7 @@ setup_for_system_time(THD *thd, TABLE_LIST *tables, COND **conds, SELECT_LEX *se } Item *cond1= 0, *cond2= 0, *curr= 0; - switch (table->system_versioning.type) + switch (select_lex->vers_conditions.type) { case FOR_SYSTEM_TIME_UNSPECIFIED: if (table->table->versioned_by_sql()) @@ -764,21 +759,21 @@ setup_for_system_time(THD *thd, TABLE_LIST *tables, COND **conds, SELECT_LEX *se break; case FOR_SYSTEM_TIME_AS_OF: cond1= new (thd->mem_root) Item_func_le(thd, row_start, - table->system_versioning.start); + select_lex->vers_conditions.start); cond2= new (thd->mem_root) Item_func_gt(thd, row_end, - table->system_versioning.start); + select_lex->vers_conditions.start); break; case FOR_SYSTEM_TIME_FROM_TO: cond1= new (thd->mem_root) Item_func_lt(thd, row_start, - table->system_versioning.end); + select_lex->vers_conditions.end); cond2= new (thd->mem_root) Item_func_ge(thd, row_end, - table->system_versioning.start); + select_lex->vers_conditions.start); break; case FOR_SYSTEM_TIME_BETWEEN: cond1= new (thd->mem_root) Item_func_le(thd, row_start, - table->system_versioning.end); + select_lex->vers_conditions.end); cond2= new (thd->mem_root) Item_func_ge(thd, row_end, - table->system_versioning.start); + select_lex->vers_conditions.start); break; default: DBUG_ASSERT(0); @@ -797,10 +792,10 @@ setup_for_system_time(THD *thd, TABLE_LIST *tables, COND **conds, SELECT_LEX *se else thd->change_item_tree(conds, cond1); - table->system_versioning.is_moved_to_where= true; + table->vers_moved_to_where= true; } - } - } + } // if (... table->table->versioned()) + } // for (table= tables; ...) if (arena) { @@ -24935,29 +24930,30 @@ bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result) void TABLE_LIST::print_system_versioning(THD *thd, table_map eliminated_tables, String *str, enum_query_type query_type) { - if (system_versioning.is_moved_to_where) + if (vers_moved_to_where) return; + DBUG_ASSERT(select_lex); // system versioning - if (system_versioning.type != FOR_SYSTEM_TIME_UNSPECIFIED) + if (select_lex->vers_conditions.type != FOR_SYSTEM_TIME_UNSPECIFIED) { - switch (system_versioning.type) + switch (select_lex->vers_conditions.type) { case FOR_SYSTEM_TIME_AS_OF: str->append(STRING_WITH_LEN(" for system_time as of ")); - system_versioning.start->print(str, query_type); + select_lex->vers_conditions.start->print(str, query_type); break; case FOR_SYSTEM_TIME_FROM_TO: str->append(STRING_WITH_LEN(" for system_time from timestamp ")); - system_versioning.start->print(str, query_type); + select_lex->vers_conditions.start->print(str, query_type); str->append(STRING_WITH_LEN(" to ")); - system_versioning.end->print(str, query_type); + select_lex->vers_conditions.end->print(str, query_type); break; case FOR_SYSTEM_TIME_BETWEEN: str->append(STRING_WITH_LEN(" for system_time between timestamp ")); - system_versioning.start->print(str, query_type); + select_lex->vers_conditions.start->print(str, query_type); str->append(STRING_WITH_LEN(" and ")); - system_versioning.end->print(str, query_type); + select_lex->vers_conditions.end->print(str, query_type); break; default: DBUG_ASSERT(0); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index f681ed25596..05c05c3e144 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -758,7 +758,6 @@ Virtual_column_info *add_virtual_expression(THD *thd, Item *expr) uint sp_instr_addr; /* structs */ - system_versioning_for_select system_versioning; LEX_STRING lex_str; LEX_SYMBOL symbol; Lex_string_with_metadata_st lex_string_with_metadata; @@ -1954,7 +1953,6 @@ END_OF_INPUT %type window_frame_extent; %type opt_window_frame_exclusion; %type window_frame_start window_frame_bound; -%type opt_for_system_time_clause; %type '-' '+' '*' '/' '%' '(' ')' @@ -8628,6 +8626,7 @@ table_expression: opt_group_clause opt_having_clause opt_window_clause + opt_for_system_time_clause ; opt_table_expression: @@ -8664,14 +8663,12 @@ select_options: opt_for_system_time_clause: /* empty */ - { - $$.init(); - } + {} | FOR_SYSTEM_TIME_SYM AS OF_SYM TIMESTAMP simple_expr { - $$.init(FOR_SYSTEM_TIME_AS_OF, $5); + Lex->current_select->vers_conditions.init(FOR_SYSTEM_TIME_AS_OF, $5); } | FOR_SYSTEM_TIME_SYM AS OF_SYM @@ -8680,7 +8677,7 @@ opt_for_system_time_clause: Item *item= new (thd->mem_root) Item_func_now_local(thd, 6); if (item == NULL) MYSQL_YYABORT; - $$.init(FOR_SYSTEM_TIME_AS_OF, item); + Lex->current_select->vers_conditions.init(FOR_SYSTEM_TIME_AS_OF, item); } | FOR_SYSTEM_TIME_SYM FROM @@ -8688,7 +8685,7 @@ opt_for_system_time_clause: TO_SYM TIMESTAMP simple_expr { - $$.init(FOR_SYSTEM_TIME_FROM_TO, $4, $7); + Lex->current_select->vers_conditions.init(FOR_SYSTEM_TIME_FROM_TO, $4, $7); } | FOR_SYSTEM_TIME_SYM BETWEEN_SYM @@ -8696,7 +8693,7 @@ opt_for_system_time_clause: AND_SYM TIMESTAMP simple_expr { - $$.init(FOR_SYSTEM_TIME_BETWEEN, $4, $7); + Lex->current_select->vers_conditions.init(FOR_SYSTEM_TIME_BETWEEN, $4, $7); } ; @@ -11107,7 +11104,7 @@ table_primary_ident: SELECT_LEX *sel= Select; sel->table_join_options= 0; } - table_ident opt_use_partition opt_table_alias opt_key_definition opt_for_system_time_clause + table_ident opt_use_partition opt_table_alias opt_key_definition { if (!($$= Select->add_table_to_list(thd, $2, $4, Select->get_table_join_options(), @@ -11117,7 +11114,6 @@ table_primary_ident: $3))) MYSQL_YYABORT; Select->add_joined_table($$); - $$->system_versioning= $6; } ; diff --git a/sql/table.h b/sql/table.h index ad62c06838b..cc575602c49 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1814,26 +1814,32 @@ class Item_in_subselect; enum for_system_time_type { - FOR_SYSTEM_TIME_UNSPECIFIED, FOR_SYSTEM_TIME_AS_OF, - FOR_SYSTEM_TIME_FROM_TO, FOR_SYSTEM_TIME_BETWEEN + FOR_SYSTEM_TIME_UNSPECIFIED = 0, + FOR_SYSTEM_TIME_AS_OF, + FOR_SYSTEM_TIME_FROM_TO, + FOR_SYSTEM_TIME_BETWEEN }; /** System versioning support. */ -struct system_versioning_for_select +struct vers_select_conds_t { enum for_system_time_type type; Item *start, *end; - bool is_moved_to_where; + + void empty() + { + type= FOR_SYSTEM_TIME_UNSPECIFIED; + start= end= NULL; + } void init( - const enum for_system_time_type t=FOR_SYSTEM_TIME_UNSPECIFIED, - Item * const s=NULL, - Item * const e=NULL) + const enum for_system_time_type t, + Item * const s, + Item * const e= NULL) { type= t; start= s; end= e; - is_moved_to_where= false; } }; @@ -2293,8 +2299,8 @@ struct TABLE_LIST TABLE_LIST *first_leaf_for_name_resolution(); TABLE_LIST *last_leaf_for_name_resolution(); - /** System versioning support. */ - system_versioning_for_select system_versioning; + /* System Versioning */ + bool vers_moved_to_where; /** @brief -- cgit v1.2.1 From a9a56b235543fd34ba57c27624aa269c0a13df9d Mon Sep 17 00:00:00 2001 From: Kosov Eugene Date: Sun, 2 Oct 2016 12:35:50 +0300 Subject: SQL: 0.3 per column versioning syntax, .frm, optimized updates and tests --- sql/field.h | 14 +++++- sql/handler.cc | 123 +++++++++++++++++++++++++++++++++++++++++++++- sql/handler.h | 33 +++++++++++-- sql/lex.h | 1 + sql/share/errmsg-utf8.txt | 3 ++ sql/sql_class.h | 2 + sql/sql_parse.cc | 67 +------------------------ sql/sql_show.cc | 5 ++ sql/sql_table.cc | 2 + sql/sql_update.cc | 25 +++++++--- sql/sql_yacc.yy | 15 ++++-- sql/table.cc | 3 ++ 12 files changed, 210 insertions(+), 83 deletions(-) (limited to 'sql') diff --git a/sql/field.h b/sql/field.h index 4b6607fa099..bf4f8b5ce5c 100644 --- a/sql/field.h +++ b/sql/field.h @@ -3872,6 +3872,13 @@ class Column_definition: public Sql_alloc } } public: + enum enum_column_versioning + { + VERSIONING_NOT_SET, + WITH_VERSIONING, + WITHOUT_VERSIONING + }; + const char *field_name; LEX_STRING comment; // Comment for field Item *on_update; // ON UPDATE NOW() @@ -3907,6 +3914,8 @@ public: *default_value, // Default value *check_constraint; // Check constraint + enum_column_versioning versioning; + Column_definition(): comment(null_lex_str), on_update(NULL), sql_type(MYSQL_TYPE_NULL), length(0), decimals(0), @@ -3914,7 +3923,8 @@ public: interval(0), charset(&my_charset_bin), srid(0), geom_type(Field::GEOM_GEOMETRY), option_list(NULL), pack_flag(0), - vcol_info(0), default_value(0), check_constraint(0) + vcol_info(0), default_value(0), check_constraint(0), + versioning(VERSIONING_NOT_SET) { interval_list.empty(); } @@ -4276,6 +4286,7 @@ bool check_expression(Virtual_column_info *vcol, const char *name, #define FIELDFLAG_TREAT_BIT_AS_CHAR 4096U /* use Field_bit_as_char */ #define FIELDFLAG_LONG_DECIMAL 8192U +#define FIELDFLAG_WITHOUT_SYSTEM_VERSIONING 8192U #define FIELDFLAG_NO_DEFAULT 16384U /* sql */ #define FIELDFLAG_MAYBE_NULL 32768U // sql #define FIELDFLAG_HEX_ESCAPE 0x10000U @@ -4302,5 +4313,6 @@ bool check_expression(Virtual_column_info *vcol, const char *name, #define f_no_default(x) ((x) & FIELDFLAG_NO_DEFAULT) #define f_bit_as_char(x) ((x) & FIELDFLAG_TREAT_BIT_AS_CHAR) #define f_is_hex_escape(x) ((x) & FIELDFLAG_HEX_ESCAPE) +#define f_without_system_versioning(x) ((x) & FIELDFLAG_WITHOUT_SYSTEM_VERSIONING) #endif /* FIELD_INCLUDED */ diff --git a/sql/handler.cc b/sql/handler.cc index 0c2dea8b00e..174d15194a0 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -6582,12 +6582,35 @@ static bool create_sys_trx_field_if_missing(THD *thd, const char *field_name, return false; } -bool System_versioning_info::add_implicit_fields(THD *thd, +bool System_versioning_info::add_versioning_info(THD *thd, Alter_info *alter_info) { - if (!declared_system_versioning) + DBUG_ASSERT(versioned()); + + if (!declared_system_versioning && !has_versioned_fields) return false; + bool without_system_versioning_by_default= !declared_system_versioning; + List_iterator it(alter_info->create_list); + while (Create_field *f= it++) + { + const char *name= f->field_name; + size_t len= strlen(name); + if (generated_as_row.start && + !strncmp(name, generated_as_row.start->c_ptr(), len)) + continue; + if (generated_as_row.end && + !strncmp(name, generated_as_row.end->c_ptr(), len)) + continue; + + if (f->versioning == Column_definition::VERSIONING_NOT_SET && + without_system_versioning_by_default) + f->flags|= WITHOUT_SYSTEM_VERSIONING_FLAG; + + else if (f->versioning == Column_definition::WITHOUT_VERSIONING) + f->flags|= WITHOUT_SYSTEM_VERSIONING_FLAG; + } + // If user specified some of these he must specify the others too. Do nothing. if (generated_as_row.start || generated_as_row.end || period_for_system_time.start || period_for_system_time.end) @@ -6602,3 +6625,99 @@ bool System_versioning_info::add_implicit_fields(THD *thd, create_string(thd->mem_root, &period_for_system_time.end, "sys_trx_end"); } + +bool System_versioning_info::check(THD *thd, Alter_info *alter_info) +{ + if (!versioned()) + return false; + + if (add_versioning_info(thd, alter_info)) + return true; + + bool r= false; + + { + int not_set= 0; + int with= 0; + List_iterator it(alter_info->create_list); + while (const Create_field *f= it++) + { + const char *name= f->field_name; + size_t len= strlen(name); + if (generated_as_row.start && + !strncmp(name, generated_as_row.start->c_ptr(), len)) + continue; + if (generated_as_row.end && + !strncmp(name, generated_as_row.end->c_ptr(), len)) + continue; + + if (f->versioning == Column_definition::VERSIONING_NOT_SET) + not_set++; + else if (f->versioning == Column_definition::WITH_VERSIONING) + with++; + } + + bool table_with_system_versioning= + declared_system_versioning || generated_as_row.start || + generated_as_row.end || period_for_system_time.start || + period_for_system_time.end; + + if ((table_with_system_versioning && not_set == 0 && with == 0) || + (!table_with_system_versioning && with == 0)) + { + r= true; + my_error(ER_NO_VERSIONED_FIELDS_IN_VERSIONED_TABLE, MYF(0)); + } + } + + if (!declared_system_versioning && !has_versioned_fields) + { + r= true; + my_error(ER_MISSING_WITH_SYSTEM_VERSIONING, MYF(0)); + } + + if (!generated_as_row.start) + { + r= true; + my_error(ER_SYS_START_NOT_SPECIFIED, MYF(0)); + } + + if (!generated_as_row.end) + { + r= true; + my_error(ER_SYS_END_NOT_SPECIFIED, MYF(0)); + } + + if (!period_for_system_time.start || !period_for_system_time.end) + { + r= true; + my_error(ER_MISSING_PERIOD_FOR_SYSTEM_TIME, MYF(0)); + } + + if (!r) + { + if (my_strcasecmp(system_charset_info, generated_as_row.start->c_ptr(), + period_for_system_time.start->c_ptr())) + { + r= true; + my_error(ER_PERIOD_FOR_SYSTEM_TIME_CONTAINS_WRONG_START_COLUMN, MYF(0)); + } + + if (my_strcasecmp(system_charset_info, generated_as_row.end->c_ptr(), + period_for_system_time.end->c_ptr())) + { + r= true; + my_error(ER_PERIOD_FOR_SYSTEM_TIME_CONTAINS_WRONG_END_COLUMN, MYF(0)); + } + } + + return r; // false means no error +} + +bool System_versioning_info::versioned() const +{ + return has_versioned_fields || has_unversioned_fields || + declared_system_versioning || period_for_system_time.start || + period_for_system_time.end || generated_as_row.start || + generated_as_row.end; +} diff --git a/sql/handler.h b/sql/handler.h index 8b7b6f516a1..698703f89ff 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -1671,6 +1671,14 @@ struct Schema_specification_st struct System_versioning_info { + System_versioning_info() : + period_for_system_time({NULL, NULL}), + generated_as_row({NULL, NULL}), + declared_system_versioning(false), + has_versioned_fields(false), + has_unversioned_fields(false) + {} + struct { String *start, *end; @@ -1693,13 +1701,28 @@ struct System_versioning_info } /** Returns true on failure */ - bool add_implicit_fields(THD *thd, Alter_info *alter_info); + bool add_versioning_info(THD *thd, Alter_info *alter_info); + + /** Returns true on failure */ + bool check(THD *thd, Alter_info *alter_info); + + /** Returns true if table is versioned */ + bool versioned() const; /** User has added 'WITH SYSTEM VERSIONING' to table definition */ - bool declared_system_versioning; + bool declared_system_versioning : 1; - /** Table described by this structure have enabled system versioning */ - bool versioned; + /** + At least one field was specified 'WITH SYSTEM VERSIONING'. Useful for + error handling. + */ + bool has_versioned_fields : 1; + + /** + At least one field was specified 'WITHOUT SYSTEM VERSIONING'. Useful for + error handling. + */ + bool has_unversioned_fields : 1; }; /** @@ -1791,7 +1814,7 @@ struct Table_scope_and_contents_source_st bool versioned() const { - return system_versioning_info.versioned; + return system_versioning_info.versioned(); } const System_versioning_info *get_system_versioning_info() const { diff --git a/sql/lex.h b/sql/lex.h index da5a288be99..dff6624518a 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -703,6 +703,7 @@ static SYMBOL symbols[] = { { "WHILE", SYM(WHILE_SYM)}, { "WINDOW", SYM(WINDOW_SYM)}, { "WITH", SYM(WITH)}, + { "WITHOUT", SYM(WITHOUT)}, { "WORK", SYM(WORK_SYM)}, { "WRAPPER", SYM(WRAPPER_SYM)}, { "WRITE", SYM(WRITE_SYM)}, diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index f3859b5ba4a..167be4d10fc 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7537,3 +7537,6 @@ ER_SYS_START_FIELD_MUST_BE_BIGINT ER_SYS_END_FIELD_MUST_BE_BIGINT eng "System end field must be of type BIGINT UNSIGNED" + +ER_NO_VERSIONED_FIELDS_IN_VERSIONED_TABLE + eng "Every field specified unversioned in versioned table" diff --git a/sql/sql_class.h b/sql/sql_class.h index 1b920b23a2d..49f62f9ea4e 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -5533,6 +5533,8 @@ class multi_update :public select_result_interceptor // For System Versioning (may need to insert new fields to a table). ha_rows updated_sys_ver; + bool has_vers_fields; + public: multi_update(THD *thd_arg, TABLE_LIST *ut, List *leaves_list, List *fields, List *values, diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 9bae9ecc658..6b75c030374 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -138,7 +138,6 @@ static void sql_kill_user(THD *thd, LEX_USER *user, killed_state state); static bool lock_tables_precheck(THD *thd, TABLE_LIST *tables); static bool execute_show_status(THD *, TABLE_LIST *); static bool check_rename_table(THD *, TABLE_LIST *, TABLE_LIST *); -static bool check_system_versioning(Table_scope_and_contents_source_st *); const char *any_db="*any*"; // Special symbol for check_access @@ -3856,11 +3855,7 @@ mysql_execute_command(THD *thd) goto end_with_restore_list; } - if (System_versioning_info *info= create_info.get_system_versioning_info()) - if (info->add_implicit_fields(thd, &alter_info)) - goto end_with_restore_list; - - if (check_system_versioning(&create_info)) + if (create_info.system_versioning_info.check(thd, &alter_info)) goto end_with_restore_list; /* Check privileges */ @@ -7326,66 +7321,6 @@ bool check_fk_parent_table_access(THD *thd, } - -/**************************************************************************** - Checks related to system versioning -****************************************************************************/ - -static bool check_system_versioning(Table_scope_and_contents_source_st *create_info) -{ - const System_versioning_info *versioning_info = &create_info->system_versioning_info; - - if (!versioning_info->versioned) - return false; - - bool r = false; - - if (!versioning_info->declared_system_versioning) - { - r = true; - my_error(ER_MISSING_WITH_SYSTEM_VERSIONING, MYF(0)); - } - - if (!versioning_info->generated_as_row.start) - { - r = true; - my_error(ER_SYS_START_NOT_SPECIFIED, MYF(0)); - } - - if (!versioning_info->generated_as_row.end) - { - r = true; - my_error(ER_SYS_END_NOT_SPECIFIED, MYF(0)); - } - - if (!versioning_info->period_for_system_time.start || !versioning_info->period_for_system_time.end) - { - r = true; - my_error(ER_MISSING_PERIOD_FOR_SYSTEM_TIME, MYF(0)); - } - - if (!r) - { - if (my_strcasecmp(system_charset_info, - versioning_info->generated_as_row.start->c_ptr(), - versioning_info->period_for_system_time.start->c_ptr())) - { - r = true; - my_error(ER_PERIOD_FOR_SYSTEM_TIME_CONTAINS_WRONG_START_COLUMN, MYF(0)); - } - - if (my_strcasecmp(system_charset_info, - versioning_info->generated_as_row.end->c_ptr(), - versioning_info->period_for_system_time.end->c_ptr())) - { - r = true; - my_error(ER_PERIOD_FOR_SYSTEM_TIME_CONTAINS_WRONG_END_COLUMN, MYF(0)); - } - } - - return r; // false means no error -} - /**************************************************************************** Check stack size; Send error if there isn't enough stack to continue ****************************************************************************/ diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 084e98b143f..cc7f2b0a017 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2133,6 +2133,11 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, packet->append(STRING_WITH_LEN(" GENERATED AS ROW END")); } + if (field->is_versioning_disabled()) + { + packet->append(STRING_WITH_LEN(" WITHOUT SYSTEM VERSIONING")); + } + if (!limited_mysql_mode && print_on_update_clause(field, &def_value, false)) { diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 620f580d688..8b62c71733c 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -2989,6 +2989,8 @@ bool Column_definition::prepare_create_field(uint *blob_columns, pack_flag|= FIELDFLAG_MAYBE_NULL; if (flags & NO_DEFAULT_VALUE_FLAG) pack_flag|= FIELDFLAG_NO_DEFAULT; + if (flags & WITHOUT_SYSTEM_VERSIONING_FLAG) + pack_flag|= FIELDFLAG_WITHOUT_SYSTEM_VERSIONING; DBUG_RETURN(false); } diff --git a/sql/sql_update.cc b/sql/sql_update.cc index acbbf559c63..419ccc1bc94 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -155,6 +155,17 @@ static bool check_fields(THD *thd, List &items) return FALSE; } +static bool check_has_vers_fields(List &items) +{ + List_iterator it(items); + while (Item *item= it++) + { + if (Item_field *item_field= item->field_for_view_update()) + if (!(item_field->field->flags & WITHOUT_SYSTEM_VERSIONING_FLAG)) + return true; + } + return false; +} /** Re-read record if more columns are needed for error message. @@ -355,6 +366,7 @@ int mysql_update(THD *thd, { DBUG_RETURN(1); } + bool has_vers_fields= check_has_vers_fields(fields); if (check_key_in_view(thd, table_list)) { my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "UPDATE"); @@ -763,7 +775,7 @@ int mysql_update(THD *thd, TRG_EVENT_UPDATE)) break; /* purecov: inspected */ - if (table->versioned_by_sql()) + if (has_vers_fields && table->versioned_by_sql()) table->vers_update_fields(); found++; @@ -835,7 +847,7 @@ int mysql_update(THD *thd, { updated++; - if (table->versioned()) + if (has_vers_fields && table->versioned()) { if (table->versioned_by_sql()) { @@ -1671,6 +1683,7 @@ multi_update::multi_update(THD *thd_arg, TABLE_LIST *table_list, transactional_tables(0), ignore(ignore_arg), error_handled(0), prepared(0), updated_sys_ver(0) { + has_vers_fields= check_has_vers_fields(*field_list); } @@ -2176,7 +2189,7 @@ int multi_update::send_data(List ¬_used_values) if (table->default_field && table->update_default_fields(1, ignore)) DBUG_RETURN(1); - if (table->versioned_by_sql()) + if (has_vers_fields && table->versioned_by_sql()) table->vers_update_fields(); if ((error= cur_table->view_check_option(thd, ignore)) != @@ -2226,7 +2239,7 @@ int multi_update::send_data(List ¬_used_values) error= 0; updated--; } - else if (table->versioned()) + else if (has_vers_fields && table->versioned()) { if (table->versioned_by_sql()) { @@ -2511,7 +2524,7 @@ int multi_update::do_updates() goto err2; } } - if (table->versioned_by_sql()) + if (has_vers_fields && table->versioned_by_sql()) table->vers_update_fields(); if ((local_error=table->file->ha_update_row(table->record[1], @@ -2529,7 +2542,7 @@ int multi_update::do_updates() { updated++; - if (table->versioned()) + if (has_vers_fields && table->versioned()) { if (table->versioned_by_sql()) { diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 05c05c3e144..9b11ac0cfe9 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1572,6 +1572,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token WINDOW_SYM %token WHILE_SYM %token WITH /* SQL-2003-R */ +%token WITHOUT /* SQL-2003-R */ %token WITH_CUBE_SYM /* INTERNAL */ %token WITH_ROLLUP_SYM /* INTERNAL */ %token WORK_SYM /* SQL-2003-N */ @@ -5852,7 +5853,7 @@ create_table_option: { System_versioning_info &info= Lex->vers_get_info(); info.declared_system_versioning= true; - info.versioned= true; + Lex->create_info.options|= HA_VERSIONED_TABLE; } ; @@ -6064,7 +6065,6 @@ period_for_system_time: MYSQL_YYABORT; } info.set_period_for_system_time($4, $6); - info.versioned= true; } ; @@ -6165,7 +6165,6 @@ field_def: | opt_generated_always AS ROW_SYM start_or_end { System_versioning_info &info= Lex->vers_get_info(); - info.versioned= true; String *field_name= new (thd->mem_root) String((const char*)Lex->last_field->field_name, system_charset_info); if (!field_name) @@ -6667,6 +6666,16 @@ serial_attribute: new (thd->mem_root) engine_option_value($1, &Lex->last_field->option_list, &Lex->option_list_last); } + | WITH SYSTEM VERSIONING + { + Lex->last_field->versioning = Column_definition::WITH_VERSIONING; + Lex->create_info.system_versioning_info.has_versioned_fields= true; + } + | WITHOUT SYSTEM VERSIONING + { + Lex->last_field->versioning = Column_definition::WITHOUT_VERSIONING; + Lex->create_info.system_versioning_info.has_unversioned_fields= true; + } ; diff --git a/sql/table.cc b/sql/table.cc index 80a7046be1a..3a75fbe8e48 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -2017,6 +2017,9 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, if (f_no_default(pack_flag)) reg_field->flags|= NO_DEFAULT_VALUE_FLAG; + if (f_without_system_versioning(pack_flag)) + reg_field->flags|= WITHOUT_SYSTEM_VERSIONING_FLAG; + if (reg_field->unireg_check == Field::NEXT_NUMBER) share->found_next_number_field= field_ptr; -- cgit v1.2.1 From 82114170bcfad83408723dd6876e4e1a5e8c7148 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Thu, 13 Oct 2016 20:23:27 +0000 Subject: SQL: implicit fields for IB tables + misc cleanups --- sql/handler.cc | 50 ++++++++++++++++++++++++++---------------- sql/handler.h | 51 ++++++++++++------------------------------- sql/sql_lex.h | 4 ++-- sql/sql_parse.cc | 8 ++++--- sql/sql_table.cc | 25 +++++++-------------- sql/sql_yacc.yy | 12 ++++++----- sql/unireg.cc | 66 +++++++++++++++----------------------------------------- 7 files changed, 84 insertions(+), 132 deletions(-) (limited to 'sql') diff --git a/sql/handler.cc b/sql/handler.cc index 174d15194a0..f577f581083 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -6560,7 +6560,8 @@ static bool create_string(MEM_ROOT *mem_root, String **s, const char *value) } static bool create_sys_trx_field_if_missing(THD *thd, const char *field_name, - Alter_info *alter_info, String **s) + Alter_info *alter_info, String **s, + bool integer_fields) { Create_field *f= new (thd->mem_root) Create_field(); if (!f) @@ -6568,8 +6569,17 @@ static bool create_sys_trx_field_if_missing(THD *thd, const char *field_name, f->field_name= field_name; f->charset= system_charset_info; - f->sql_type= MYSQL_TYPE_TIMESTAMP2; - f->length= 6; + if (integer_fields) + { + f->sql_type= MYSQL_TYPE_LONGLONG; + f->flags= UNSIGNED_FLAG; + f->length= MY_INT64_NUM_DECIMAL_DIGITS; + } + else + { + f->sql_type= MYSQL_TYPE_TIMESTAMP2; + f->length= 6; + } f->decimals= 0; if (f->check(thd)) @@ -6582,11 +6592,11 @@ static bool create_sys_trx_field_if_missing(THD *thd, const char *field_name, return false; } -bool System_versioning_info::add_versioning_info(THD *thd, - Alter_info *alter_info) +bool Vers_parse_info::add_versioning_info( + THD *thd, + Alter_info *alter_info, + bool integer_fields) { - DBUG_ASSERT(versioned()); - if (!declared_system_versioning && !has_versioned_fields) return false; @@ -6617,21 +6627,30 @@ bool System_versioning_info::add_versioning_info(THD *thd, return false; return create_sys_trx_field_if_missing(thd, "sys_trx_start", alter_info, - &generated_as_row.start) || + &generated_as_row.start, integer_fields) || create_sys_trx_field_if_missing(thd, "sys_trx_end", alter_info, - &generated_as_row.end) || + &generated_as_row.end, integer_fields) || create_string(thd->mem_root, &period_for_system_time.start, "sys_trx_start") || create_string(thd->mem_root, &period_for_system_time.end, "sys_trx_end"); } -bool System_versioning_info::check(THD *thd, Alter_info *alter_info) +bool Vers_parse_info::check(THD *thd, Alter_info *alter_info, bool integer_fields) { - if (!versioned()) + if (!( + has_versioned_fields || + has_unversioned_fields || + declared_system_versioning || + period_for_system_time.start || + period_for_system_time.end || + generated_as_row.start || + generated_as_row.end)) + { return false; + } - if (add_versioning_info(thd, alter_info)) + if (add_versioning_info(thd, alter_info, integer_fields)) return true; bool r= false; @@ -6714,10 +6733,3 @@ bool System_versioning_info::check(THD *thd, Alter_info *alter_info) return r; // false means no error } -bool System_versioning_info::versioned() const -{ - return has_versioned_fields || has_unversioned_fields || - declared_system_versioning || period_for_system_time.start || - period_for_system_time.end || generated_as_row.start || - generated_as_row.end; -} diff --git a/sql/handler.h b/sql/handler.h index 698703f89ff..11b71a6f03d 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -1669,45 +1669,34 @@ struct Schema_specification_st }; -struct System_versioning_info +struct Vers_parse_info { - System_versioning_info() : - period_for_system_time({NULL, NULL}), - generated_as_row({NULL, NULL}), + Vers_parse_info() : declared_system_versioning(false), has_versioned_fields(false), has_unversioned_fields(false) {} - struct - { - String *start, *end; - } period_for_system_time; - - struct + struct start_end_t { + start_end_t() : + start(NULL), + end(NULL) {} String *start; String *end; - } generated_as_row; + }; + + start_end_t period_for_system_time; + start_end_t generated_as_row; void set_period_for_system_time(String *start, String *end) { period_for_system_time.start = start; period_for_system_time.end = end; } - void set_period_for_system_time() - { - set_period_for_system_time(NULL, NULL); - } - - /** Returns true on failure */ - bool add_versioning_info(THD *thd, Alter_info *alter_info); - /** Returns true on failure */ - bool check(THD *thd, Alter_info *alter_info); - - /** Returns true if table is versioned */ - bool versioned() const; + bool add_versioning_info(THD *thd, Alter_info *alter_info, bool integer_fields); + bool check(THD *thd, Alter_info *alter_info, bool integer_fields); /** User has added 'WITH SYSTEM VERSIONING' to table definition */ bool declared_system_versioning : 1; @@ -1799,7 +1788,7 @@ struct Table_scope_and_contents_source_st bool table_was_deleted; sequence_definition *seq_create_info; - System_versioning_info system_versioning_info; + Vers_parse_info vers_info; void init() { @@ -1814,19 +1803,7 @@ struct Table_scope_and_contents_source_st bool versioned() const { - return system_versioning_info.versioned(); - } - const System_versioning_info *get_system_versioning_info() const - { - if (!versioned()) - return NULL; - return &system_versioning_info; - } - System_versioning_info *get_system_versioning_info() - { - if (!versioned()) - return NULL; - return &system_versioning_info; + return options & HA_VERSIONED_TABLE; } }; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 64e0c9ddaa9..f2fc43074c6 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -3565,9 +3565,9 @@ public: bool add_unit_in_brackets(SELECT_LEX *nselect); void check_automatic_up(enum sub_select_type type); - System_versioning_info &vers_get_info() + Vers_parse_info &vers_get_info() { - return create_info.system_versioning_info; + return create_info.vers_info; } }; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 6b75c030374..611e1c1a021 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3855,9 +3855,6 @@ mysql_execute_command(THD *thd) goto end_with_restore_list; } - if (create_info.system_versioning_info.check(thd, &alter_info)) - goto end_with_restore_list; - /* Check privileges */ if ((res= create_table_precheck(thd, select_tables, create_table))) goto end_with_restore_list; @@ -3878,6 +3875,11 @@ mysql_execute_command(THD *thd) */ if (!(create_info.used_fields & HA_CREATE_USED_ENGINE)) create_info.use_default_db_type(thd); + + DBUG_ASSERT(create_info.db_type); + if (create_info.vers_info.check(thd, &alter_info, create_info.db_type->versioned())) + goto end_with_restore_list; + /* If we are using SET CHARSET without DEFAULT, add an implicit DEFAULT to not confuse old users. (This may change). diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 8b62c71733c..c4bc2028caf 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -3155,12 +3155,9 @@ copy_info_about_generated_fields(Alter_info *dst_alter_info, if (!src_create_info->versioned()) return; - const System_versioning_info *versioning_info = - src_create_info->get_system_versioning_info(); - DBUG_ASSERT(versioning_info); - const char *row_start_field = versioning_info->generated_as_row.start->c_ptr(); + const char *row_start_field = src_create_info->vers_info.generated_as_row.start->c_ptr(); DBUG_ASSERT(row_start_field); - const char *row_end_field = versioning_info->generated_as_row.end->c_ptr(); + const char *row_end_field = src_create_info->vers_info.generated_as_row.end->c_ptr(); DBUG_ASSERT(row_end_field); List_iterator it(dst_alter_info->create_list); @@ -3249,8 +3246,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, } select_field_pos= alter_info->create_list.elements - select_field_count; - const System_versioning_info *versioning_info = - create_info->get_system_versioning_info(); for (field_no=0; (sql_field=it++) ; field_no++) { @@ -3478,15 +3473,15 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, if (sql_field->stored_in_db()) record_offset+= sql_field->pack_length; - if (versioning_info) + if (create_info->versioned()) { const bool is_generated_as_row_start = !my_strcasecmp(system_charset_info, - versioning_info->generated_as_row.start->c_ptr(), + create_info->vers_info.generated_as_row.start->c_ptr(), sql_field->field_name); const bool is_generated_as_row_end = !my_strcasecmp(system_charset_info, - versioning_info->generated_as_row.end->c_ptr(), + create_info->vers_info.generated_as_row.end->c_ptr(), sql_field->field_name); if (is_generated_as_row_start && is_generated_as_row_end) { @@ -4351,13 +4346,9 @@ vers_prepare_keys(THD *thd, { DBUG_ASSERT(create_info->versioned()); - const System_versioning_info *versioning_info= - create_info->get_system_versioning_info(); - - DBUG_ASSERT(versioning_info); - const char *row_start_field= versioning_info->generated_as_row.start->c_ptr(); + const char *row_start_field= create_info->vers_info.generated_as_row.start->c_ptr(); DBUG_ASSERT(row_start_field); - const char *row_end_field= versioning_info->generated_as_row.end->c_ptr(); + const char *row_end_field= create_info->vers_info.generated_as_row.end->c_ptr(); DBUG_ASSERT(row_end_field); List_iterator key_it(alter_info->key_list); @@ -4384,7 +4375,7 @@ vers_prepare_keys(THD *thd, continue; // Key already contains Sys_start or Sys_end const LEX_STRING &lex_sys_end= - versioning_info->generated_as_row.end->lex_string(); + create_info->vers_info.generated_as_row.end->lex_string(); Key_part_spec *key_part_sys_end_col= new(thd->mem_root) Key_part_spec(lex_sys_end, 0); key->columns.push_back(key_part_sys_end_col); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 9b11ac0cfe9..9d5640280e0 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -5851,7 +5851,7 @@ create_table_option: } | WITH SYSTEM VERSIONING { - System_versioning_info &info= Lex->vers_get_info(); + Vers_parse_info &info= Lex->vers_get_info(); info.declared_system_versioning= true; Lex->create_info.options|= HA_VERSIONED_TABLE; } @@ -6058,7 +6058,7 @@ period_for_system_time: // If FOR_SYM is followed by SYSTEM_TIME_SYM then they are merged to: FOR_SYSTEM_TIME_SYM . PERIOD_SYM FOR_SYSTEM_TIME_SYM '(' period_for_system_time_column_id ',' period_for_system_time_column_id ')' { - System_versioning_info &info= Lex->vers_get_info(); + Vers_parse_info &info= Lex->vers_get_info(); if (!my_strcasecmp(system_charset_info, $4->c_ptr(), $6->c_ptr())) { my_error(ER_SYS_START_AND_SYS_END_SAME, MYF(0), $4->c_ptr()); @@ -6164,7 +6164,7 @@ field_def: vcol_opt_specifier vcol_opt_attribute | opt_generated_always AS ROW_SYM start_or_end { - System_versioning_info &info= Lex->vers_get_info(); + Vers_parse_info &info= Lex->vers_get_info(); String *field_name= new (thd->mem_root) String((const char*)Lex->last_field->field_name, system_charset_info); if (!field_name) @@ -6669,12 +6669,14 @@ serial_attribute: | WITH SYSTEM VERSIONING { Lex->last_field->versioning = Column_definition::WITH_VERSIONING; - Lex->create_info.system_versioning_info.has_versioned_fields= true; + Lex->create_info.vers_info.has_versioned_fields= true; + Lex->create_info.options|= HA_VERSIONED_TABLE; } | WITHOUT SYSTEM VERSIONING { Lex->last_field->versioning = Column_definition::WITHOUT_VERSIONING; - Lex->create_info.system_versioning_info.has_unversioned_fields= true; + Lex->create_info.vers_info.has_unversioned_fields= true; + Lex->create_info.options|= HA_VERSIONED_TABLE; } ; diff --git a/sql/unireg.cc b/sql/unireg.cc index 0465626c132..445d3441af7 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -87,31 +87,28 @@ static uchar *extra2_write(uchar *pos, enum extra2_frm_value_type type, return extra2_write(pos, type, reinterpret_cast(str)); } -static bool -versioned(HA_CREATE_INFO *create_info) -{ - return create_info->versioned(); -} +static const bool ROW_START = true; +static const bool ROW_END = false; -static uint16 -get_row_start_field(HA_CREATE_INFO *create_info, List &create_fields) +inline +uint16 +vers_get_field(HA_CREATE_INFO *create_info, List &create_fields, bool row_start) { - DBUG_ASSERT(versioned(create_info)); + DBUG_ASSERT(create_info->versioned()); List_iterator it(create_fields); - Create_field*sql_field = NULL; - - const System_versioning_info *versioning_info = - create_info->get_system_versioning_info(); - DBUG_ASSERT(versioning_info); + Create_field *sql_field = NULL; - const char *row_start_field = versioning_info->generated_as_row.start->c_ptr(); - DBUG_ASSERT(row_start_field); + const char *row_field = + row_start ? + create_info->vers_info.generated_as_row.start->c_ptr() : + create_info->vers_info.generated_as_row.end->c_ptr(); + DBUG_ASSERT(row_field); for (unsigned field_no = 0; (sql_field = it++); ++field_no) { if (!my_strcasecmp(system_charset_info, - row_start_field, + row_field, sql_field->field_name)) { DBUG_ASSERT(field_no <= uint16(~0U)); @@ -123,35 +120,6 @@ get_row_start_field(HA_CREATE_INFO *create_info, List &create_fiel return 0; } -static uint16 -get_row_end_field(HA_CREATE_INFO *create_info, List &create_fields) -{ - DBUG_ASSERT(versioned(create_info)); - - List_iterator it(create_fields); - Create_field*sql_field = NULL; - - const System_versioning_info *versioning_info = - create_info->get_system_versioning_info(); - DBUG_ASSERT(versioning_info); - - const char *row_end_field = versioning_info->generated_as_row.end->c_ptr(); - DBUG_ASSERT(row_end_field); - - for (unsigned field_no = 0; (sql_field = it++); ++field_no) - { - if (!my_strcasecmp(system_charset_info, - row_end_field, - sql_field->field_name)) - { - DBUG_ASSERT(field_no <= uint16(~0U)); - return uint16(field_no); - } - } - - DBUG_ASSERT(0); /* Not Reachable */ - return 0; -} /** Create a frm (table definition) file @@ -285,7 +253,7 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table, if (gis_extra2_len) extra2_size+= 1 + (gis_extra2_len > 255 ? 3 : 1) + gis_extra2_len; - if (versioned(create_info)) + if (create_info->versioned()) { extra2_size+= 1 + 1 + 2 * sizeof(uint16); } @@ -345,13 +313,13 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table, } #endif /*HAVE_SPATIAL*/ - if (versioned(create_info)) + if (create_info->versioned()) { *pos++= EXTRA2_PERIOD_FOR_SYSTEM_TIME; *pos++= 2 * sizeof(uint16); - int2store(pos, get_row_start_field(create_info, create_fields)); + int2store(pos, vers_get_field(create_info, create_fields, ROW_START)); pos+= sizeof(uint16); - int2store(pos, get_row_end_field(create_info, create_fields)); + int2store(pos, vers_get_field(create_info, create_fields, ROW_END)); pos+= sizeof(uint16); } -- cgit v1.2.1 From 6ccae7369b2d5a9b38cbad480e393ee9a3a7fec8 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Fri, 14 Oct 2016 07:24:59 +0000 Subject: SQL: fixed LEFT JOIN, RIGHT JOIN --- sql/sql_lex.cc | 2 +- sql/sql_lex.h | 2 +- sql/sql_select.cc | 43 +++++++++++++++++++++++++++++++++---------- sql/table.h | 1 + 4 files changed, 36 insertions(+), 12 deletions(-) (limited to 'sql') diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 34b35d60c14..242923de546 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -2200,7 +2200,7 @@ void st_select_lex::init_query() join= 0; having= prep_having= where= prep_where= 0; cond_pushed_into_where= cond_pushed_into_having= 0; - saved_conds= 0; + saved_where= 0; olap= UNSPECIFIED_OLAP_TYPE; having_fix_field= 0; context.select_lex= this; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index f2fc43074c6..826ebd03063 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -810,7 +810,7 @@ public: Item *prep_having;/* saved HAVING clause for prepared statement processing */ Item *cond_pushed_into_where; /* condition pushed into the select's WHERE */ Item *cond_pushed_into_having; /* condition pushed into the select's HAVING */ - Item *saved_conds; + Item *saved_where; /* Saved values of the WHERE and HAVING clauses*/ Item::cond_result cond_value, having_value; /* point on lex in which it was created, used in view subquery detection */ diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 331421ecb6d..9aff4c3e731 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -668,7 +668,7 @@ setup_without_group(THD *thd, Ref_ptr_array ref_pointer_array, } static int -setup_for_system_time(THD *thd, TABLE_LIST *tables, COND **conds, SELECT_LEX *select_lex) +setup_for_system_time(THD *thd, TABLE_LIST *tables, COND **where_expr, SELECT_LEX *select_lex) { DBUG_ENTER("setup_for_system_time"); @@ -697,23 +697,46 @@ setup_for_system_time(THD *thd, TABLE_LIST *tables, COND **conds, SELECT_LEX *se because they must outlive execution phase for multiple executions. */ arena= thd->activate_stmt_arena_if_needed(&backup); - if (select_lex->saved_conds) + if (select_lex->saved_where) { DBUG_ASSERT(thd->stmt_arena->is_sp_execute()); - *conds= select_lex->saved_conds; + *where_expr= select_lex->saved_where; } else if (thd->stmt_arena->is_sp_execute()) { - if (thd->stmt_arena->is_stmt_execute()) - *conds= 0; - else if (*conds) - select_lex->saved_conds= (*conds)->copy_andor_structure(thd); + if (thd->stmt_arena->is_stmt_execute()) // SP executed second time (STMT_EXECUTED) + *where_expr= 0; + else if (*where_expr) // SP executed first time (STMT_INITIALIZED_FOR_SP) + /* copy_andor_structure() is required since this andor tree + is modified later (and on shorter arena) */ + select_lex->saved_where= (*where_expr)->copy_andor_structure(thd); } for (table= tables; table; table= table->next_local) { if (table->table && table->table->versioned()) { + COND** dst_cond; + if (table->on_expr) + { + if (table->saved_on_expr) // same logic as saved_where + { + DBUG_ASSERT(thd->stmt_arena->is_sp_execute()); + table->on_expr= table->saved_on_expr; + } + else if (thd->stmt_arena->is_sp_execute()) + { + if (thd->stmt_arena->is_stmt_execute()) // SP executed second time (STMT_EXECUTED) + table->on_expr= 0; + else if (table->on_expr) // SP executed first time (STMT_INITIALIZED_FOR_SP) + table->saved_on_expr= table->on_expr->copy_andor_structure(thd); + } + dst_cond= &table->on_expr; + } + else + { + dst_cond= where_expr; + } Field *fstart= table->table->vers_start_field(); Field *fend= table->table->vers_end_field(); @@ -782,15 +805,15 @@ setup_for_system_time(THD *thd, TABLE_LIST *tables, COND **conds, SELECT_LEX *se if (cond1) { cond1= and_items(thd, - *conds, + *dst_cond, and_items(thd, cond2, cond1)); if (arena) - *conds= cond1; + *dst_cond= cond1; else - thd->change_item_tree(conds, cond1); + thd->change_item_tree(dst_cond, cond1); table->vers_moved_to_where= true; } diff --git a/sql/table.h b/sql/table.h index cc575602c49..e2c055b14d5 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1908,6 +1908,7 @@ struct TABLE_LIST char *db, *alias, *table_name, *schema_table_name; char *option; /* Used by cache index */ Item *on_expr; /* Used with outer join */ + Item *saved_on_expr; /* Used with SP and System Versioning */ Item *sj_on_expr; /* -- cgit v1.2.1 From a03da4fa7886f41641d379362a4599b5f2f8fe52 Mon Sep 17 00:00:00 2001 From: Kosov Eugene Date: Sat, 15 Oct 2016 00:54:32 +0300 Subject: Style: removed unused header, other small fixes --- sql/field.h | 2 -- sql/handler.cc | 15 +++++++-------- 2 files changed, 7 insertions(+), 10 deletions(-) (limited to 'sql') diff --git a/sql/field.h b/sql/field.h index bf4f8b5ce5c..a52fb656b39 100644 --- a/sql/field.h +++ b/sql/field.h @@ -33,8 +33,6 @@ #include "compat56.h" #include "sql_type.h" /* Type_std_attributes */ -#include - class Send_field; class Copy_field; class Protocol; diff --git a/sql/handler.cc b/sql/handler.cc index f577f581083..e3b8d6ace3f 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -6559,9 +6559,9 @@ static bool create_string(MEM_ROOT *mem_root, String **s, const char *value) return *s == NULL; } -static bool create_sys_trx_field_if_missing(THD *thd, const char *field_name, - Alter_info *alter_info, String **s, - bool integer_fields) +static bool create_sys_trx_field(THD *thd, const char *field_name, + Alter_info *alter_info, String **s, + bool integer_fields) { Create_field *f= new (thd->mem_root) Create_field(); if (!f) @@ -6626,10 +6626,10 @@ bool Vers_parse_info::add_versioning_info( period_for_system_time.start || period_for_system_time.end) return false; - return create_sys_trx_field_if_missing(thd, "sys_trx_start", alter_info, - &generated_as_row.start, integer_fields) || - create_sys_trx_field_if_missing(thd, "sys_trx_end", alter_info, - &generated_as_row.end, integer_fields) || + return create_sys_trx_field(thd, "sys_trx_start", alter_info, + &generated_as_row.start, integer_fields) || + create_sys_trx_field(thd, "sys_trx_end", alter_info, + &generated_as_row.end, integer_fields) || create_string(thd->mem_root, &period_for_system_time.start, "sys_trx_start") || create_string(thd->mem_root, &period_for_system_time.end, @@ -6732,4 +6732,3 @@ bool Vers_parse_info::check(THD *thd, Alter_info *alter_info, bool integer_field return r; // false means no error } - -- cgit v1.2.1 From 0a8fd20ad6860bbf04f3bbf573b64045d0da87c5 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Sat, 15 Oct 2016 08:21:11 +0000 Subject: SQL: NOT NULL for implicit fields (fixes #46) --- sql/handler.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'sql') diff --git a/sql/handler.cc b/sql/handler.cc index e3b8d6ace3f..0b079676fc9 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -6567,20 +6567,21 @@ static bool create_sys_trx_field(THD *thd, const char *field_name, if (!f) return true; + memset(f, 0, sizeof(*f)); f->field_name= field_name; f->charset= system_charset_info; if (integer_fields) { f->sql_type= MYSQL_TYPE_LONGLONG; - f->flags= UNSIGNED_FLAG; + f->flags= UNSIGNED_FLAG | NOT_NULL_FLAG; f->length= MY_INT64_NUM_DECIMAL_DIGITS; } else { f->sql_type= MYSQL_TYPE_TIMESTAMP2; + f->flags= NOT_NULL_FLAG; f->length= 6; } - f->decimals= 0; if (f->check(thd)) return true; -- cgit v1.2.1 From 5cf3dd79fafc4918360aa45af47054de3a1d034f Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 18 Oct 2016 08:07:22 +0000 Subject: SQL: JOIN + WHERE in SP --- sql/sql_select.cc | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) (limited to 'sql') diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 9aff4c3e731..eec6f742d9d 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -681,6 +681,7 @@ setup_for_system_time(THD *thd, TABLE_LIST *tables, COND **where_expr, SELECT_LE && !is_prepare && !thd->stmt_arena->is_sp_execute()) { + // statement is already prepared DBUG_RETURN(0); } @@ -716,27 +717,30 @@ setup_for_system_time(THD *thd, TABLE_LIST *tables, COND **where_expr, SELECT_LE { if (table->table && table->table->versioned()) { - COND** dst_cond; - if (table->on_expr) + COND** dst_cond= where_expr; + if (table->saved_on_expr) // same logic as saved_where { - if (table->saved_on_expr) // same logic as saved_where + DBUG_ASSERT(thd->stmt_arena->is_sp_execute()); + if (table->on_expr) { - DBUG_ASSERT(thd->stmt_arena->is_sp_execute()); table->on_expr= table->saved_on_expr; + dst_cond= &table->on_expr; } - else if (thd->stmt_arena->is_sp_execute()) + else { - if (thd->stmt_arena->is_stmt_execute()) // SP executed second time (STMT_EXECUTED) - table->on_expr= 0; - else if (table->on_expr) // SP executed first time (STMT_INITIALIZED_FOR_SP) - table->saved_on_expr= table->on_expr->copy_andor_structure(thd); + // on_expr was moved to WHERE (see below: Add ON expression to the WHERE) + *dst_cond= and_items(thd, + *where_expr, + table->saved_on_expr); } - dst_cond= &table->on_expr; } - else + else if (table->on_expr) { - dst_cond= where_expr; + dst_cond= &table->on_expr; + if (thd->stmt_arena->state == Query_arena::STMT_INITIALIZED_FOR_SP) + table->saved_on_expr= table->on_expr->copy_andor_structure(thd); } + Field *fstart= table->table->vers_start_field(); Field *fend= table->table->vers_end_field(); -- cgit v1.2.1 From 01c9d1c97fdfc5b72e967aaac0ebf29a039bd3d5 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 18 Oct 2016 16:35:52 +0000 Subject: SQL: SP idempotency fix Fixes #52 --- sql/sql_select.cc | 44 +++++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 17 deletions(-) (limited to 'sql') diff --git a/sql/sql_select.cc b/sql/sql_select.cc index eec6f742d9d..4e7ca4663b1 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -701,44 +701,54 @@ setup_for_system_time(THD *thd, TABLE_LIST *tables, COND **where_expr, SELECT_LE if (select_lex->saved_where) { DBUG_ASSERT(thd->stmt_arena->is_sp_execute()); - *where_expr= select_lex->saved_where; + /* 2. this copy_andor_structure() is also required by the same reason */ + *where_expr= select_lex->saved_where->copy_andor_structure(thd); } else if (thd->stmt_arena->is_sp_execute()) { if (thd->stmt_arena->is_stmt_execute()) // SP executed second time (STMT_EXECUTED) *where_expr= 0; else if (*where_expr) // SP executed first time (STMT_INITIALIZED_FOR_SP) - /* copy_andor_structure() is required since this andor tree + /* 1. copy_andor_structure() is required since this andor tree is modified later (and on shorter arena) */ select_lex->saved_where= (*where_expr)->copy_andor_structure(thd); } - for (table= tables; table; table= table->next_local) + /* We have to save also non-versioned on_expr since we may have + conjuction of versioned + non-versioned */ + if (thd->stmt_arena->is_sp_execute()) { - if (table->table && table->table->versioned()) + for (table= tables; table; table= table->next_local) { - COND** dst_cond= where_expr; + if (!table->table) + continue; + if (table->saved_on_expr) // same logic as saved_where { - DBUG_ASSERT(thd->stmt_arena->is_sp_execute()); if (table->on_expr) - { - table->on_expr= table->saved_on_expr; - dst_cond= &table->on_expr; - } + table->on_expr= table->saved_on_expr->copy_andor_structure(thd); else - { // on_expr was moved to WHERE (see below: Add ON expression to the WHERE) - *dst_cond= and_items(thd, + *where_expr= and_items(thd, *where_expr, - table->saved_on_expr); - } + table->saved_on_expr->copy_andor_structure(thd)); + } + else if (table->on_expr && + thd->stmt_arena->state == Query_arena::STMT_INITIALIZED_FOR_SP) + { + table->saved_on_expr= table->on_expr->copy_andor_structure(thd); } - else if (table->on_expr) + } + } + + for (table= tables; table; table= table->next_local) + { + if (table->table && table->table->versioned()) + { + COND** dst_cond= where_expr; + if (table->on_expr) { dst_cond= &table->on_expr; - if (thd->stmt_arena->state == Query_arena::STMT_INITIALIZED_FOR_SP) - table->saved_on_expr= table->on_expr->copy_andor_structure(thd); } Field *fstart= table->table->vers_start_field(); -- cgit v1.2.1 From e0942286313f78e497a36394111b4546d5a20ed0 Mon Sep 17 00:00:00 2001 From: Kosov Eugene Date: Thu, 20 Oct 2016 23:05:55 +0300 Subject: SQL: hide implicitly added columns from SELECT * --- sql/field.h | 3 + sql/field.h.orig | 4160 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ sql/handler.cc | 4 +- sql/sql_base.cc | 8 + sql/sql_table.cc | 2 + sql/table.cc | 3 + 6 files changed, 4178 insertions(+), 2 deletions(-) create mode 100644 sql/field.h.orig (limited to 'sql') diff --git a/sql/field.h b/sql/field.h index a52fb656b39..e50595df11a 100644 --- a/sql/field.h +++ b/sql/field.h @@ -4281,6 +4281,8 @@ bool check_expression(Virtual_column_info *vcol, const char *name, #define FIELDFLAG_BITFIELD 512U // mangled with decimals! #define FIELDFLAG_BLOB 1024U // mangled with decimals! #define FIELDFLAG_GEOM 2048U // mangled with decimals! +// Do not show field in SELECT *. Hope GEOM field is never hidden. +#define FIELDFLAG_HIDDEN 2048U #define FIELDFLAG_TREAT_BIT_AS_CHAR 4096U /* use Field_bit_as_char */ #define FIELDFLAG_LONG_DECIMAL 8192U @@ -4312,5 +4314,6 @@ bool check_expression(Virtual_column_info *vcol, const char *name, #define f_bit_as_char(x) ((x) & FIELDFLAG_TREAT_BIT_AS_CHAR) #define f_is_hex_escape(x) ((x) & FIELDFLAG_HEX_ESCAPE) #define f_without_system_versioning(x) ((x) & FIELDFLAG_WITHOUT_SYSTEM_VERSIONING) +#define f_hidden(x) ((x) & FIELDFLAG_HIDDEN) #endif /* FIELD_INCLUDED */ diff --git a/sql/field.h.orig b/sql/field.h.orig new file mode 100644 index 00000000000..03d9ba06ac4 --- /dev/null +++ b/sql/field.h.orig @@ -0,0 +1,4160 @@ +#ifndef FIELD_INCLUDED +#define FIELD_INCLUDED +/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. + Copyright (c) 2008, 2017, MariaDB Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/* + Because of the function make_new_field() all field classes that have static + variables must declare the size_of() member function. +*/ + +#ifdef USE_PRAGMA_INTERFACE +#pragma interface /* gcc class implementation */ +#endif + +#include "mysqld.h" /* system_charset_info */ +#include "table.h" /* TABLE */ +#include "sql_string.h" /* String */ +#include "my_decimal.h" /* my_decimal */ +#include "sql_error.h" /* Sql_condition */ +#include "compat56.h" +#include "sql_type.h" /* Type_std_attributes */ + +class Send_field; +class Copy_field; +class Protocol; +class Create_field; +class Relay_log_info; +class Field; +class Column_statistics; +class Column_statistics_collected; +class Item_func; +class Item_bool_func; +class Item_equal; + +enum enum_check_fields +{ + CHECK_FIELD_IGNORE, + CHECK_FIELD_WARN, + CHECK_FIELD_ERROR_FOR_NULL +}; + +/* + Common declarations for Field and Item +*/ +class Value_source +{ +protected: + + // Parameters for warning and note generation + class Warn_filter + { + bool m_want_warning_edom; + bool m_want_note_truncated_spaces; + public: + Warn_filter(bool want_warning_edom, bool want_note_truncated_spaces) : + m_want_warning_edom(want_warning_edom), + m_want_note_truncated_spaces(want_note_truncated_spaces) + { } + Warn_filter(const THD *thd); + bool want_warning_edom() const + { return m_want_warning_edom; } + bool want_note_truncated_spaces() const + { return m_want_note_truncated_spaces; } + }; + class Warn_filter_all: public Warn_filter + { + public: + Warn_filter_all() :Warn_filter(true, true) { } + }; + + class Converter_double_to_longlong + { + protected: + bool m_error; + longlong m_result; + public: + Converter_double_to_longlong(double nr, bool unsigned_flag); + longlong result() const { return m_result; } + bool error() const { return m_error; } + void push_warning(THD *thd, double nr, bool unsigned_flag); + }; + class Converter_double_to_longlong_with_warn: + public Converter_double_to_longlong + { + public: + Converter_double_to_longlong_with_warn(THD *thd, double nr, + bool unsigned_flag) + :Converter_double_to_longlong(nr, unsigned_flag) + { + if (m_error) + push_warning(thd, nr, unsigned_flag); + } + Converter_double_to_longlong_with_warn(double nr, bool unsigned_flag) + :Converter_double_to_longlong(nr, unsigned_flag) + { + if (m_error) + push_warning(current_thd, nr, unsigned_flag); + } + }; + + // String-to-number converters + class Converter_string_to_number + { + protected: + char *m_end_of_num; // Where the low-level conversion routine stopped + int m_error; // The error code returned by the low-level routine + bool m_edom; // If EDOM-alike error happened during conversion + /** + Check string-to-number conversion and produce a warning if + - could not convert any digits (EDOM-alike error) + - found garbage at the end of the string + - found extra spaces at the end (a note) + See also Field_num::check_edom_and_truncation() for a similar function. + + @param thd - the thread that will be used to generate warnings. + Can be NULL (which means current_thd will be used + if a warning is really necessary). + @param type - name of the data type + (e.g. "INTEGER", "DECIMAL", "DOUBLE") + @param cs - character set of the original string + @param str - the original string + @param end - the end of the string + @param allow_notes - tells if trailing space notes should be displayed + or suppressed. + + Unlike Field_num::check_edom_and_truncation(), this function does not + distinguish between EDOM and truncation and reports the same warning for + both cases. Perhaps we should eventually print different warnings, + to make the explicit CAST work closer to the implicit cast in + Field_xxx::store(). + */ + void check_edom_and_truncation(THD *thd, Warn_filter filter, + const char *type, + CHARSET_INFO *cs, + const char *str, + size_t length) const; + public: + int error() const { return m_error; } + }; + + class Converter_strntod: public Converter_string_to_number + { + double m_result; + public: + Converter_strntod(CHARSET_INFO *cs, const char *str, size_t length) + { + m_result= my_strntod(cs, (char *) str, length, &m_end_of_num, &m_error); + // strntod() does not set an error if the input string was empty + m_edom= m_error !=0 || str == m_end_of_num; + } + double result() const { return m_result; } + }; + + class Converter_string_to_longlong: public Converter_string_to_number + { + protected: + longlong m_result; + public: + longlong result() const { return m_result; } + }; + + class Converter_strntoll: public Converter_string_to_longlong + { + public: + Converter_strntoll(CHARSET_INFO *cs, const char *str, size_t length) + { + m_result= my_strntoll(cs, str, length, 10, &m_end_of_num, &m_error); + /* + All non-zero errors means EDOM error. + strntoll() does not set an error if the input string was empty. + Check it here. + Notice the different with the same condition in Converter_strntoll10. + */ + m_edom= m_error != 0 || str == m_end_of_num; + } + }; + + class Converter_strtoll10: public Converter_string_to_longlong + { + public: + Converter_strtoll10(CHARSET_INFO *cs, const char *str, size_t length) + { + m_end_of_num= (char *) str + length; + m_result= (*(cs->cset->strtoll10))(cs, str, &m_end_of_num, &m_error); + /* + Negative error means "good negative number". + Only a positive m_error value means a real error. + strtoll10() sets error to MY_ERRNO_EDOM in case of an empty string, + so we don't have to additionally catch empty strings here. + */ + m_edom= m_error > 0; + } + }; + + class Converter_str2my_decimal: public Converter_string_to_number + { + public: + Converter_str2my_decimal(uint mask, + CHARSET_INFO *cs, const char *str, size_t length, + my_decimal *buf) + { + m_error= str2my_decimal(mask, str, length, cs, + buf, (const char **) &m_end_of_num); + // E_DEC_TRUNCATED means a very minor truncation: '1e-100' -> 0 + m_edom= m_error && m_error != E_DEC_TRUNCATED; + } + }; + + + // String-to-number converters with automatic warning generation + class Converter_strntod_with_warn: public Converter_strntod + { + public: + Converter_strntod_with_warn(THD *thd, Warn_filter filter, + CHARSET_INFO *cs, + const char *str, size_t length) + :Converter_strntod(cs, str, length) + { + check_edom_and_truncation(thd, filter, "DOUBLE", cs, str, length); + } + }; + + class Converter_strntoll_with_warn: public Converter_strntoll + { + public: + Converter_strntoll_with_warn(THD *thd, Warn_filter filter, + CHARSET_INFO *cs, + const char *str, size_t length) + :Converter_strntoll(cs, str, length) + { + check_edom_and_truncation(thd, filter, "INTEGER", cs, str, length); + } + }; + + class Converter_strtoll10_with_warn: public Converter_strtoll10 + { + public: + Converter_strtoll10_with_warn(THD *thd, Warn_filter filter, + CHARSET_INFO *cs, + const char *str, size_t length) + :Converter_strtoll10(cs, str, length) + { + check_edom_and_truncation(thd, filter, "INTEGER", cs, str, length); + } + }; + + class Converter_str2my_decimal_with_warn: public Converter_str2my_decimal + { + public: + Converter_str2my_decimal_with_warn(THD *thd, Warn_filter filter, + uint mask, CHARSET_INFO *cs, + const char *str, size_t length, + my_decimal *buf) + :Converter_str2my_decimal(mask, cs, str, length, buf) + { + check_edom_and_truncation(thd, filter, "DECIMAL", cs, str, length); + } + }; + + + // String-to-number convertion methods for the old code compatibility + longlong longlong_from_string_with_check(CHARSET_INFO *cs, const char *cptr, + const char *end) const + { + /* + TODO: Give error if we wanted a signed integer and we got an unsigned + one + + Notice, longlong_from_string_with_check() honors thd->no_error, because + it's used to handle queries like this: + SELECT COUNT(@@basedir); + and is called when Item_func_get_system_var::update_null_value() + suppresses warnings and then calls val_int(). + The other methods {double|decimal}_from_string_with_check() ignore + thd->no_errors, because they are not used for update_null_value() + and they always allow all kind of warnings. + */ + THD *thd= current_thd; + return Converter_strtoll10_with_warn(thd, Warn_filter(thd), + cs, cptr, end - cptr).result(); + } + + double double_from_string_with_check(CHARSET_INFO *cs, const char *cptr, + const char *end) const + { + return Converter_strntod_with_warn(NULL, Warn_filter_all(), + cs, cptr, end - cptr).result(); + } + my_decimal *decimal_from_string_with_check(my_decimal *decimal_value, + CHARSET_INFO *cs, + const char *cptr, + const char *end) + { + Converter_str2my_decimal_with_warn(NULL, Warn_filter_all(), + E_DEC_FATAL_ERROR & ~E_DEC_BAD_NUM, + cs, cptr, end - cptr, decimal_value); + return decimal_value; + } + + longlong longlong_from_hex_hybrid(const char *str, uint32 length) + { + const char *end= str + length; + const char *ptr= end - MY_MIN(length, sizeof(longlong)); + ulonglong value= 0; + for ( ; ptr != end ; ptr++) + value= (value << 8) + (ulonglong) (uchar) *ptr; + return (longlong) value; + } + + longlong longlong_from_string_with_check(const String *str) const + { + return longlong_from_string_with_check(str->charset(), + str->ptr(), str->end()); + } + double double_from_string_with_check(const String *str) const + { + return double_from_string_with_check(str->charset(), + str->ptr(), str->end()); + } + my_decimal *decimal_from_string_with_check(my_decimal *decimal_value, + const String *str) + { + return decimal_from_string_with_check(decimal_value, str->charset(), + str->ptr(), str->end()); + } + // End of String-to-number conversion methods + +public: + /* + The enumeration Subst_constraint is currently used only in implementations + of the virtual function subst_argument_checker. + */ + enum Subst_constraint + { + ANY_SUBST, /* Any substitution for a field is allowed */ + IDENTITY_SUBST /* Substitution for a field is allowed if any two + different values of the field type are not equal */ + }; + /* + Item context attributes. + Comparison functions pass their attributes to propagate_equal_fields(). + For exmple, for string comparison, the collation of the comparison + operation is important inside propagate_equal_fields(). + */ + class Context + { + /* + Which type of propagation is allowed: + - ANY_SUBST (loose equality, according to the collation), or + - IDENTITY_SUBST (strict binary equality). + */ + Subst_constraint m_subst_constraint; + /* + Comparison type. + Impostant only when ANY_SUBSTS. + */ + Item_result m_compare_type; + /* + Collation of the comparison operation. + Important only when ANY_SUBST. + */ + CHARSET_INFO *m_compare_collation; + public: + Context(Subst_constraint subst, Item_result type, CHARSET_INFO *cs) + :m_subst_constraint(subst), + m_compare_type(type), + m_compare_collation(cs) { } + Subst_constraint subst_constraint() const { return m_subst_constraint; } + Item_result compare_type() const + { + DBUG_ASSERT(m_subst_constraint == ANY_SUBST); + return m_compare_type; + } + CHARSET_INFO *compare_collation() const + { + DBUG_ASSERT(m_subst_constraint == ANY_SUBST); + return m_compare_collation; + } + }; + class Context_identity: public Context + { // Use this to request only exact value, no invariants. + public: + Context_identity() + :Context(IDENTITY_SUBST, STRING_RESULT, &my_charset_bin) { } + }; + class Context_boolean: public Context + { // Use this when an item is [a part of] a boolean expression + public: + Context_boolean() :Context(ANY_SUBST, INT_RESULT, &my_charset_bin) { } + }; +}; + + +#define STORAGE_TYPE_MASK 7 +#define COLUMN_FORMAT_MASK 7 +#define COLUMN_FORMAT_SHIFT 3 + +/* The length of the header part for each virtual column in the .frm file */ +#define FRM_VCOL_OLD_HEADER_SIZE(b) (3 + MY_TEST(b)) +#define FRM_VCOL_NEW_BASE_SIZE 16 +#define FRM_VCOL_NEW_HEADER_SIZE 6 + +class Count_distinct_field; + +struct ha_field_option_struct; + +struct st_cache_field; +int field_conv(Field *to,Field *from); +int truncate_double(double *nr, uint field_length, uint dec, + bool unsigned_flag, double max_value); + +inline uint get_enum_pack_length(int elements) +{ + return elements < 256 ? 1 : 2; +} + +inline uint get_set_pack_length(int elements) +{ + uint len= (elements + 7) / 8; + return len > 4 ? 8 : len; +} + + +/** + Tests if field type is temporal and has date part, + i.e. represents DATE, DATETIME or TIMESTAMP types in SQL. + + @param type Field type, as returned by field->type(). + @retval true If field type is temporal type with date part. + @retval false If field type is not temporal type with date part. +*/ +inline bool is_temporal_type_with_date(enum_field_types type) +{ + switch (type) + { + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_TIMESTAMP: + return true; + case MYSQL_TYPE_DATETIME2: + case MYSQL_TYPE_TIMESTAMP2: + DBUG_ASSERT(0); // field->real_type() should not get to here. + default: + return false; + } +} + + +/** + Recognizer for concrete data type (called real_type for some reason), + returning true if it is one of the TIMESTAMP types. +*/ +inline bool is_timestamp_type(enum_field_types type) +{ + return type == MYSQL_TYPE_TIMESTAMP || type == MYSQL_TYPE_TIMESTAMP2; +} + + +/** + Convert temporal real types as retuned by field->real_type() + to field type as returned by field->type(). + + @param real_type Real type. + @retval Field type. +*/ +inline enum_field_types real_type_to_type(enum_field_types real_type) +{ + switch (real_type) + { + case MYSQL_TYPE_TIME2: + return MYSQL_TYPE_TIME; + case MYSQL_TYPE_DATETIME2: + return MYSQL_TYPE_DATETIME; + case MYSQL_TYPE_TIMESTAMP2: + return MYSQL_TYPE_TIMESTAMP; + case MYSQL_TYPE_NEWDATE: + return MYSQL_TYPE_DATE; + /* Note: NEWDECIMAL is a type, not only a real_type */ + default: return real_type; + } +} + + +static inline enum enum_mysql_timestamp_type +mysql_type_to_time_type(enum enum_field_types mysql_type) +{ + switch(mysql_type) { + case MYSQL_TYPE_TIME2: + case MYSQL_TYPE_TIME: return MYSQL_TIMESTAMP_TIME; + case MYSQL_TYPE_TIMESTAMP2: + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_DATETIME2: + case MYSQL_TYPE_DATETIME: return MYSQL_TIMESTAMP_DATETIME; + case MYSQL_TYPE_NEWDATE: + case MYSQL_TYPE_DATE: return MYSQL_TIMESTAMP_DATE; + default: return MYSQL_TIMESTAMP_ERROR; + } +} + + +/** + Tests if field type is temporal, i.e. represents + DATE, TIME, DATETIME or TIMESTAMP types in SQL. + + @param type Field type, as returned by field->type(). + @retval true If field type is temporal + @retval false If field type is not temporal +*/ +inline bool is_temporal_type(enum_field_types type) +{ + return mysql_type_to_time_type(type) != MYSQL_TIMESTAMP_ERROR; +} + + +/** + Tests if field type is temporal and has time part, + i.e. represents TIME, DATETIME or TIMESTAMP types in SQL. + + @param type Field type, as returned by field->type(). + @retval true If field type is temporal type with time part. + @retval false If field type is not temporal type with time part. +*/ +inline bool is_temporal_type_with_time(enum_field_types type) +{ + switch (type) + { + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_TIMESTAMP: + return true; + default: + return false; + } +} + +enum enum_vcol_info_type +{ + VCOL_GENERATED_VIRTUAL, VCOL_GENERATED_STORED, + VCOL_DEFAULT, VCOL_CHECK_FIELD, VCOL_CHECK_TABLE +}; + +static inline const char *vcol_type_name(enum_vcol_info_type type) +{ + switch (type) + { + case VCOL_GENERATED_VIRTUAL: + case VCOL_GENERATED_STORED: + return "GENERATED ALWAYS AS"; + case VCOL_DEFAULT: + return "DEFAULT"; + case VCOL_CHECK_FIELD: + case VCOL_CHECK_TABLE: + return "CHECK"; + } + return 0; +} + +/* + Flags for Virtual_column_info. If none is set, the expression must be + a constant with no side-effects, so it's calculated at CREATE TABLE time, + stored in table->record[2], and not recalculated for every statement. +*/ +#define VCOL_FIELD_REF 1 +#define VCOL_NON_DETERMINISTIC 2 +#define VCOL_SESSION_FUNC 4 /* uses session data, e.g. USER or DAYNAME */ +#define VCOL_TIME_FUNC 8 +#define VCOL_IMPOSSIBLE 16 + +#define VCOL_NOT_STRICTLY_DETERMINISTIC \ + (VCOL_NON_DETERMINISTIC | VCOL_TIME_FUNC | VCOL_SESSION_FUNC) + +/* + Virtual_column_info is the class to contain additional + characteristics that is specific for a virtual/computed + field such as: + - the defining expression that is evaluated to compute the value + of the field + - whether the field is to be stored in the database + - whether the field is used in a partitioning expression +*/ + +class Virtual_column_info: public Sql_alloc +{ +private: + /* + The following data is only updated by the parser and read + when a Create_field object is created/initialized. + */ + enum_field_types field_type; /* Real field type*/ + /* Flag indicating that the field used in a partitioning expression */ + bool in_partitioning_expr; + +public: + /* Flag indicating that the field is physically stored in the database */ + bool stored_in_db; + bool utf8; /* Already in utf8 */ + Item *expr; + LEX_STRING name; /* Name of constraint */ + uint flags; + + Virtual_column_info() + : field_type((enum enum_field_types)MYSQL_TYPE_VIRTUAL), + in_partitioning_expr(FALSE), stored_in_db(FALSE), + utf8(TRUE), expr(NULL), flags(0) + { + name.str= NULL; + name.length= 0; + }; + ~Virtual_column_info() {} + enum_field_types get_real_type() const + { + return field_type; + } + void set_field_type(enum_field_types fld_type) + { + /* Calling this function can only be done once. */ + field_type= fld_type; + } + bool is_stored() const + { + return stored_in_db; + } + void set_stored_in_db_flag(bool stored) + { + stored_in_db= stored; + } + bool is_in_partitioning_expr() const + { + return in_partitioning_expr; + } + void mark_as_in_partitioning_expr() + { + in_partitioning_expr= TRUE; + } + inline bool is_equal(const Virtual_column_info* vcol) const; + void print(String*); +}; + +class Field: public Value_source +{ + Field(const Item &); /* Prevent use of these */ + void operator=(Field &); +protected: + int save_in_field_str(Field *to) + { + StringBuffer result(charset()); + val_str(&result); + return to->store(result.ptr(), result.length(), charset()); + } + static void do_field_int(Copy_field *copy); + static void do_field_real(Copy_field *copy); + static void do_field_string(Copy_field *copy); + static void do_field_temporal(Copy_field *copy); + static void do_field_decimal(Copy_field *copy); +public: + static void *operator new(size_t size, MEM_ROOT *mem_root) throw () + { return alloc_root(mem_root, size); } + static void *operator new(size_t size) throw () + { return thd_alloc(current_thd, size); } + static void operator delete(void *ptr_arg, size_t size) { TRASH(ptr_arg, size); } + static void operator delete(void *ptr, MEM_ROOT *mem_root) + { DBUG_ASSERT(0); } + + /** + Used by System Versioning. + */ + virtual bool set_max() + { DBUG_ASSERT(0); return false; } + + /** + Used by System Versioning. + */ + virtual bool is_max() + { DBUG_ASSERT(0); return false; } + + uchar *ptr; // Position to field in record + /** + Byte where the @c NULL bit is stored inside a record. If this Field is a + @c NOT @c NULL field, this member is @c NULL. + */ + uchar *null_ptr; + /* + Note that you can use table->in_use as replacement for current_thd member + only inside of val_*() and store() members (e.g. you can't use it in cons) + */ + TABLE *table; // Pointer for table + TABLE *orig_table; // Pointer to original table + const char * const *table_name; + const char *field_name; + /** reference to the list of options or NULL */ + engine_option_value *option_list; + ha_field_option_struct *option_struct; /* structure with parsed options */ + LEX_STRING comment; + /* Field is part of the following keys */ + key_map key_start, part_of_key, part_of_key_not_clustered; + + /* + Bitmap of indexes that have records ordered by col1, ... this_field, ... + + For example, INDEX (col(prefix_n)) is not present in col.part_of_sortkey. + */ + key_map part_of_sortkey; + /* + We use three additional unireg types for TIMESTAMP to overcome limitation + of current binary format of .frm file. We'd like to be able to support + NOW() as default and on update value for such fields but unable to hold + this info anywhere except unireg_check field. This issue will be resolved + in more clean way with transition to new text based .frm format. + See also comment for Field_timestamp::Field_timestamp(). + */ + enum utype { + NONE=0, + NEXT_NUMBER=15, // AUTO_INCREMENT + TIMESTAMP_OLD_FIELD=18, // TIMESTAMP created before 4.1.3 + TIMESTAMP_DN_FIELD=21, // TIMESTAMP DEFAULT NOW() + TIMESTAMP_UN_FIELD=22, // TIMESTAMP ON UPDATE NOW() + TIMESTAMP_DNUN_FIELD=23 // TIMESTAMP DEFAULT NOW() ON UPDATE NOW() + }; + enum geometry_type + { + GEOM_GEOMETRY = 0, GEOM_POINT = 1, GEOM_LINESTRING = 2, GEOM_POLYGON = 3, + GEOM_MULTIPOINT = 4, GEOM_MULTILINESTRING = 5, GEOM_MULTIPOLYGON = 6, + GEOM_GEOMETRYCOLLECTION = 7 + }; + enum imagetype { itRAW, itMBR}; + + utype unireg_check; + uint32 field_length; // Length of field + uint32 flags; + uint16 field_index; // field number in fields array + uchar null_bit; // Bit used to test null bit + /** + If true, this field was created in create_tmp_field_from_item from a NULL + value. This means that the type of the field is just a guess, and the type + may be freely coerced to another type. + + @see create_tmp_field_from_item + @see Item_type_holder::get_real_type + + */ + bool is_created_from_null_item; + + /* TRUE in Field objects created for column min/max values */ + bool is_stat_field; + + /* + Selectivity of the range condition over this field. + When calculating this selectivity a range predicate + is taken into account only if: + - it is extracted from the WHERE clause + - it depends only on the table the field belongs to + */ + double cond_selectivity; + + /* + The next field in the class of equal fields at the top AND level + of the WHERE clause + */ + Field *next_equal_field; + + /* + This structure is used for statistical data on the column + that has been read from the statistical table column_stat + */ + Column_statistics *read_stats; + /* + This structure is used for statistical data on the column that + is collected by the function collect_statistics_for_table + */ + Column_statistics_collected *collected_stats; + + /* + This is additional data provided for any computed(virtual) field, + default function or check constraint. + In particular it includes a pointer to the item by which this field + can be computed from other fields. + */ + Virtual_column_info *vcol_info, *check_constraint, *default_value; + + Field(uchar *ptr_arg,uint32 length_arg,uchar *null_ptr_arg, + uchar null_bit_arg, utype unireg_check_arg, + const char *field_name_arg); + virtual ~Field() {} + + DTCollation dtcollation() const + { + return DTCollation(charset(), derivation(), repertoire()); + } + Type_std_attributes type_std_attributes() const + { + return Type_std_attributes(field_length, decimals(), + MY_TEST(flags & UNSIGNED_FLAG), + dtcollation()); + } + + /** + Convenience definition of a copy function returned by + Field::get_copy_func() + */ + typedef void Copy_func(Copy_field*); + virtual Copy_func *get_copy_func(const Field *from) const= 0; + /* Store functions returns 1 on overflow and -1 on fatal error */ + virtual int store_field(Field *from) { return from->save_in_field(this); } + virtual int save_in_field(Field *to)= 0; + /** + Check if it is possible just copy the value + of the field 'from' to the field 'this', e.g. for + INSERT INTO t1 (field1) SELECT field2 FROM t2; + @param from - The field to copy from + @retval true - it is possible to just copy value of 'from' to 'this' + @retval false - conversion is needed + */ + virtual bool memcpy_field_possible(const Field *from) const= 0; + virtual int store(const char *to, uint length,CHARSET_INFO *cs)=0; + virtual int store_hex_hybrid(const char *str, uint length); + virtual int store(double nr)=0; + virtual int store(longlong nr, bool unsigned_val)=0; + virtual int store_decimal(const my_decimal *d)=0; + virtual int store_time_dec(MYSQL_TIME *ltime, uint dec); + int store_time(MYSQL_TIME *ltime) + { return store_time_dec(ltime, TIME_SECOND_PART_DIGITS); } + int store(const char *to, uint length, CHARSET_INFO *cs, + enum_check_fields check_level); + int store(const LEX_STRING *ls, CHARSET_INFO *cs) + { return store(ls->str, ls->length, cs); } + virtual double val_real(void)=0; + virtual longlong val_int(void)=0; + virtual bool val_bool(void)= 0; + virtual my_decimal *val_decimal(my_decimal *); + inline String *val_str(String *str) { return val_str(str, str); } + /* + val_str(buf1, buf2) gets two buffers and should use them as follows: + if it needs a temp buffer to convert result to string - use buf1 + example Field_tiny::val_str() + if the value exists as a string already - use buf2 + example Field_string::val_str() + consequently, buf2 may be created as 'String buf;' - no memory + will be allocated for it. buf1 will be allocated to hold a + value if it's too small. Using allocated buffer for buf2 may result in + an unnecessary free (and later, may be an alloc). + This trickery is used to decrease a number of malloc calls. + */ + virtual String *val_str(String*,String *)=0; + String *val_int_as_str(String *val_buffer, bool unsigned_flag); + fast_field_copier get_fast_field_copier(const Field *from); + /* + str_needs_quotes() returns TRUE if the value returned by val_str() needs + to be quoted when used in constructing an SQL query. + */ + virtual bool str_needs_quotes() { return FALSE; } + virtual Item_result result_type () const=0; + virtual Item_result cmp_type () const { return result_type(); } + virtual const Type_handler *cast_to_int_type_handler() const + { + return Type_handler::get_handler_by_field_type(type()); + } + static bool type_can_have_key_part(enum_field_types); + static enum_field_types field_type_merge(enum_field_types, enum_field_types); + virtual bool eq(Field *field) + { + return (ptr == field->ptr && null_ptr == field->null_ptr && + null_bit == field->null_bit && field->type() == type()); + } + virtual bool eq_def(const Field *field) const; + + /* + pack_length() returns size (in bytes) used to store field data in memory + (i.e. it returns the maximum size of the field in a row of the table, + which is located in RAM). + */ + virtual uint32 pack_length() const { return (uint32) field_length; } + + /* + pack_length_in_rec() returns size (in bytes) used to store field data on + storage (i.e. it returns the maximal size of the field in a row of the + table, which is located on disk). + */ + virtual uint32 pack_length_in_rec() const { return pack_length(); } + virtual bool compatible_field_size(uint metadata, Relay_log_info *rli, + uint16 mflags, int *order); + virtual uint pack_length_from_metadata(uint field_metadata) + { + DBUG_ENTER("Field::pack_length_from_metadata"); + DBUG_RETURN(field_metadata); + } + virtual uint row_pack_length() const { return 0; } + virtual int save_field_metadata(uchar *first_byte) + { return do_save_field_metadata(first_byte); } + + /* + data_length() return the "real size" of the data in memory. + */ + virtual uint32 data_length() { return pack_length(); } + virtual uint32 sort_length() const { return pack_length(); } + + /* + Get the number bytes occupied by the value in the field. + CHAR values are stripped of trailing spaces. + Flexible values are stripped of their length. + */ + virtual uint32 value_length() + { + uint len; + if (!zero_pack() && + (type() == MYSQL_TYPE_STRING && + (len= pack_length()) >= 4 && len < 256)) + { + uchar *str, *end; + for (str= ptr, end= str+len; end > str && end[-1] == ' '; end--) {} + len=(uint) (end-str); + return len; + } + return data_length(); + } + + /** + Get the maximum size of the data in packed format. + + @return Maximum data length of the field when packed using the + Field::pack() function. + */ + virtual uint32 max_data_length() const { + return pack_length(); + }; + + virtual int reset(void) { bzero(ptr,pack_length()); return 0; } + virtual void reset_fields() {} + const uchar *ptr_in_record(const uchar *record) const + { + my_ptrdiff_t l_offset= (my_ptrdiff_t) (record - table->record[0]); + return ptr + l_offset; + } + virtual void set_default(); + + bool has_update_default_function() const + { + return flags & ON_UPDATE_NOW_FLAG; + } + bool has_default_now_unireg_check() const + { + return unireg_check == TIMESTAMP_DN_FIELD + || unireg_check == TIMESTAMP_DNUN_FIELD; + } + + /* + Mark the field as having a value supplied by the client, thus it should + not be auto-updated. + */ + void set_has_explicit_value() + { + bitmap_set_bit(&table->has_value_set, field_index); + } + bool has_explicit_value() + { + return bitmap_is_set(&table->has_value_set, field_index); + } + virtual bool set_explicit_default(Item *value); + + /** + Evaluates the @c UPDATE default function, if one exists, and stores the + result in the record buffer. If no such function exists for the column, + or the function is not valid for the column's data type, invoking this + function has no effect. + */ + virtual int evaluate_update_default_function() { return 0; } + + virtual bool binary() const { return 1; } + virtual bool zero_pack() const { return 1; } + virtual enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; } + virtual uint32 key_length() const { return pack_length(); } + virtual enum_field_types type() const =0; + virtual enum_field_types real_type() const { return type(); } + virtual enum_field_types binlog_type() const + { + /* + Binlog stores field->type() as type code by default. For example, + it puts MYSQL_TYPE_STRING in case of CHAR, VARCHAR, SET and ENUM, + with extra data type details put into metadata. + + Binlog behaviour slightly differs between various MySQL and MariaDB + versions for the temporal data types TIME, DATETIME and TIMESTAMP. + + MySQL prior to 5.6 uses MYSQL_TYPE_TIME, MYSQL_TYPE_DATETIME + and MYSQL_TYPE_TIMESTAMP type codes in binlog and stores no + additional metadata. + + MariaDB-5.3 implements new versions for TIME, DATATIME, TIMESTAMP + with fractional second precision, but uses the old format for the + types TIME(0), DATETIME(0), TIMESTAMP(0), and it still stores + MYSQL_TYPE_TIME, MYSQL_TYPE_DATETIME and MYSQL_TYPE_TIMESTAMP in binlog, + with no additional metadata. + So row-based replication between temporal data types of + different precision is not possible in MariaDB. + + MySQL-5.6 also implements a new version of TIME, DATETIME, TIMESTAMP + which support fractional second precision 0..6, and use the new + format even for the types TIME(0), DATETIME(0), TIMESTAMP(0). + For these new data types, MySQL-5.6 stores new type codes + MYSQL_TYPE_TIME2, MYSQL_TYPE_DATETIME2, MYSQL_TYPE_TIMESTAMP2 in binlog, + with fractional precision 0..6 put into metadata. + This makes it in theory possible to do row-based replication between + columns of different fractional precision (e.g. from TIME(1) on master + to TIME(6) on slave). However, it's not currently fully implemented yet. + MySQL-5.6 can only do row-based replication from the old types + TIME, DATETIME, TIMESTAMP (represented by MYSQL_TYPE_TIME, + MYSQL_TYPE_DATETIME and MYSQL_TYPE_TIMESTAMP type codes in binlog) + to the new corresponding types TIME(0), DATETIME(0), TIMESTAMP(0). + + Note: MariaDB starting from the version 10.0 understands the new + MySQL-5.6 type codes MYSQL_TYPE_TIME2, MYSQL_TYPE_DATETIME2, + MYSQL_TYPE_TIMESTAMP2. When started over MySQL-5.6 tables both on + master and on slave, MariaDB-10.0 can also do row-based replication + from the old types TIME, DATETIME, TIMESTAMP to the new MySQL-5.6 + types TIME(0), DATETIME(0), TIMESTAMP(0). + + Note: perhaps binlog should eventually be modified to store + real_type() instead of type() for all column types. + */ + return type(); + } + inline int cmp(const uchar *str) { return cmp(ptr,str); } + virtual int cmp_max(const uchar *a, const uchar *b, uint max_len) + { return cmp(a, b); } + virtual int cmp(const uchar *,const uchar *)=0; + virtual int cmp_binary(const uchar *a,const uchar *b, uint32 max_length=~0U) + { return memcmp(a,b,pack_length()); } + virtual int cmp_offset(uint row_offset) + { return cmp(ptr,ptr+row_offset); } + virtual int cmp_binary_offset(uint row_offset) + { return cmp_binary(ptr, ptr+row_offset); }; + virtual int key_cmp(const uchar *a,const uchar *b) + { return cmp(a, b); } + virtual int key_cmp(const uchar *str, uint length) + { return cmp(ptr,str); } + /* + Update the value m of the 'min_val' field with the current value v + of this field if force_update is set to TRUE or if v < m. + Return TRUE if the value has been updated. + */ + virtual bool update_min(Field *min_val, bool force_update) + { + bool update_fl= force_update || cmp(ptr, min_val->ptr) < 0; + if (update_fl) + { + min_val->set_notnull(); + memcpy(min_val->ptr, ptr, pack_length()); + } + return update_fl; + } + /* + Update the value m of the 'max_val' field with the current value v + of this field if force_update is set to TRUE or if v > m. + Return TRUE if the value has been updated. + */ + virtual bool update_max(Field *max_val, bool force_update) + { + bool update_fl= force_update || cmp(ptr, max_val->ptr) > 0; + if (update_fl) + { + max_val->set_notnull(); + memcpy(max_val->ptr, ptr, pack_length()); + } + return update_fl; + } + virtual void store_field_value(uchar *val, uint len) + { + memcpy(ptr, val, len); + } + virtual uint decimals() const { return 0; } + /* + Caller beware: sql_type can change str.Ptr, so check + ptr() to see if it changed if you are using your own buffer + in str and restore it with set() if needed + */ + virtual void sql_type(String &str) const =0; + virtual uint size_of() const =0; // For new field + inline bool is_null(my_ptrdiff_t row_offset= 0) const + { + /* + The table may have been marked as containing only NULL values + for all fields if it is a NULL-complemented row of an OUTER JOIN + or if the query is an implicitly grouped query (has aggregate + functions but no GROUP BY clause) with no qualifying rows. If + this is the case (in which TABLE::null_row is true), the field + is considered to be NULL. + + Note that if a table->null_row is set then also all null_bits are + set for the row. + + In the case of the 'result_field' for GROUP BY, table->null_row might + refer to the *next* row in the table (when the algorithm is: read the + next row, see if any of group column values have changed, send the + result - grouped - row to the client if yes). So, table->null_row might + be wrong, but such a result_field is always nullable (that's defined by + original_field->maybe_null()) and we trust its null bit. + */ + return null_ptr ? null_ptr[row_offset] & null_bit : table->null_row; + } + inline bool is_real_null(my_ptrdiff_t row_offset= 0) const + { return null_ptr && (null_ptr[row_offset] & null_bit); } + inline bool is_null_in_record(const uchar *record) const + { + if (maybe_null_in_table()) + return record[(uint) (null_ptr - table->record[0])] & null_bit; + return 0; + } + inline void set_null(my_ptrdiff_t row_offset= 0) + { if (null_ptr) null_ptr[row_offset]|= null_bit; } + inline void set_notnull(my_ptrdiff_t row_offset= 0) + { if (null_ptr) null_ptr[row_offset]&= (uchar) ~null_bit; } + inline bool maybe_null(void) const + { return null_ptr != 0 || table->maybe_null; } + + /* @return true if this field is NULL-able (even if temporarily) */ + inline bool real_maybe_null(void) const { return null_ptr != 0; } + uint null_offset(const uchar *record) const + { return (uint) (null_ptr - record); } + /* + For a NULL-able field (that can actually store a NULL value in a table) + null_ptr points to the "null bitmap" in the table->record[0] header. For + NOT NULL fields it is either 0 or points outside table->record[0] into the + table->triggers->extra_null_bitmap (so that the field can store a NULL + value temporarily, only in memory) + */ + bool maybe_null_in_table() const + { return null_ptr >= table->record[0] && null_ptr <= ptr; } + + uint null_offset() const + { return null_offset(table->record[0]); } + void set_null_ptr(uchar *p_null_ptr, uint p_null_bit) + { + null_ptr= p_null_ptr; + null_bit= p_null_bit; + } + + bool stored_in_db() const { return !vcol_info || vcol_info->stored_in_db; } + + inline THD *get_thd() const + { return likely(table) ? table->in_use : current_thd; } + + enum { + LAST_NULL_BYTE_UNDEF= 0 + }; + + /* + Find the position of the last null byte for the field. + + SYNOPSIS + last_null_byte() + + DESCRIPTION + Return a pointer to the last byte of the null bytes where the + field conceptually is placed. + + RETURN VALUE + The position of the last null byte relative to the beginning of + the record. If the field does not use any bits of the null + bytes, the value 0 (LAST_NULL_BYTE_UNDEF) is returned. + */ + size_t last_null_byte() const { + size_t bytes= do_last_null_byte(); + DBUG_PRINT("debug", ("last_null_byte() ==> %ld", (long) bytes)); + DBUG_ASSERT(bytes <= table->s->null_bytes); + return bytes; + } + + void make_sort_key(uchar *buff, uint length); + virtual void make_field(Send_field *); + virtual void sort_string(uchar *buff,uint length)=0; + virtual bool optimize_range(uint idx, uint part); + virtual void free() {} + virtual Field *make_new_field(MEM_ROOT *root, TABLE *new_table, + bool keep_type); + virtual Field *new_key_field(MEM_ROOT *root, TABLE *new_table, + uchar *new_ptr, uint32 length, + uchar *new_null_ptr, uint new_null_bit); + Field *clone(MEM_ROOT *mem_root, TABLE *new_table); + Field *clone(MEM_ROOT *mem_root, TABLE *new_table, my_ptrdiff_t diff, + bool stat_flag= FALSE); + Field *clone(MEM_ROOT *mem_root, my_ptrdiff_t diff); + inline void move_field(uchar *ptr_arg,uchar *null_ptr_arg,uchar null_bit_arg) + { + ptr=ptr_arg; null_ptr=null_ptr_arg; null_bit=null_bit_arg; + } + inline void move_field(uchar *ptr_arg) { ptr=ptr_arg; } + inline uchar *record_ptr() // record[0] or wherever the field was moved to + { + my_ptrdiff_t offset= table->s->field[field_index]->ptr - table->s->default_values; + return ptr - offset; + } + virtual void move_field_offset(my_ptrdiff_t ptr_diff) + { + ptr=ADD_TO_PTR(ptr,ptr_diff, uchar*); + if (null_ptr) + null_ptr=ADD_TO_PTR(null_ptr,ptr_diff,uchar*); + } + virtual void get_image(uchar *buff, uint length, CHARSET_INFO *cs) + { memcpy(buff,ptr,length); } + virtual void set_image(const uchar *buff,uint length, CHARSET_INFO *cs) + { memcpy(ptr,buff,length); } + + + /* + Copy a field part into an output buffer. + + SYNOPSIS + Field::get_key_image() + buff [out] output buffer + length output buffer size + type itMBR for geometry blobs, otherwise itRAW + + DESCRIPTION + This function makes a copy of field part of size equal to or + less than "length" parameter value. + For fields of string types (CHAR, VARCHAR, TEXT) the rest of buffer + is padded by zero byte. + + NOTES + For variable length character fields (i.e. UTF-8) the "length" + parameter means a number of output buffer bytes as if all field + characters have maximal possible size (mbmaxlen). In the other words, + "length" parameter is a number of characters multiplied by + field_charset->mbmaxlen. + + RETURN + Number of copied bytes (excluding padded zero bytes -- see above). + */ + + virtual uint get_key_image(uchar *buff, uint length, imagetype type_arg) + { + get_image(buff, length, &my_charset_bin); + return length; + } + virtual void set_key_image(const uchar *buff,uint length) + { set_image(buff,length, &my_charset_bin); } + inline longlong val_int_offset(uint row_offset) + { + ptr+=row_offset; + longlong tmp=val_int(); + ptr-=row_offset; + return tmp; + } + inline longlong val_int(const uchar *new_ptr) + { + uchar *old_ptr= ptr; + longlong return_value; + ptr= (uchar*) new_ptr; + return_value= val_int(); + ptr= old_ptr; + return return_value; + } + inline String *val_str(String *str, const uchar *new_ptr) + { + uchar *old_ptr= ptr; + ptr= (uchar*) new_ptr; + val_str(str); + ptr= old_ptr; + return str; + } + virtual bool send_binary(Protocol *protocol); + + virtual uchar *pack(uchar *to, const uchar *from, uint max_length); + /** + @overload Field::pack(uchar*, const uchar*, uint, bool) + */ + uchar *pack(uchar *to, const uchar *from) + { + DBUG_ENTER("Field::pack"); + uchar *result= this->pack(to, from, UINT_MAX); + DBUG_RETURN(result); + } + + virtual const uchar *unpack(uchar* to, const uchar *from, + const uchar *from_end, uint param_data=0); + + virtual uint packed_col_length(const uchar *to, uint length) + { return length;} + virtual uint max_packed_col_length(uint max_length) + { return max_length;} + + uint offset(uchar *record) const + { + return (uint) (ptr - record); + } + void copy_from_tmp(int offset); + uint fill_cache_field(struct st_cache_field *copy); + virtual bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_time(MYSQL_TIME *ltime) { return get_date(ltime, TIME_TIME_ONLY); } + virtual CHARSET_INFO *charset(void) const { return &my_charset_bin; } + virtual CHARSET_INFO *charset_for_protocol(void) const + { return binary() ? &my_charset_bin : charset(); } + virtual CHARSET_INFO *sort_charset(void) const { return charset(); } + virtual bool has_charset(void) const { return FALSE; } + virtual enum Derivation derivation(void) const + { return DERIVATION_IMPLICIT; } + virtual uint repertoire(void) const { return MY_REPERTOIRE_UNICODE30; } + virtual void set_derivation(enum Derivation derivation_arg, + uint repertoire_arg) + { } + virtual int set_time() { return 1; } + bool set_warning(Sql_condition::enum_warning_level, unsigned int code, + int cuted_increment) const; +protected: + bool set_warning(unsigned int code, int cuted_increment) const + { + return set_warning(Sql_condition::WARN_LEVEL_WARN, code, cuted_increment); + } + bool set_note(unsigned int code, int cuted_increment) const + { + return set_warning(Sql_condition::WARN_LEVEL_NOTE, code, cuted_increment); + } + void set_datetime_warning(Sql_condition::enum_warning_level, uint code, + const ErrConv *str, timestamp_type ts_type, + int cuted_increment) const; + void set_datetime_warning(uint code, + const ErrConv *str, timestamp_type ts_type, + int cuted_increment) const + { + set_datetime_warning(Sql_condition::WARN_LEVEL_WARN, code, str, ts_type, + cuted_increment); + } + void set_warning_truncated_wrong_value(const char *type, const char *value); + inline bool check_overflow(int op_result) + { + return (op_result == E_DEC_OVERFLOW); + } + int warn_if_overflow(int op_result); + Copy_func *get_identical_copy_func() const; +public: + void set_table_name(String *alias) + { + table_name= &alias->Ptr; + } + void init(TABLE *table_arg) + { + orig_table= table= table_arg; + set_table_name(&table_arg->alias); + } + + /* maximum possible display length */ + virtual uint32 max_display_length()= 0; + + /** + Whether a field being created is compatible with a existing one. + + Used by the ALTER TABLE code to evaluate whether the new definition + of a table is compatible with the old definition so that it can + determine if data needs to be copied over (table data change). + */ + virtual uint is_equal(Create_field *new_field); + /* convert decimal to longlong with overflow check */ + longlong convert_decimal2longlong(const my_decimal *val, bool unsigned_flag, + int *err); + /* The max. number of characters */ + virtual uint32 char_length() const + { + return field_length / charset()->mbmaxlen; + } + + virtual geometry_type get_geometry_type() + { + /* shouldn't get here. */ + DBUG_ASSERT(0); + return GEOM_GEOMETRY; + } + + ha_storage_media field_storage_type() const + { + return (ha_storage_media) + ((flags >> FIELD_FLAGS_STORAGE_MEDIA) & 3); + } + + void set_storage_type(ha_storage_media storage_type_arg) + { + DBUG_ASSERT(field_storage_type() == HA_SM_DEFAULT); + flags |= static_cast(storage_type_arg) << + FIELD_FLAGS_STORAGE_MEDIA; + } + + column_format_type column_format() const + { + return (column_format_type) + ((flags >> FIELD_FLAGS_COLUMN_FORMAT) & 3); + } + + void set_column_format(column_format_type column_format_arg) + { + DBUG_ASSERT(column_format() == COLUMN_FORMAT_TYPE_DEFAULT); + flags |= static_cast(column_format_arg) << + FIELD_FLAGS_COLUMN_FORMAT; + } + + /* + System versioning support. + */ + + bool is_generated() + { + return flags & (GENERATED_ROW_START_FLAG | GENERATED_ROW_END_FLAG); + } + + bool is_generated_row_start() + { + return flags & GENERATED_ROW_START_FLAG; + } + + bool is_generated_row_end() + { + return flags & GENERATED_ROW_END_FLAG; + } + + bool is_versioning_disabled() + { + return flags & WITHOUT_SYSTEM_VERSIONING_FLAG; + } + + /* Mark a field as auto-generated row start column. */ + void set_generated_row_start() + { + //DBUG_ASSERT((flags & GENERATED_ROW_END_FLAG) == 0); + flags |= GENERATED_ROW_START_FLAG; + } + + /* Mark a field as auto-generated row start column. */ + void set_generated_row_end() + { + //DBUG_ASSERT((flags & GENERATED_ROW_START_FLAG) == 0); + flags |= GENERATED_ROW_END_FLAG; + } + + /* Disable a field versioning for a versioned table. */ + void disable_versioning() + { + flags |= WITHOUT_SYSTEM_VERSIONING_FLAG; + } + + /* Inherit a field versioning status from the table. */ + void inherit_versioning() + { + flags &= ~WITHOUT_SYSTEM_VERSIONING_FLAG; + } + + /* + Validate a non-null field value stored in the given record + according to the current thread settings, e.g. sql_mode. + @param thd - the thread + @param record - the record to check in + */ + virtual bool validate_value_in_record(THD *thd, const uchar *record) const + { return false; } + bool validate_value_in_record_with_warn(THD *thd, const uchar *record); + key_map get_possible_keys(); + + /* Hash value */ + virtual void hash(ulong *nr, ulong *nr2); + +/** + Checks whether a string field is part of write_set. + + @return + FALSE - If field is not char/varchar/.... + - If field is char/varchar/.. and is not part of write set. + TRUE - If field is char/varchar/.. and is part of write set. +*/ + virtual bool is_varchar_and_in_write_set() const { return FALSE; } + + /* Check whether the field can be used as a join attribute in hash join */ + virtual bool hash_join_is_possible() { return TRUE; } + virtual bool eq_cmp_as_binary() { return TRUE; } + + /* Position of the field value within the interval of [min, max] */ + virtual double pos_in_interval(Field *min, Field *max) + { + return (double) 0.5; + } + + /* + Check if comparison between the field and an item unambiguously + identifies a distinct field value. + + Example1: SELECT * FROM t1 WHERE int_column=10; + This example returns distinct integer value of 10. + + Example2: SELECT * FROM t1 WHERE varchar_column=DATE'2001-01-01' + This example returns non-distinct values. + Comparison as DATE will return '2001-01-01' and '2001-01-01x', + but these two values are not equal to each other as VARCHARs. + See also the function with the same name in sql_select.cc. + */ + virtual bool test_if_equality_guarantees_uniqueness(const Item *const_item) + const; + virtual bool can_be_substituted_to_equal_item(const Context &ctx, + const Item_equal *item); + virtual Item *get_equal_const_item(THD *thd, const Context &ctx, + Item *const_item) + { + return const_item; + } + virtual bool can_optimize_keypart_ref(const Item_bool_func *cond, + const Item *item) const; + virtual bool can_optimize_hash_join(const Item_bool_func *cond, + const Item *item) const + { + return can_optimize_keypart_ref(cond, item); + } + virtual bool can_optimize_group_min_max(const Item_bool_func *cond, + const Item *const_item) const; + /** + Test if Field can use range optimizer for a standard comparison operation: + <=, <, =, <=>, >, >= + Note, this method does not cover spatial operations. + */ + virtual bool can_optimize_range(const Item_bool_func *cond, + const Item *item, + bool is_eq_func) const; + + bool can_optimize_outer_join_table_elimination(const Item_bool_func *cond, + const Item *item) const + { + // Exactly the same rules with REF access + return can_optimize_keypart_ref(cond, item); + } + + bool save_in_field_default_value(bool view_eror_processing); + bool save_in_field_ignore_value(bool view_error_processing); + + /* Mark field in read map. Updates also virtual fields */ + void register_field_in_read_map(); + + friend int cre_myisam(char * name, register TABLE *form, uint options, + ulonglong auto_increment_value); + friend class Copy_field; + friend class Item_avg_field; + friend class Item_std_field; + friend class Item_sum_num; + friend class Item_sum_sum; + friend class Item_sum_count; + friend class Item_sum_avg; + friend class Item_sum_std; + friend class Item_sum_min; + friend class Item_sum_max; + friend class Item_func_group_concat; + +private: + /* + Primitive for implementing last_null_byte(). + + SYNOPSIS + do_last_null_byte() + + DESCRIPTION + Primitive for the implementation of the last_null_byte() + function. This represents the inheritance interface and can be + overridden by subclasses. + */ + virtual size_t do_last_null_byte() const; + +/** + Retrieve the field metadata for fields. + + This default implementation returns 0 and saves 0 in the metadata_ptr + value. + + @param metadata_ptr First byte of field metadata + + @returns 0 no bytes written. +*/ + virtual int do_save_field_metadata(uchar *metadata_ptr) + { return 0; } + +protected: + uchar *pack_int(uchar *to, const uchar *from, size_t size) + { + memcpy(to, from, size); + return to + size; + } + + const uchar *unpack_int(uchar* to, const uchar *from, + const uchar *from_end, size_t size) + { + if (from + size > from_end) + return 0; + memcpy(to, from, size); + return from + size; + } + + uchar *pack_int16(uchar *to, const uchar *from) + { return pack_int(to, from, 2); } + const uchar *unpack_int16(uchar* to, const uchar *from, const uchar *from_end) + { return unpack_int(to, from, from_end, 2); } + uchar *pack_int24(uchar *to, const uchar *from) + { return pack_int(to, from, 3); } + const uchar *unpack_int24(uchar* to, const uchar *from, const uchar *from_end) + { return unpack_int(to, from, from_end, 3); } + uchar *pack_int32(uchar *to, const uchar *from) + { return pack_int(to, from, 4); } + const uchar *unpack_int32(uchar* to, const uchar *from, const uchar *from_end) + { return unpack_int(to, from, from_end, 4); } + uchar *pack_int64(uchar* to, const uchar *from) + { return pack_int(to, from, 8); } + const uchar *unpack_int64(uchar* to, const uchar *from, const uchar *from_end) + { return unpack_int(to, from, from_end, 8); } + + double pos_in_interval_val_real(Field *min, Field *max); + double pos_in_interval_val_str(Field *min, Field *max, uint data_offset); +}; + + +class Field_num :public Field { +protected: + int check_edom_and_important_data_truncation(const char *type, bool edom, + CHARSET_INFO *cs, + const char *str, uint length, + const char *end_of_num); + int check_edom_and_truncation(const char *type, bool edom, + CHARSET_INFO *cs, + const char *str, uint length, + const char *end_of_num); + int check_int(CHARSET_INFO *cs, const char *str, uint length, + const char *int_end, int error) + { + return check_edom_and_truncation("integer", + error == MY_ERRNO_EDOM || str == int_end, + cs, str, length, int_end); + } + bool get_int(CHARSET_INFO *cs, const char *from, uint len, + longlong *rnd, ulonglong unsigned_max, + longlong signed_min, longlong signed_max); + void prepend_zeros(String *value) const; + Item *get_equal_zerofill_const_item(THD *thd, const Context &ctx, + Item *const_item); +public: + const uint8 dec; + bool zerofill,unsigned_flag; // Purify cannot handle bit fields + Field_num(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg, + uchar null_bit_arg, utype unireg_check_arg, + const char *field_name_arg, + uint8 dec_arg, bool zero_arg, bool unsigned_arg); + enum Item_result result_type () const { return INT_RESULT; } + enum Derivation derivation(void) const { return DERIVATION_NUMERIC; } + uint repertoire(void) const { return MY_REPERTOIRE_NUMERIC; } + CHARSET_INFO *charset(void) const { return &my_charset_numeric; } + Item *get_equal_const_item(THD *thd, const Context &ctx, Item *const_item) + { + return (flags & ZEROFILL_FLAG) ? + get_equal_zerofill_const_item(thd, ctx, const_item) : + const_item; + } + void add_zerofill_and_unsigned(String &res) const; + friend class Create_field; + void make_field(Send_field *); + uint decimals() const { return (uint) dec; } + uint size_of() const { return sizeof(*this); } + bool eq_def(const Field *field) const; + Copy_func *get_copy_func(const Field *from) const + { + return do_field_int; + } + int save_in_field(Field *to) + { + return to->store(val_int(), MY_TEST(flags & UNSIGNED_FLAG)); + } + bool memcpy_field_possible(const Field *from) const + { + return real_type() == from->real_type() && + pack_length() == from->pack_length() && + !((flags & UNSIGNED_FLAG) && !(from->flags & UNSIGNED_FLAG)) && + decimals() == from->decimals(); + } + int store_decimal(const my_decimal *); + my_decimal *val_decimal(my_decimal *); + bool val_bool() { return val_int() != 0; } + uint is_equal(Create_field *new_field); + uint row_pack_length() const { return pack_length(); } + uint32 pack_length_from_metadata(uint field_metadata) { + uint32 length= pack_length(); + DBUG_PRINT("result", ("pack_length_from_metadata(%d): %u", + field_metadata, length)); + return length; + } + int store_time_dec(MYSQL_TIME *ltime, uint dec); + double pos_in_interval(Field *min, Field *max) + { + return pos_in_interval_val_real(min, max); + } + bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); +}; + + +class Field_str :public Field { +protected: + // TODO-10.2: Reuse DTCollation instead of these three members + CHARSET_INFO *field_charset; + enum Derivation field_derivation; + uint field_repertoire; +public: + bool can_be_substituted_to_equal_item(const Context &ctx, + const Item_equal *item_equal); + Field_str(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg, + uchar null_bit_arg, utype unireg_check_arg, + const char *field_name_arg, CHARSET_INFO *charset); + Item_result result_type () const { return STRING_RESULT; } + uint decimals() const { return NOT_FIXED_DEC; } + int save_in_field(Field *to) { return save_in_field_str(to); } + bool memcpy_field_possible(const Field *from) const + { + return real_type() == from->real_type() && + pack_length() == from->pack_length() && + charset() == from->charset(); + } + int store(double nr); + int store(longlong nr, bool unsigned_val)=0; + int store_decimal(const my_decimal *); + int store(const char *to,uint length,CHARSET_INFO *cs)=0; + int store_hex_hybrid(const char *str, uint length) + { + return store(str, length, &my_charset_bin); + } + uint repertoire(void) const { return field_repertoire; } + CHARSET_INFO *charset(void) const { return field_charset; } + enum Derivation derivation(void) const { return field_derivation; } + void set_derivation(enum Derivation derivation_arg, + uint repertoire_arg) + { + field_derivation= derivation_arg; + field_repertoire= repertoire_arg; + } + bool binary() const { return field_charset == &my_charset_bin; } + uint32 max_display_length() { return field_length; } + friend class Create_field; + my_decimal *val_decimal(my_decimal *); + bool val_bool() { return val_real() != 0e0; } + virtual bool str_needs_quotes() { return TRUE; } + uint is_equal(Create_field *new_field); + bool eq_cmp_as_binary() { return MY_TEST(flags & BINARY_FLAG); } + virtual uint length_size() { return 0; } + double pos_in_interval(Field *min, Field *max) + { + return pos_in_interval_val_str(min, max, length_size()); + } + bool test_if_equality_guarantees_uniqueness(const Item *const_item) const; +}; + +/* base class for Field_string, Field_varstring and Field_blob */ + +class Field_longstr :public Field_str +{ +protected: + int report_if_important_data(const char *ptr, const char *end, + bool count_spaces); + bool check_string_copy_error(const String_copier *copier, + const char *end, CHARSET_INFO *cs); + int check_conversion_status(const String_copier *copier, + const char *end, CHARSET_INFO *cs, + bool count_spaces) + { + if (check_string_copy_error(copier, end, cs)) + return 2; + return report_if_important_data(copier->source_end_pos(), + end, count_spaces); + } + bool cmp_to_string_with_same_collation(const Item_bool_func *cond, + const Item *item) const; + bool cmp_to_string_with_stricter_collation(const Item_bool_func *cond, + const Item *item) const; +public: + Field_longstr(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, + uchar null_bit_arg, utype unireg_check_arg, + const char *field_name_arg, CHARSET_INFO *charset_arg) + :Field_str(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, + field_name_arg, charset_arg) + {} + + int store_decimal(const my_decimal *d); + uint32 max_data_length() const; + + bool is_varchar_and_in_write_set() const + { + DBUG_ASSERT(table && table->write_set); + return bitmap_is_set(table->write_set, field_index); + } + bool match_collation_to_optimize_range() const { return true; } + + bool can_optimize_keypart_ref(const Item_bool_func *cond, + const Item *item) const; + bool can_optimize_hash_join(const Item_bool_func *cond, + const Item *item) const; + bool can_optimize_group_min_max(const Item_bool_func *cond, + const Item *const_item) const; + bool can_optimize_range(const Item_bool_func *cond, + const Item *item, + bool is_eq_func) const; +}; + +/* base class for float and double and decimal (old one) */ +class Field_real :public Field_num { +protected: + double get_double(const char *str, uint length, CHARSET_INFO *cs, int *err); +public: + bool not_fixed; + + Field_real(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, + uchar null_bit_arg, utype unireg_check_arg, + const char *field_name_arg, + uint8 dec_arg, bool zero_arg, bool unsigned_arg) + :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, + field_name_arg, dec_arg, zero_arg, unsigned_arg), + not_fixed(dec_arg >= FLOATING_POINT_DECIMALS) + {} + Item_result result_type () const { return REAL_RESULT; } + Copy_func *get_copy_func(const Field *from) const + { + return do_field_real; + } + int save_in_field(Field *to) { return to->store(val_real()); } + bool memcpy_field_possible(const Field *from) const + { + /* + Cannot do memcpy from a longer field to a shorter field, + e.g. a DOUBLE(53,10) into a DOUBLE(10,10). + But it should be OK the other way around. + */ + return Field_num::memcpy_field_possible(from) && + field_length >= from->field_length; + } + int store_decimal(const my_decimal *); + int store_time_dec(MYSQL_TIME *ltime, uint dec); + bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + my_decimal *val_decimal(my_decimal *); + bool val_bool() { return val_real() != 0e0; } + uint32 max_display_length() { return field_length; } + uint size_of() const { return sizeof(*this); } + Item *get_equal_const_item(THD *thd, const Context &ctx, Item *const_item); +}; + + +class Field_decimal :public Field_real { +public: + Field_decimal(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, + uchar null_bit_arg, + enum utype unireg_check_arg, const char *field_name_arg, + uint8 dec_arg,bool zero_arg,bool unsigned_arg) + :Field_real(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg, + dec_arg, zero_arg, unsigned_arg) + {} + enum_field_types type() const { return MYSQL_TYPE_DECIMAL;} + enum ha_base_keytype key_type() const + { return zerofill ? HA_KEYTYPE_BINARY : HA_KEYTYPE_NUM; } + Copy_func *get_copy_func(const Field *from) const + { + return eq_def(from) ? get_identical_copy_func() : do_field_string; + } + int reset(void); + int store(const char *to,uint length,CHARSET_INFO *charset); + int store(double nr); + int store(longlong nr, bool unsigned_val); + double val_real(void); + longlong val_int(void); + String *val_str(String*,String *); + int cmp(const uchar *,const uchar *); + void sort_string(uchar *buff,uint length); + void overflow(bool negative); + bool zero_pack() const { return 0; } + void sql_type(String &str) const; + virtual uchar *pack(uchar* to, const uchar *from, uint max_length) + { + return Field::pack(to, from, max_length); + } +}; + + +/* New decimal/numeric field which use fixed point arithmetic */ +class Field_new_decimal :public Field_num { +private: + int do_save_field_metadata(uchar *first_byte); +public: + /* The maximum number of decimal digits can be stored */ + uint precision; + uint bin_size; + /* + Constructors take max_length of the field as a parameter - not the + precision as the number of decimal digits allowed. + So for example we need to count length from precision handling + CREATE TABLE ( DECIMAL(x,y)) + */ + Field_new_decimal(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, + uchar null_bit_arg, + enum utype unireg_check_arg, const char *field_name_arg, + uint8 dec_arg, bool zero_arg, bool unsigned_arg); + Field_new_decimal(uint32 len_arg, bool maybe_null_arg, + const char *field_name_arg, uint8 dec_arg, + bool unsigned_arg); + enum_field_types type() const { return MYSQL_TYPE_NEWDECIMAL;} + enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; } + Item_result result_type () const { return DECIMAL_RESULT; } + Copy_func *get_copy_func(const Field *from) const + { + // if (from->real_type() == MYSQL_TYPE_BIT) // QQ: why? + // return do_field_int; + return do_field_decimal; + } + int save_in_field(Field *to) + { + my_decimal buff; + return to->store_decimal(val_decimal(&buff)); + } + bool memcpy_field_possible(const Field *from) const + { + return Field_num::memcpy_field_possible(from) && + field_length == from->field_length; + } + int reset(void); + bool store_value(const my_decimal *decimal_value); + bool store_value(const my_decimal *decimal_value, int *native_error); + void set_value_on_overflow(my_decimal *decimal_value, bool sign); + int store(const char *to, uint length, CHARSET_INFO *charset); + int store(double nr); + int store(longlong nr, bool unsigned_val); + int store_time_dec(MYSQL_TIME *ltime, uint dec); + int store_decimal(const my_decimal *); + double val_real(void); + longlong val_int(void); + my_decimal *val_decimal(my_decimal *); + String *val_str(String*, String *); + bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool val_bool() + { + my_decimal decimal_value; + my_decimal *val= val_decimal(&decimal_value); + return val ? !my_decimal_is_zero(val) : 0; + } + int cmp(const uchar *, const uchar *); + void sort_string(uchar *buff, uint length); + bool zero_pack() const { return 0; } + void sql_type(String &str) const; + uint32 max_display_length() { return field_length; } + uint size_of() const { return sizeof(*this); } + uint32 pack_length() const { return (uint32) bin_size; } + uint pack_length_from_metadata(uint field_metadata); + uint row_pack_length() const { return pack_length(); } + bool compatible_field_size(uint field_metadata, Relay_log_info *rli, + uint16 mflags, int *order_var); + uint is_equal(Create_field *new_field); + virtual const uchar *unpack(uchar* to, const uchar *from, const uchar *from_end, uint param_data); + static Field *create_from_item(MEM_ROOT *root, Item *); + Item *get_equal_const_item(THD *thd, const Context &ctx, Item *const_item); +}; + + +class Field_tiny :public Field_num { +public: + Field_tiny(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, + uchar null_bit_arg, + enum utype unireg_check_arg, const char *field_name_arg, + bool zero_arg, bool unsigned_arg) + :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg, + 0, zero_arg,unsigned_arg) + {} + enum_field_types type() const { return MYSQL_TYPE_TINY;} + enum ha_base_keytype key_type() const + { return unsigned_flag ? HA_KEYTYPE_BINARY : HA_KEYTYPE_INT8; } + int store(const char *to,uint length,CHARSET_INFO *charset); + int store(double nr); + int store(longlong nr, bool unsigned_val); + int reset(void) { ptr[0]=0; return 0; } + double val_real(void); + longlong val_int(void); + String *val_str(String*,String *); + bool send_binary(Protocol *protocol); + int cmp(const uchar *,const uchar *); + void sort_string(uchar *buff,uint length); + uint32 pack_length() const { return 1; } + void sql_type(String &str) const; + uint32 max_display_length() { return 4; } + + virtual uchar *pack(uchar* to, const uchar *from, uint max_length) + { + *to= *from; + return to + 1; + } + + virtual const uchar *unpack(uchar* to, const uchar *from, + const uchar *from_end, uint param_data) + { + if (from == from_end) + return 0; + *to= *from; + return from + 1; + } +}; + + +class Field_short :public Field_num { +public: + Field_short(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, + uchar null_bit_arg, + enum utype unireg_check_arg, const char *field_name_arg, + bool zero_arg, bool unsigned_arg) + :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg, + 0, zero_arg,unsigned_arg) + {} + Field_short(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg, + bool unsigned_arg) + :Field_num((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0,0, + NONE, field_name_arg, 0, 0, unsigned_arg) + {} + enum_field_types type() const { return MYSQL_TYPE_SHORT;} + enum ha_base_keytype key_type() const + { return unsigned_flag ? HA_KEYTYPE_USHORT_INT : HA_KEYTYPE_SHORT_INT;} + int store(const char *to,uint length,CHARSET_INFO *charset); + int store(double nr); + int store(longlong nr, bool unsigned_val); + int reset(void) { ptr[0]=ptr[1]=0; return 0; } + double val_real(void); + longlong val_int(void); + String *val_str(String*,String *); + bool send_binary(Protocol *protocol); + int cmp(const uchar *,const uchar *); + void sort_string(uchar *buff,uint length); + uint32 pack_length() const { return 2; } + void sql_type(String &str) const; + uint32 max_display_length() { return 6; } + + virtual uchar *pack(uchar* to, const uchar *from, uint max_length) + { return pack_int16(to, from); } + + virtual const uchar *unpack(uchar* to, const uchar *from, + const uchar *from_end, uint param_data) + { return unpack_int16(to, from, from_end); } +}; + +class Field_medium :public Field_num { +public: + Field_medium(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, + uchar null_bit_arg, + enum utype unireg_check_arg, const char *field_name_arg, + bool zero_arg, bool unsigned_arg) + :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg, + 0, zero_arg,unsigned_arg) + {} + enum_field_types type() const { return MYSQL_TYPE_INT24;} + enum ha_base_keytype key_type() const + { return unsigned_flag ? HA_KEYTYPE_UINT24 : HA_KEYTYPE_INT24; } + int store(const char *to,uint length,CHARSET_INFO *charset); + int store(double nr); + int store(longlong nr, bool unsigned_val); + int reset(void) { ptr[0]=ptr[1]=ptr[2]=0; return 0; } + double val_real(void); + longlong val_int(void); + String *val_str(String*,String *); + bool send_binary(Protocol *protocol); + int cmp(const uchar *,const uchar *); + void sort_string(uchar *buff,uint length); + uint32 pack_length() const { return 3; } + void sql_type(String &str) const; + uint32 max_display_length() { return 8; } + + virtual uchar *pack(uchar* to, const uchar *from, uint max_length) + { + return Field::pack(to, from, max_length); + } +}; + + +class Field_long :public Field_num { +public: + Field_long(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, + uchar null_bit_arg, + enum utype unireg_check_arg, const char *field_name_arg, + bool zero_arg, bool unsigned_arg) + :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg, + 0, zero_arg,unsigned_arg) + {} + Field_long(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg, + bool unsigned_arg) + :Field_num((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0,0, + NONE, field_name_arg,0,0,unsigned_arg) + {} + enum_field_types type() const { return MYSQL_TYPE_LONG;} + enum ha_base_keytype key_type() const + { return unsigned_flag ? HA_KEYTYPE_ULONG_INT : HA_KEYTYPE_LONG_INT; } + int store(const char *to,uint length,CHARSET_INFO *charset); + int store(double nr); + int store(longlong nr, bool unsigned_val); + int reset(void) { ptr[0]=ptr[1]=ptr[2]=ptr[3]=0; return 0; } + double val_real(void); + longlong val_int(void); + bool send_binary(Protocol *protocol); + String *val_str(String*,String *); + int cmp(const uchar *,const uchar *); + void sort_string(uchar *buff,uint length); + uint32 pack_length() const { return 4; } + void sql_type(String &str) const; + uint32 max_display_length() { return MY_INT32_NUM_DECIMAL_DIGITS; } + virtual uchar *pack(uchar* to, const uchar *from, + uint max_length __attribute__((unused))) + { + return pack_int32(to, from); + } + virtual const uchar *unpack(uchar* to, const uchar *from, + const uchar *from_end, + uint param_data __attribute__((unused))) + { + return unpack_int32(to, from, from_end); + } +}; + + +class Field_longlong :public Field_num { +public: + Field_longlong(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, + uchar null_bit_arg, + enum utype unireg_check_arg, const char *field_name_arg, + bool zero_arg, bool unsigned_arg) + :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg, + 0, zero_arg,unsigned_arg) + {} + Field_longlong(uint32 len_arg,bool maybe_null_arg, + const char *field_name_arg, + bool unsigned_arg) + :Field_num((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0,0, + NONE, field_name_arg,0,0,unsigned_arg) + {} + enum_field_types type() const { return MYSQL_TYPE_LONGLONG;} + enum ha_base_keytype key_type() const + { return unsigned_flag ? HA_KEYTYPE_ULONGLONG : HA_KEYTYPE_LONGLONG; } + int store(const char *to,uint length,CHARSET_INFO *charset); + int store(double nr); + int store(longlong nr, bool unsigned_val); + int reset(void) + { + ptr[0]=ptr[1]=ptr[2]=ptr[3]=ptr[4]=ptr[5]=ptr[6]=ptr[7]=0; + return 0; + } + double val_real(void); + longlong val_int(void); + String *val_str(String*,String *); + bool send_binary(Protocol *protocol); + int cmp(const uchar *,const uchar *); + void sort_string(uchar *buff,uint length); + uint32 pack_length() const { return 8; } + void sql_type(String &str) const; + uint32 max_display_length() { return 20; } + virtual uchar *pack(uchar* to, const uchar *from, + uint max_length __attribute__((unused))) + { + return pack_int64(to, from); + } + const uchar *unpack(uchar* to, const uchar *from, const uchar *from_end, + uint param_data __attribute__((unused))) + { + return unpack_int64(to, from, from_end); + } + + bool set_max(); + bool is_max(); +}; + + +class Field_float :public Field_real { +public: + Field_float(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, + uchar null_bit_arg, + enum utype unireg_check_arg, const char *field_name_arg, + uint8 dec_arg,bool zero_arg,bool unsigned_arg) + :Field_real(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg, + dec_arg, zero_arg, unsigned_arg) + { + if (dec_arg >= FLOATING_POINT_DECIMALS) + dec_arg= NOT_FIXED_DEC; + } + Field_float(uint32 len_arg, bool maybe_null_arg, const char *field_name_arg, + uint8 dec_arg) + :Field_real((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0, (uint) 0, + NONE, field_name_arg, dec_arg, 0, 0) + { + if (dec_arg >= FLOATING_POINT_DECIMALS) + dec_arg= NOT_FIXED_DEC; + } + enum_field_types type() const { return MYSQL_TYPE_FLOAT;} + enum ha_base_keytype key_type() const { return HA_KEYTYPE_FLOAT; } + int store(const char *to,uint length,CHARSET_INFO *charset); + int store(double nr); + int store(longlong nr, bool unsigned_val); + int reset(void) { bzero(ptr,sizeof(float)); return 0; } + double val_real(void); + longlong val_int(void); + String *val_str(String*,String *); + bool send_binary(Protocol *protocol); + int cmp(const uchar *,const uchar *); + void sort_string(uchar *buff,uint length); + uint32 pack_length() const { return sizeof(float); } + uint row_pack_length() const { return pack_length(); } + void sql_type(String &str) const; +private: + int do_save_field_metadata(uchar *first_byte); +}; + + +class Field_double :public Field_real { +public: + Field_double(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, + uchar null_bit_arg, + enum utype unireg_check_arg, const char *field_name_arg, + uint8 dec_arg,bool zero_arg,bool unsigned_arg) + :Field_real(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg, + dec_arg, zero_arg, unsigned_arg) + { + if (dec_arg >= FLOATING_POINT_DECIMALS) + dec_arg= NOT_FIXED_DEC; + } + Field_double(uint32 len_arg, bool maybe_null_arg, const char *field_name_arg, + uint8 dec_arg) + :Field_real((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "" : 0, (uint) 0, + NONE, field_name_arg, dec_arg, 0, 0) + { + if (dec_arg >= FLOATING_POINT_DECIMALS) + dec_arg= NOT_FIXED_DEC; + } + Field_double(uint32 len_arg, bool maybe_null_arg, const char *field_name_arg, + uint8 dec_arg, bool not_fixed_arg) + :Field_real((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "" : 0, (uint) 0, + NONE, field_name_arg, dec_arg, 0, 0) + { + not_fixed= not_fixed_arg; + if (dec_arg >= FLOATING_POINT_DECIMALS) + dec_arg= NOT_FIXED_DEC; + } + enum_field_types type() const { return MYSQL_TYPE_DOUBLE;} + enum ha_base_keytype key_type() const { return HA_KEYTYPE_DOUBLE; } + int store(const char *to,uint length,CHARSET_INFO *charset); + int store(double nr); + int store(longlong nr, bool unsigned_val); + int reset(void) { bzero(ptr,sizeof(double)); return 0; } + double val_real(void); + longlong val_int(void) + { + Converter_double_to_longlong conv(Field_double::val_real(), false); + if (conv.error()) + conv.push_warning(get_thd(), Field_double::val_real(), false); + return conv.result(); + } + String *val_str(String*,String *); + bool send_binary(Protocol *protocol); + int cmp(const uchar *,const uchar *); + void sort_string(uchar *buff,uint length); + uint32 pack_length() const { return sizeof(double); } + uint row_pack_length() const { return pack_length(); } + void sql_type(String &str) const; +private: + int do_save_field_metadata(uchar *first_byte); +}; + + +/* Everything saved in this will disappear. It will always return NULL */ + +class Field_null :public Field_str { + static uchar null[1]; +public: + Field_null(uchar *ptr_arg, uint32 len_arg, + enum utype unireg_check_arg, const char *field_name_arg, + CHARSET_INFO *cs) + :Field_str(ptr_arg, len_arg, null, 1, + unireg_check_arg, field_name_arg, cs) + {} + enum_field_types type() const { return MYSQL_TYPE_NULL;} + Copy_func *get_copy_func(const Field *from) const + { + return do_field_string; + } + int store(const char *to, uint length, CHARSET_INFO *cs) + { null[0]=1; return 0; } + int store(double nr) { null[0]=1; return 0; } + int store(longlong nr, bool unsigned_val) { null[0]=1; return 0; } + int store_decimal(const my_decimal *d) { null[0]=1; return 0; } + int reset(void) { return 0; } + double val_real(void) { return 0.0;} + longlong val_int(void) { return 0;} + bool val_bool(void) { return false; } + my_decimal *val_decimal(my_decimal *) { return 0; } + String *val_str(String *value,String *value2) + { value2->length(0); return value2;} + int cmp(const uchar *a, const uchar *b) { return 0;} + void sort_string(uchar *buff, uint length) {} + uint32 pack_length() const { return 0; } + void sql_type(String &str) const; + uint size_of() const { return sizeof(*this); } + uint32 max_display_length() { return 4; } + void move_field_offset(my_ptrdiff_t ptr_diff) {} + bool can_optimize_keypart_ref(const Item_bool_func *cond, + const Item *item) const + { + DBUG_ASSERT(0); + return false; + } + bool can_optimize_group_min_max(const Item_bool_func *cond, + const Item *const_item) const + { + DBUG_ASSERT(0); + return false; + } +}; + + +class Field_temporal: public Field { +protected: + Item *get_equal_const_item_datetime(THD *thd, const Context &ctx, + Item *const_item); +public: + Field_temporal(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg, + uchar null_bit_arg, utype unireg_check_arg, + const char *field_name_arg) + :Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, + field_name_arg) + { flags|= BINARY_FLAG; } + Item_result result_type () const { return STRING_RESULT; } + int store_hex_hybrid(const char *str, uint length) + { + return store(str, length, &my_charset_bin); + } + Copy_func *get_copy_func(const Field *from) const; + int save_in_field(Field *to) + { + MYSQL_TIME ltime; + if (get_date(<ime, 0)) + return to->reset(); + return to->store_time_dec(<ime, decimals()); + } + bool memcpy_field_possible(const Field *from) const; + uint32 max_display_length() { return field_length; } + bool str_needs_quotes() { return TRUE; } + enum Derivation derivation(void) const { return DERIVATION_NUMERIC; } + uint repertoire(void) const { return MY_REPERTOIRE_NUMERIC; } + CHARSET_INFO *charset(void) const { return &my_charset_numeric; } + CHARSET_INFO *sort_charset(void) const { return &my_charset_bin; } + bool binary() const { return true; } + enum Item_result cmp_type () const { return TIME_RESULT; } + bool val_bool() { return val_real() != 0e0; } + uint is_equal(Create_field *new_field); + bool eq_def(const Field *field) const + { + return (Field::eq_def(field) && decimals() == field->decimals()); + } + my_decimal *val_decimal(my_decimal*); + void set_warnings(Sql_condition::enum_warning_level trunc_level, + const ErrConv *str, int was_cut, timestamp_type ts_type); + double pos_in_interval(Field *min, Field *max) + { + return pos_in_interval_val_real(min, max); + } + bool can_optimize_keypart_ref(const Item_bool_func *cond, + const Item *item) const; + bool can_optimize_group_min_max(const Item_bool_func *cond, + const Item *const_item) const; + bool can_optimize_range(const Item_bool_func *cond, + const Item *item, + bool is_eq_func) const + { + return true; + } +}; + + +/** + Abstract class for: + - DATE + - DATETIME + - DATETIME(1..6) + - DATETIME(0..6) - MySQL56 version +*/ +class Field_temporal_with_date: public Field_temporal { +protected: + int store_TIME_with_warning(MYSQL_TIME *ltime, const ErrConv *str, + int was_cut, int have_smth_to_conv); + virtual void store_TIME(MYSQL_TIME *ltime) = 0; + virtual bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, + ulonglong fuzzydate) const = 0; + bool validate_MMDD(bool not_zero_date, uint month, uint day, + ulonglong fuzzydate) const + { + if (!not_zero_date) + return fuzzydate & TIME_NO_ZERO_DATE; + if (!month || !day) + return fuzzydate & TIME_NO_ZERO_IN_DATE; + return false; + } +public: + Field_temporal_with_date(uchar *ptr_arg, uint32 len_arg, + uchar *null_ptr_arg, uchar null_bit_arg, + utype unireg_check_arg, + const char *field_name_arg) + :Field_temporal(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg) + {} + int store(const char *to, uint length, CHARSET_INFO *charset); + int store(double nr); + int store(longlong nr, bool unsigned_val); + int store_time_dec(MYSQL_TIME *ltime, uint dec); + int store_decimal(const my_decimal *); + bool validate_value_in_record(THD *thd, const uchar *record) const; +}; + + +class Field_timestamp :public Field_temporal { +protected: + int store_TIME_with_warning(THD *, MYSQL_TIME *, const ErrConv *, + int warnings, bool have_smth_to_conv); +public: + Field_timestamp(uchar *ptr_arg, uint32 len_arg, + uchar *null_ptr_arg, uchar null_bit_arg, + enum utype unireg_check_arg, const char *field_name_arg, + TABLE_SHARE *share); + enum_field_types type() const { return MYSQL_TYPE_TIMESTAMP;} + enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONG_INT; } + int store(const char *to,uint length,CHARSET_INFO *charset); + int store(double nr); + int store(longlong nr, bool unsigned_val); + int store_time_dec(MYSQL_TIME *ltime, uint dec); + int store_decimal(const my_decimal *); + double val_real(void); + longlong val_int(void); + String *val_str(String*,String *); + bool send_binary(Protocol *protocol); + int cmp(const uchar *,const uchar *); + void sort_string(uchar *buff,uint length); + uint32 pack_length() const { return 4; } + void sql_type(String &str) const; + bool zero_pack() const { return 0; } + int set_time(); + bool set_explicit_default(Item *value); + int evaluate_update_default_function() + { + int res= 0; + if (has_update_default_function()) + res= set_time(); + return res; + } + /* Get TIMESTAMP field value as seconds since begging of Unix Epoch */ + virtual my_time_t get_timestamp(const uchar *pos, ulong *sec_part) const; + my_time_t get_timestamp(ulong *sec_part) const + { + return get_timestamp(ptr, sec_part); + } + virtual void store_TIME(my_time_t timestamp, ulong sec_part) + { + int4store(ptr,timestamp); + } + bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + uchar *pack(uchar *to, const uchar *from, + uint max_length __attribute__((unused))) + { + return pack_int32(to, from); + } + const uchar *unpack(uchar* to, const uchar *from, const uchar *from_end, + uint param_data __attribute__((unused))) + { + return unpack_int32(to, from, from_end); + } + bool validate_value_in_record(THD *thd, const uchar *record) const; + Item *get_equal_const_item(THD *thd, const Context &ctx, Item *const_item) + { + return get_equal_const_item_datetime(thd, ctx, const_item); + } + uint size_of() const { return sizeof(*this); } +}; + + +/** + Abstract class for: + - TIMESTAMP(1..6) + - TIMESTAMP(0..6) - MySQL56 version +*/ +class Field_timestamp_with_dec :public Field_timestamp { +protected: + uint dec; +public: + Field_timestamp_with_dec(uchar *ptr_arg, + uchar *null_ptr_arg, uchar null_bit_arg, + enum utype unireg_check_arg, + const char *field_name_arg, + TABLE_SHARE *share, uint dec_arg) : + Field_timestamp(ptr_arg, + MAX_DATETIME_WIDTH + dec_arg + MY_TEST(dec_arg), null_ptr_arg, + null_bit_arg, unireg_check_arg, field_name_arg, share), + dec(dec_arg) + { + DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS); + } + uint decimals() const { return dec; } + enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; } + uchar *pack(uchar *to, const uchar *from, uint max_length) + { return Field::pack(to, from, max_length); } + const uchar *unpack(uchar* to, const uchar *from, const uchar *from_end, + uint param_data) + { return Field::unpack(to, from, from_end, param_data); } + void make_field(Send_field *field); + void sort_string(uchar *to, uint length) + { + DBUG_ASSERT(length == pack_length()); + memcpy(to, ptr, length); + } + bool send_binary(Protocol *protocol); + double val_real(void); + my_decimal* val_decimal(my_decimal*); + int set_time(); +}; + + +class Field_timestamp_hires :public Field_timestamp_with_dec { +public: + Field_timestamp_hires(uchar *ptr_arg, + uchar *null_ptr_arg, uchar null_bit_arg, + enum utype unireg_check_arg, + const char *field_name_arg, + TABLE_SHARE *share, uint dec_arg) : + Field_timestamp_with_dec(ptr_arg, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg, share, dec_arg) + { + DBUG_ASSERT(dec); + } + my_time_t get_timestamp(const uchar *pos, ulong *sec_part) const; + void store_TIME(my_time_t timestamp, ulong sec_part); + int cmp(const uchar *,const uchar *); + uint32 pack_length() const; + uint size_of() const { return sizeof(*this); } +}; + + +/** + TIMESTAMP(0..6) - MySQL56 version +*/ +class Field_timestampf :public Field_timestamp_with_dec { + int do_save_field_metadata(uchar *metadata_ptr) + { + *metadata_ptr= decimals(); + return 1; + } +public: + Field_timestampf(uchar *ptr_arg, + uchar *null_ptr_arg, uchar null_bit_arg, + enum utype unireg_check_arg, + const char *field_name_arg, + TABLE_SHARE *share, uint dec_arg) : + Field_timestamp_with_dec(ptr_arg, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg, share, dec_arg) + {} + enum_field_types real_type() const { return MYSQL_TYPE_TIMESTAMP2; } + enum_field_types binlog_type() const { return MYSQL_TYPE_TIMESTAMP2; } + uint32 pack_length() const + { + return my_timestamp_binary_length(dec); + } + uint row_pack_length() const { return pack_length(); } + uint pack_length_from_metadata(uint field_metadata) + { + DBUG_ENTER("Field_timestampf::pack_length_from_metadata"); + uint tmp= my_timestamp_binary_length(field_metadata); + DBUG_RETURN(tmp); + } + int cmp(const uchar *a_ptr,const uchar *b_ptr) + { + return memcmp(a_ptr, b_ptr, pack_length()); + } + bool set_max(); + bool is_max(); + void store_TIME(my_time_t timestamp, ulong sec_part); + my_time_t get_timestamp(const uchar *pos, ulong *sec_part) const; + uint size_of() const { return sizeof(*this); } +}; + + +class Field_year :public Field_tiny { +public: + Field_year(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, + uchar null_bit_arg, + enum utype unireg_check_arg, const char *field_name_arg) + :Field_tiny(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg, 1, 1) + {} + enum_field_types type() const { return MYSQL_TYPE_YEAR;} + Copy_func *get_copy_func(const Field *from) const + { + if (eq_def(from)) + return get_identical_copy_func(); + switch (from->cmp_type()) { + case STRING_RESULT: + return do_field_string; + case TIME_RESULT: + return do_field_temporal; + case DECIMAL_RESULT: + return do_field_decimal; + case REAL_RESULT: + return do_field_real; + case INT_RESULT: + break; + case ROW_RESULT: + default: + DBUG_ASSERT(0); + break; + } + return do_field_int; + } + int store(const char *to,uint length,CHARSET_INFO *charset); + int store(double nr); + int store(longlong nr, bool unsigned_val); + int store_time_dec(MYSQL_TIME *ltime, uint dec); + double val_real(void); + longlong val_int(void); + String *val_str(String*,String *); + bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool send_binary(Protocol *protocol); + uint32 max_display_length() { return field_length; } + void sql_type(String &str) const; +}; + + +class Field_date :public Field_temporal_with_date { + void store_TIME(MYSQL_TIME *ltime); + bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const; +public: + Field_date(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, + enum utype unireg_check_arg, const char *field_name_arg) + :Field_temporal_with_date(ptr_arg, MAX_DATE_WIDTH, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg) {} + enum_field_types type() const { return MYSQL_TYPE_DATE;} + enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONG_INT; } + int reset(void) { ptr[0]=ptr[1]=ptr[2]=ptr[3]=0; return 0; } + bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + { return Field_date::get_TIME(ltime, ptr, fuzzydate); } + double val_real(void); + longlong val_int(void); + String *val_str(String*,String *); + bool send_binary(Protocol *protocol); + int cmp(const uchar *,const uchar *); + void sort_string(uchar *buff,uint length); + uint32 pack_length() const { return 4; } + void sql_type(String &str) const; + uchar *pack(uchar* to, const uchar *from, + uint max_length __attribute__((unused))) + { + return pack_int32(to, from); + } + const uchar *unpack(uchar* to, const uchar *from, const uchar *from_end, + uint param_data __attribute__((unused))) + { + return unpack_int32(to, from, from_end); + } + uint size_of() const { return sizeof(*this); } +}; + + +class Field_newdate :public Field_temporal_with_date { + void store_TIME(MYSQL_TIME *ltime); + bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const; +public: + Field_newdate(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, + enum utype unireg_check_arg, const char *field_name_arg) + :Field_temporal_with_date(ptr_arg, MAX_DATE_WIDTH, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg) + {} + enum_field_types type() const { return MYSQL_TYPE_DATE;} + enum_field_types real_type() const { return MYSQL_TYPE_NEWDATE; } + enum ha_base_keytype key_type() const { return HA_KEYTYPE_UINT24; } + int reset(void) { ptr[0]=ptr[1]=ptr[2]=0; return 0; } + double val_real(void); + longlong val_int(void); + String *val_str(String*,String *); + bool send_binary(Protocol *protocol); + int cmp(const uchar *,const uchar *); + void sort_string(uchar *buff,uint length); + uint32 pack_length() const { return 3; } + void sql_type(String &str) const; + bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + { return Field_newdate::get_TIME(ltime, ptr, fuzzydate); } + uint size_of() const { return sizeof(*this); } + Item *get_equal_const_item(THD *thd, const Context &ctx, Item *const_item); +}; + + +class Field_time :public Field_temporal { + /* + when this Field_time instance is used for storing values for index lookups + (see class store_key, Field::new_key_field(), etc), the following + might be set to TO_DAYS(CURDATE()). See also Field_time::store_time_dec() + */ + long curdays; +protected: + virtual void store_TIME(MYSQL_TIME *ltime); + int store_TIME_with_warning(MYSQL_TIME *ltime, const ErrConv *str, + int was_cut, int have_smth_to_conv); + bool check_zero_in_date_with_warn(ulonglong fuzzydate); + static void do_field_time(Copy_field *copy); +public: + Field_time(uchar *ptr_arg, uint length_arg, uchar *null_ptr_arg, + uchar null_bit_arg, enum utype unireg_check_arg, + const char *field_name_arg) + :Field_temporal(ptr_arg, length_arg, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg), curdays(0) + {} + enum_field_types type() const { return MYSQL_TYPE_TIME;} + enum ha_base_keytype key_type() const { return HA_KEYTYPE_INT24; } + Copy_func *get_copy_func(const Field *from) const + { + return from->cmp_type() == REAL_RESULT ? do_field_string : // MDEV-9344 + from->type() == MYSQL_TYPE_YEAR ? do_field_int : + from->type() == MYSQL_TYPE_BIT ? do_field_int : + eq_def(from) ? get_identical_copy_func() : + do_field_time; + } + bool memcpy_field_possible(const Field *from) const + { + return real_type() == from->real_type() && + decimals() == from->decimals(); + } + int store_time_dec(MYSQL_TIME *ltime, uint dec); + int store(const char *to,uint length,CHARSET_INFO *charset); + int store(double nr); + int store(longlong nr, bool unsigned_val); + int store_decimal(const my_decimal *); + double val_real(void); + longlong val_int(void); + String *val_str(String*,String *); + bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool send_binary(Protocol *protocol); + int cmp(const uchar *,const uchar *); + void sort_string(uchar *buff,uint length); + uint32 pack_length() const { return 3; } + void sql_type(String &str) const; + uint size_of() const { return sizeof(*this); } + void set_curdays(THD *thd); + Field *new_key_field(MEM_ROOT *root, TABLE *new_table, + uchar *new_ptr, uint32 length, + uchar *new_null_ptr, uint new_null_bit); + Item *get_equal_const_item(THD *thd, const Context &ctx, Item *const_item); +}; + + +/** + Abstract class for: + - TIME(1..6) + - TIME(0..6) - MySQL56 version +*/ +class Field_time_with_dec :public Field_time { +protected: + uint dec; +public: + Field_time_with_dec(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, + enum utype unireg_check_arg, const char *field_name_arg, + uint dec_arg) + :Field_time(ptr_arg, MIN_TIME_WIDTH + dec_arg + MY_TEST(dec_arg), + null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg), + dec(dec_arg) + { + DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS); + } + uint decimals() const { return dec; } + enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; } + longlong val_int(void); + double val_real(void); + void make_field(Send_field *); +}; + + +/** + TIME(1..6) +*/ +class Field_time_hires :public Field_time_with_dec { + longlong zero_point; + void store_TIME(MYSQL_TIME *ltime); +public: + Field_time_hires(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, + enum utype unireg_check_arg, const char *field_name_arg, + uint dec_arg) + :Field_time_with_dec(ptr_arg, null_ptr_arg, + null_bit_arg, unireg_check_arg, field_name_arg, + dec_arg) + { + DBUG_ASSERT(dec); + zero_point= sec_part_shift( + ((TIME_MAX_VALUE_SECONDS+1LL)*TIME_SECOND_PART_FACTOR), dec); + } + int reset(void); + bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + int cmp(const uchar *,const uchar *); + void sort_string(uchar *buff,uint length); + uint32 pack_length() const; + uint size_of() const { return sizeof(*this); } +}; + + +/** + TIME(0..6) - MySQL56 version +*/ +class Field_timef :public Field_time_with_dec { + void store_TIME(MYSQL_TIME *ltime); + int do_save_field_metadata(uchar *metadata_ptr) + { + *metadata_ptr= decimals(); + return 1; + } +public: + Field_timef(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, + enum utype unireg_check_arg, const char *field_name_arg, + uint dec_arg) + :Field_time_with_dec(ptr_arg, null_ptr_arg, + null_bit_arg, unireg_check_arg, field_name_arg, + dec_arg) + { + DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS); + } + enum_field_types real_type() const { return MYSQL_TYPE_TIME2; } + enum_field_types binlog_type() const { return MYSQL_TYPE_TIME2; } + uint32 pack_length() const + { + return my_time_binary_length(dec); + } + uint row_pack_length() const { return pack_length(); } + uint pack_length_from_metadata(uint field_metadata) + { + DBUG_ENTER("Field_timef::pack_length_from_metadata"); + uint tmp= my_time_binary_length(field_metadata); + DBUG_RETURN(tmp); + } + void sort_string(uchar *to, uint length) + { + DBUG_ASSERT(length == Field_timef::pack_length()); + memcpy(to, ptr, length); + } + int cmp(const uchar *a_ptr, const uchar *b_ptr) + { + return memcmp(a_ptr, b_ptr, pack_length()); + } + int reset(); + bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + uint size_of() const { return sizeof(*this); } +}; + + +class Field_datetime :public Field_temporal_with_date { + void store_TIME(MYSQL_TIME *ltime); + bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const; +public: + Field_datetime(uchar *ptr_arg, uint length_arg, uchar *null_ptr_arg, + uchar null_bit_arg, enum utype unireg_check_arg, + const char *field_name_arg) + :Field_temporal_with_date(ptr_arg, length_arg, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg) + { + if (unireg_check == TIMESTAMP_UN_FIELD || + unireg_check == TIMESTAMP_DNUN_FIELD) + flags|= ON_UPDATE_NOW_FLAG; + } + enum_field_types type() const { return MYSQL_TYPE_DATETIME;} + enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONGLONG; } + double val_real(void); + longlong val_int(void); + String *val_str(String*,String *); + bool send_binary(Protocol *protocol); + int cmp(const uchar *,const uchar *); + void sort_string(uchar *buff,uint length); + uint32 pack_length() const { return 8; } + void sql_type(String &str) const; + bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + { return Field_datetime::get_TIME(ltime, ptr, fuzzydate); } + int set_time(); + int evaluate_update_default_function() + { + int res= 0; + if (has_update_default_function()) + res= set_time(); + return res; + } + uchar *pack(uchar* to, const uchar *from, + uint max_length __attribute__((unused))) + { + return pack_int64(to, from); + } + const uchar *unpack(uchar* to, const uchar *from, const uchar *from_end, + uint param_data __attribute__((unused))) + { + return unpack_int64(to, from, from_end); + } + Item *get_equal_const_item(THD *thd, const Context &ctx, Item *const_item) + { + return get_equal_const_item_datetime(thd, ctx, const_item); + } + uint size_of() const { return sizeof(*this); } +}; + + +/** + Abstract class for: + - DATETIME(1..6) + - DATETIME(0..6) - MySQL56 version +*/ +class Field_datetime_with_dec :public Field_datetime { +protected: + uint dec; +public: + Field_datetime_with_dec(uchar *ptr_arg, uchar *null_ptr_arg, + uchar null_bit_arg, enum utype unireg_check_arg, + const char *field_name_arg, uint dec_arg) + :Field_datetime(ptr_arg, MAX_DATETIME_WIDTH + dec_arg + MY_TEST(dec_arg), + null_ptr_arg, null_bit_arg, unireg_check_arg, + field_name_arg), dec(dec_arg) + { + DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS); + } + uint decimals() const { return dec; } + enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; } + void make_field(Send_field *field); + bool send_binary(Protocol *protocol); + uchar *pack(uchar *to, const uchar *from, uint max_length) + { return Field::pack(to, from, max_length); } + const uchar *unpack(uchar* to, const uchar *from, const uchar *from_end, + uint param_data) + { return Field::unpack(to, from, from_end, param_data); } + void sort_string(uchar *to, uint length) + { + DBUG_ASSERT(length == pack_length()); + memcpy(to, ptr, length); + } + double val_real(void); + longlong val_int(void); + String *val_str(String*,String *); +}; + + +/** + DATETIME(1..6) +*/ +class Field_datetime_hires :public Field_datetime_with_dec { + void store_TIME(MYSQL_TIME *ltime); + bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const; +public: + Field_datetime_hires(uchar *ptr_arg, uchar *null_ptr_arg, + uchar null_bit_arg, enum utype unireg_check_arg, + const char *field_name_arg, uint dec_arg) + :Field_datetime_with_dec(ptr_arg, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg, dec_arg) + { + DBUG_ASSERT(dec); + } + int cmp(const uchar *,const uchar *); + uint32 pack_length() const; + bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + { return Field_datetime_hires::get_TIME(ltime, ptr, fuzzydate); } + uint size_of() const { return sizeof(*this); } +}; + + +/** + DATETIME(0..6) - MySQL56 version +*/ +class Field_datetimef :public Field_datetime_with_dec { + void store_TIME(MYSQL_TIME *ltime); + bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const; + int do_save_field_metadata(uchar *metadata_ptr) + { + *metadata_ptr= decimals(); + return 1; + } +public: + Field_datetimef(uchar *ptr_arg, uchar *null_ptr_arg, + uchar null_bit_arg, enum utype unireg_check_arg, + const char *field_name_arg, uint dec_arg) + :Field_datetime_with_dec(ptr_arg, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg, dec_arg) + {} + enum_field_types real_type() const { return MYSQL_TYPE_DATETIME2; } + enum_field_types binlog_type() const { return MYSQL_TYPE_DATETIME2; } + uint32 pack_length() const + { + return my_datetime_binary_length(dec); + } + uint row_pack_length() const { return pack_length(); } + uint pack_length_from_metadata(uint field_metadata) + { + DBUG_ENTER("Field_datetimef::pack_length_from_metadata"); + uint tmp= my_datetime_binary_length(field_metadata); + DBUG_RETURN(tmp); + } + int cmp(const uchar *a_ptr, const uchar *b_ptr) + { + return memcmp(a_ptr, b_ptr, pack_length()); + } + int reset(); + bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + { return Field_datetimef::get_TIME(ltime, ptr, fuzzydate); } + uint size_of() const { return sizeof(*this); } +}; + + +static inline Field_timestamp * +new_Field_timestamp(MEM_ROOT *root,uchar *ptr, uchar *null_ptr, uchar null_bit, + enum Field::utype unireg_check, const char *field_name, + TABLE_SHARE *share, uint dec) +{ + if (dec==0) + return new (root) + Field_timestamp(ptr, MAX_DATETIME_WIDTH, null_ptr, + null_bit, unireg_check, field_name, share); + if (dec >= FLOATING_POINT_DECIMALS) + dec= MAX_DATETIME_PRECISION; + return new (root) + Field_timestamp_hires(ptr, null_ptr, null_bit, unireg_check, + field_name, share, dec); +} + +static inline Field_time * +new_Field_time(MEM_ROOT *root, uchar *ptr, uchar *null_ptr, uchar null_bit, + enum Field::utype unireg_check, const char *field_name, + uint dec) +{ + if (dec == 0) + return new (root) + Field_time(ptr, MIN_TIME_WIDTH, null_ptr, null_bit, unireg_check, + field_name); + if (dec >= FLOATING_POINT_DECIMALS) + dec= MAX_DATETIME_PRECISION; + return new (root) + Field_time_hires(ptr, null_ptr, null_bit, unireg_check, field_name, dec); +} + +static inline Field_datetime * +new_Field_datetime(MEM_ROOT *root, uchar *ptr, uchar *null_ptr, uchar null_bit, + enum Field::utype unireg_check, + const char *field_name, uint dec) +{ + if (dec == 0) + return new (root) + Field_datetime(ptr, MAX_DATETIME_WIDTH, null_ptr, null_bit, + unireg_check, field_name); + if (dec >= FLOATING_POINT_DECIMALS) + dec= MAX_DATETIME_PRECISION; + return new (root) + Field_datetime_hires(ptr, null_ptr, null_bit, + unireg_check, field_name, dec); +} + +class Field_string :public Field_longstr { + class Warn_filter_string: public Warn_filter + { + public: + Warn_filter_string(const THD *thd, const Field_string *field); + }; +public: + bool can_alter_field_type; + Field_string(uchar *ptr_arg, uint32 len_arg,uchar *null_ptr_arg, + uchar null_bit_arg, + enum utype unireg_check_arg, const char *field_name_arg, + CHARSET_INFO *cs) + :Field_longstr(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg, cs), + can_alter_field_type(1) {}; + Field_string(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg, + CHARSET_INFO *cs) + :Field_longstr((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0, 0, + NONE, field_name_arg, cs), + can_alter_field_type(1) {}; + + enum_field_types type() const + { + return ((can_alter_field_type && orig_table && + orig_table->s->db_create_options & HA_OPTION_PACK_RECORD && + field_length >= 4) && + orig_table->s->frm_version < FRM_VER_TRUE_VARCHAR ? + MYSQL_TYPE_VAR_STRING : MYSQL_TYPE_STRING); + } + enum ha_base_keytype key_type() const + { return binary() ? HA_KEYTYPE_BINARY : HA_KEYTYPE_TEXT; } + bool zero_pack() const { return 0; } + Copy_func *get_copy_func(const Field *from) const; + int reset(void) + { + charset()->cset->fill(charset(),(char*) ptr, field_length, + (has_charset() ? ' ' : 0)); + return 0; + } + int store(const char *to,uint length,CHARSET_INFO *charset); + int store(longlong nr, bool unsigned_val); + int store(double nr) { return Field_str::store(nr); } /* QQ: To be deleted */ + double val_real(void); + longlong val_int(void); + String *val_str(String*,String *); + my_decimal *val_decimal(my_decimal *); + int cmp(const uchar *,const uchar *); + void sort_string(uchar *buff,uint length); + void sql_type(String &str) const; + virtual uchar *pack(uchar *to, const uchar *from, + uint max_length); + virtual const uchar *unpack(uchar* to, const uchar *from, + const uchar *from_end,uint param_data); + uint pack_length_from_metadata(uint field_metadata) + { + DBUG_PRINT("debug", ("field_metadata: 0x%04x", field_metadata)); + if (field_metadata == 0) + return row_pack_length(); + return (((field_metadata >> 4) & 0x300) ^ 0x300) + (field_metadata & 0x00ff); + } + bool compatible_field_size(uint field_metadata, Relay_log_info *rli, + uint16 mflags, int *order_var); + uint row_pack_length() const { return field_length; } + int pack_cmp(const uchar *a,const uchar *b,uint key_length, + bool insert_or_update); + int pack_cmp(const uchar *b,uint key_length,bool insert_or_update); + uint packed_col_length(const uchar *to, uint length); + uint max_packed_col_length(uint max_length); + uint size_of() const { return sizeof(*this); } + enum_field_types real_type() const { return MYSQL_TYPE_STRING; } + bool has_charset(void) const + { return charset() == &my_charset_bin ? FALSE : TRUE; } + Field *make_new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type); + virtual uint get_key_image(uchar *buff,uint length, imagetype type); +private: + int do_save_field_metadata(uchar *first_byte); +}; + + +class Field_varstring :public Field_longstr { + uchar *get_data() const + { + return ptr + length_bytes; + } + uint get_length() const + { + return length_bytes == 1 ? (uint) *ptr : uint2korr(ptr); + } +public: + /* + The maximum space available in a Field_varstring, in bytes. See + length_bytes. + */ + static const uint MAX_SIZE; + /* Store number of bytes used to store length (1 or 2) */ + uint32 length_bytes; + Field_varstring(uchar *ptr_arg, + uint32 len_arg, uint length_bytes_arg, + uchar *null_ptr_arg, uchar null_bit_arg, + enum utype unireg_check_arg, const char *field_name_arg, + TABLE_SHARE *share, CHARSET_INFO *cs) + :Field_longstr(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg, cs), + length_bytes(length_bytes_arg) + { + share->varchar_fields++; + } + Field_varstring(uint32 len_arg,bool maybe_null_arg, + const char *field_name_arg, + TABLE_SHARE *share, CHARSET_INFO *cs) + :Field_longstr((uchar*) 0,len_arg, maybe_null_arg ? (uchar*) "": 0, 0, + NONE, field_name_arg, cs), + length_bytes(len_arg < 256 ? 1 :2) + { + share->varchar_fields++; + } + + enum_field_types type() const { return MYSQL_TYPE_VARCHAR; } + enum ha_base_keytype key_type() const; + uint row_pack_length() const { return field_length; } + bool zero_pack() const { return 0; } + int reset(void) { bzero(ptr,field_length+length_bytes); return 0; } + uint32 pack_length() const { return (uint32) field_length+length_bytes; } + uint32 key_length() const { return (uint32) field_length; } + uint32 sort_length() const + { + return (uint32) field_length + (field_charset == &my_charset_bin ? + length_bytes : 0); + } + Copy_func *get_copy_func(const Field *from) const; + bool memcpy_field_possible(const Field *from) const + { + return Field_str::memcpy_field_possible(from) && + length_bytes == ((Field_varstring*) from)->length_bytes; + } + int store(const char *to,uint length,CHARSET_INFO *charset); + int store(longlong nr, bool unsigned_val); + int store(double nr) { return Field_str::store(nr); } /* QQ: To be deleted */ + double val_real(void); + longlong val_int(void); + String *val_str(String*,String *); + my_decimal *val_decimal(my_decimal *); + int cmp_max(const uchar *, const uchar *, uint max_length); + int cmp(const uchar *a,const uchar *b) + { + return cmp_max(a, b, ~0U); + } + void sort_string(uchar *buff,uint length); + uint get_key_image(uchar *buff,uint length, imagetype type); + void set_key_image(const uchar *buff,uint length); + void sql_type(String &str) const; + virtual uchar *pack(uchar *to, const uchar *from, uint max_length); + virtual const uchar *unpack(uchar* to, const uchar *from, + const uchar *from_end, uint param_data); + int cmp_binary(const uchar *a,const uchar *b, uint32 max_length=~0U); + int key_cmp(const uchar *,const uchar*); + int key_cmp(const uchar *str, uint length); + uint packed_col_length(const uchar *to, uint length); + uint max_packed_col_length(uint max_length); + uint32 data_length(); + uint size_of() const { return sizeof(*this); } + enum_field_types real_type() const { return MYSQL_TYPE_VARCHAR; } + bool has_charset(void) const + { return charset() == &my_charset_bin ? FALSE : TRUE; } + Field *make_new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type); + Field *new_key_field(MEM_ROOT *root, TABLE *new_table, + uchar *new_ptr, uint32 length, + uchar *new_null_ptr, uint new_null_bit); + uint is_equal(Create_field *new_field); + void hash(ulong *nr, ulong *nr2); + uint length_size() { return length_bytes; } +private: + int do_save_field_metadata(uchar *first_byte); +}; + + +class Field_blob :public Field_longstr { +protected: + /** + The number of bytes used to represent the length of the blob. + */ + uint packlength; + + /** + The 'value'-object is a cache fronting the storage engine. + */ + String value; + /** + Cache for blob values when reading a row with a virtual blob + field. This is needed to not destroy the old cached value when + updating the blob with a new value when creating the new row. + */ + String read_value; + + static void do_copy_blob(Copy_field *copy); + static void do_conv_blob(Copy_field *copy); +public: + Field_blob(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, + enum utype unireg_check_arg, const char *field_name_arg, + TABLE_SHARE *share, uint blob_pack_length, CHARSET_INFO *cs); + Field_blob(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg, + CHARSET_INFO *cs) + :Field_longstr((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0, 0, + NONE, field_name_arg, cs), + packlength(4) + { + flags|= BLOB_FLAG; + } + Field_blob(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg, + CHARSET_INFO *cs, bool set_packlength) + :Field_longstr((uchar*) 0,len_arg, maybe_null_arg ? (uchar*) "": 0, 0, + NONE, field_name_arg, cs) + { + flags|= BLOB_FLAG; + packlength= 4; + if (set_packlength) + { + packlength= len_arg <= 255 ? 1 : + len_arg <= 65535 ? 2 : + len_arg <= 16777215 ? 3 : 4; + } + } + Field_blob(uint32 packlength_arg) + :Field_longstr((uchar*) 0, 0, (uchar*) "", 0, NONE, "temp", system_charset_info), + packlength(packlength_arg) {} + /* Note that the default copy constructor is used, in clone() */ + enum_field_types type() const { return MYSQL_TYPE_BLOB;} + enum ha_base_keytype key_type() const + { return binary() ? HA_KEYTYPE_VARBINARY2 : HA_KEYTYPE_VARTEXT2; } + Copy_func *get_copy_func(const Field *from) const + { + /* + TODO: MDEV-9331 + if (from->type() == MYSQL_TYPE_BIT) + return do_field_int; + */ + if (!(from->flags & BLOB_FLAG) || from->charset() != charset()) + return do_conv_blob; + if (from->pack_length() != Field_blob::pack_length()) + return do_copy_blob; + return get_identical_copy_func(); + } + int store_field(Field *from) + { // Be sure the value is stored + from->val_str(&value); + if (table->copy_blobs || + (!value.is_alloced() && from->is_varchar_and_in_write_set())) + value.copy(); + return store(value.ptr(), value.length(), from->charset()); + } + bool memcpy_field_possible(const Field *from) const + { + return Field_str::memcpy_field_possible(from) && + !table->copy_blobs; + } + int store(const char *to,uint length,CHARSET_INFO *charset); + int store(double nr); + int store(longlong nr, bool unsigned_val); + double val_real(void); + longlong val_int(void); + String *val_str(String*,String *); + my_decimal *val_decimal(my_decimal *); + int cmp_max(const uchar *, const uchar *, uint max_length); + int cmp(const uchar *a,const uchar *b) + { return cmp_max(a, b, ~0U); } + int cmp(const uchar *a, uint32 a_length, const uchar *b, uint32 b_length); + int cmp_binary(const uchar *a,const uchar *b, uint32 max_length=~0U); + int key_cmp(const uchar *,const uchar*); + int key_cmp(const uchar *str, uint length); + /* Never update the value of min_val for a blob field */ + bool update_min(Field *min_val, bool force_update) { return FALSE; } + /* Never update the value of max_val for a blob field */ + bool update_max(Field *max_val, bool force_update) { return FALSE; } + uint32 key_length() const { return 0; } + void sort_string(uchar *buff,uint length); + uint32 pack_length() const + { return (uint32) (packlength + portable_sizeof_char_ptr); } + + /** + Return the packed length without the pointer size added. + + This is used to determine the size of the actual data in the row + buffer. + + @returns The length of the raw data itself without the pointer. + */ + uint32 pack_length_no_ptr() const + { return (uint32) (packlength); } + uint row_pack_length() const { return pack_length_no_ptr(); } + uint32 sort_length() const; + uint32 value_length() { return get_length(); } + virtual uint32 max_data_length() const + { + return (uint32) (((ulonglong) 1 << (packlength*8)) -1); + } + int reset(void) { bzero(ptr, packlength+sizeof(uchar*)); return 0; } + void reset_fields() { bzero((uchar*) &value,sizeof(value)); bzero((uchar*) &read_value,sizeof(read_value)); } + uint32 get_field_buffer_size(void) { return value.alloced_length(); } + void store_length(uchar *i_ptr, uint i_packlength, uint32 i_number); + inline void store_length(uint32 number) + { + store_length(ptr, packlength, number); + } + inline uint32 get_length(uint row_offset= 0) const + { return get_length(ptr+row_offset, this->packlength); } + uint32 get_length(const uchar *ptr, uint packlength) const; + uint32 get_length(const uchar *ptr_arg) const + { return get_length(ptr_arg, this->packlength); } + inline uchar *get_ptr() const { return get_ptr(0); } + inline uchar *get_ptr(my_ptrdiff_t row_offset) const + { + uchar *s; + memcpy(&s, ptr + packlength + row_offset, sizeof(uchar*)); + return s; + } + inline void set_ptr(uchar *length, uchar *data) + { + memcpy(ptr,length,packlength); + memcpy(ptr+packlength, &data,sizeof(char*)); + } + void set_ptr_offset(my_ptrdiff_t ptr_diff, uint32 length, uchar *data) + { + uchar *ptr_ofs= ADD_TO_PTR(ptr,ptr_diff,uchar*); + store_length(ptr_ofs, packlength, length); + memcpy(ptr_ofs+packlength, &data, sizeof(char*)); + } + inline void set_ptr(uint32 length, uchar *data) + { + set_ptr_offset(0, length, data); + } + int copy_value(Field_blob *from); + uint get_key_image(uchar *buff,uint length, imagetype type); + void set_key_image(const uchar *buff,uint length); + Field *new_key_field(MEM_ROOT *root, TABLE *new_table, + uchar *new_ptr, uint32 length, + uchar *new_null_ptr, uint new_null_bit); + void sql_type(String &str) const; + inline bool copy() + { + uchar *tmp= get_ptr(); + if (value.copy((char*) tmp, get_length(), charset())) + { + Field_blob::reset(); + return 1; + } + tmp=(uchar*) value.ptr(); + memcpy(ptr+packlength, &tmp, sizeof(char*)); + return 0; + } + /* store value for the duration of the current read record */ + inline void swap_value_and_read_value() + { + read_value.swap(value); + } + inline void set_value(uchar *data) + { + /* Set value pointer. Lengths are not important */ + value.reset((char*) data, 1, 1, &my_charset_bin); + } + virtual uchar *pack(uchar *to, const uchar *from, uint max_length); + virtual const uchar *unpack(uchar *to, const uchar *from, + const uchar *from_end, uint param_data); + uint packed_col_length(const uchar *col_ptr, uint length); + uint max_packed_col_length(uint max_length); + void free() + { + value.free(); + read_value.free(); + } + inline void clear_temporary() + { + uchar *tmp= get_ptr(); + if (likely(value.ptr() == (char*) tmp)) + bzero((uchar*) &value, sizeof(value)); + else + { + /* + Currently read_value should never point to tmp, the following code + is mainly here to make things future proof. + */ + if (unlikely(read_value.ptr() == (char*) tmp)) + bzero((uchar*) &read_value, sizeof(read_value)); + } + } + uint size_of() const { return sizeof(*this); } + bool has_charset(void) const + { return charset() == &my_charset_bin ? FALSE : TRUE; } + uint32 max_display_length(); + uint32 char_length() const; + uint is_equal(Create_field *new_field); +private: + int do_save_field_metadata(uchar *first_byte); +}; + + +#ifdef HAVE_SPATIAL +class Field_geom :public Field_blob { +public: + enum geometry_type geom_type; + uint srid; + uint precision; + enum storage_type { GEOM_STORAGE_WKB= 0, GEOM_STORAGE_BINARY= 1}; + enum storage_type storage; + + Field_geom(uchar *ptr_arg, uchar *null_ptr_arg, uint null_bit_arg, + enum utype unireg_check_arg, const char *field_name_arg, + TABLE_SHARE *share, uint blob_pack_length, + enum geometry_type geom_type_arg, uint field_srid) + :Field_blob(ptr_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, + field_name_arg, share, blob_pack_length, &my_charset_bin) + { geom_type= geom_type_arg; srid= field_srid; } + Field_geom(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg, + TABLE_SHARE *share, enum geometry_type geom_type_arg) + :Field_blob(len_arg, maybe_null_arg, field_name_arg, &my_charset_bin) + { geom_type= geom_type_arg; srid= 0; } + enum ha_base_keytype key_type() const { return HA_KEYTYPE_VARBINARY2; } + enum_field_types type() const { return MYSQL_TYPE_GEOMETRY; } + bool can_optimize_range(const Item_bool_func *cond, + const Item *item, + bool is_eq_func) const; + void sql_type(String &str) const; + uint is_equal(Create_field *new_field); + int store(const char *to, uint length, CHARSET_INFO *charset); + int store(double nr); + int store(longlong nr, bool unsigned_val); + int store_decimal(const my_decimal *); + uint size_of() const { return sizeof(*this); } + /** + Key length is provided only to support hash joins. (compared byte for byte) + Ex: SELECT .. FROM t1,t2 WHERE t1.field_geom1=t2.field_geom2. + + The comparison is not very relevant, as identical geometry might be + represented differently, but we need to support it either way. + */ + uint32 key_length() const { return packlength; } + + /** + Non-nullable GEOMETRY types cannot have defaults, + but the underlying blob must still be reset. + */ + int reset(void) { return Field_blob::reset() || !maybe_null(); } + + geometry_type get_geometry_type() { return geom_type; }; + static geometry_type geometry_type_merge(geometry_type, geometry_type); + uint get_srid() { return srid; } +}; + +uint gis_field_options_image(uchar *buff, List &create_fields); +uint gis_field_options_read(const uchar *buf, uint buf_len, + Field_geom::storage_type *st_type,uint *precision, uint *scale, uint *srid); + +#endif /*HAVE_SPATIAL*/ + + +class Field_enum :public Field_str { + static void do_field_enum(Copy_field *copy_field); +protected: + uint packlength; +public: + TYPELIB *typelib; + Field_enum(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, + uchar null_bit_arg, + enum utype unireg_check_arg, const char *field_name_arg, + uint packlength_arg, + TYPELIB *typelib_arg, + CHARSET_INFO *charset_arg) + :Field_str(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg, charset_arg), + packlength(packlength_arg),typelib(typelib_arg) + { + flags|=ENUM_FLAG; + } + Field *make_new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type); + enum_field_types type() const { return MYSQL_TYPE_STRING; } + enum Item_result cmp_type () const { return INT_RESULT; } + const Type_handler *cast_to_int_type_handler() const + { + return &type_handler_longlong; + } + enum ha_base_keytype key_type() const; + Copy_func *get_copy_func(const Field *from) const + { + if (eq_def(from)) + return get_identical_copy_func(); + if (real_type() == MYSQL_TYPE_ENUM && + from->real_type() == MYSQL_TYPE_ENUM) + return do_field_enum; + if (from->result_type() == STRING_RESULT) + return do_field_string; + return do_field_int; + } + int store_field(Field *from) + { + if (from->real_type() == MYSQL_TYPE_ENUM && from->val_int() == 0) + { + store_type(0); + return 0; + } + return from->save_in_field(this); + } + int save_in_field(Field *to) + { + if (to->result_type() != STRING_RESULT) + return to->store(val_int(), 0); + return save_in_field_str(to); + } + bool memcpy_field_possible(const Field *from) const { return false; } + int store(const char *to,uint length,CHARSET_INFO *charset); + int store(double nr); + int store(longlong nr, bool unsigned_val); + double val_real(void); + longlong val_int(void); + String *val_str(String*,String *); + int cmp(const uchar *,const uchar *); + void sort_string(uchar *buff,uint length); + uint32 pack_length() const { return (uint32) packlength; } + void store_type(ulonglong value); + void sql_type(String &str) const; + uint size_of() const { return sizeof(*this); } + enum_field_types real_type() const { return MYSQL_TYPE_ENUM; } + uint pack_length_from_metadata(uint field_metadata) + { return (field_metadata & 0x00ff); } + uint row_pack_length() const { return pack_length(); } + virtual bool zero_pack() const { return 0; } + bool optimize_range(uint idx, uint part) { return 0; } + bool eq_def(const Field *field) const; + bool has_charset(void) const { return TRUE; } + /* enum and set are sorted as integers */ + CHARSET_INFO *sort_charset(void) const { return &my_charset_bin; } + uint decimals() const { return 0; } + + virtual uchar *pack(uchar *to, const uchar *from, uint max_length); + virtual const uchar *unpack(uchar *to, const uchar *from, + const uchar *from_end, uint param_data); + + bool can_optimize_keypart_ref(const Item_bool_func *cond, + const Item *item) const; + bool can_optimize_group_min_max(const Item_bool_func *cond, + const Item *const_item) const + { + /* + Can't use GROUP_MIN_MAX optimization for ENUM and SET, + because the values are stored as numbers in index, + while MIN() and MAX() work as strings. + It would return the records with min and max enum numeric indexes. + "Bug#45300 MAX() and ENUM type" should be fixed first. + */ + return false; + } +private: + int do_save_field_metadata(uchar *first_byte); + uint is_equal(Create_field *new_field); +}; + + +class Field_set :public Field_enum { +public: + Field_set(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, + uchar null_bit_arg, + enum utype unireg_check_arg, const char *field_name_arg, + uint32 packlength_arg, + TYPELIB *typelib_arg, CHARSET_INFO *charset_arg) + :Field_enum(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg, + packlength_arg, + typelib_arg,charset_arg), + empty_set_string("", 0, charset_arg) + { + flags=(flags & ~ENUM_FLAG) | SET_FLAG; + } + int store_field(Field *from) { return from->save_in_field(this); } + int store(const char *to,uint length,CHARSET_INFO *charset); + int store(double nr) { return Field_set::store((longlong) nr, FALSE); } + int store(longlong nr, bool unsigned_val); + + virtual bool zero_pack() const { return 1; } + String *val_str(String*,String *); + void sql_type(String &str) const; + uint size_of() const { return sizeof(*this); } + enum_field_types real_type() const { return MYSQL_TYPE_SET; } + bool has_charset(void) const { return TRUE; } +private: + const String empty_set_string; +}; + + +/* + Note: + To use Field_bit::cmp_binary() you need to copy the bits stored in + the beginning of the record (the NULL bytes) to each memory you + want to compare (where the arguments point). + + This is the reason: + - Field_bit::cmp_binary() is only implemented in the base class + (Field::cmp_binary()). + - Field::cmp_binary() currenly use pack_length() to calculate how + long the data is. + - pack_length() includes size of the bits stored in the NULL bytes + of the record. +*/ +class Field_bit :public Field { +public: + uchar *bit_ptr; // position in record where 'uneven' bits store + uchar bit_ofs; // offset to 'uneven' high bits + uint bit_len; // number of 'uneven' high bits + uint bytes_in_rec; + Field_bit(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, + uchar null_bit_arg, uchar *bit_ptr_arg, uchar bit_ofs_arg, + enum utype unireg_check_arg, const char *field_name_arg); + enum_field_types type() const { return MYSQL_TYPE_BIT; } + enum ha_base_keytype key_type() const { return HA_KEYTYPE_BIT; } + uint32 key_length() const { return (uint32) (field_length + 7) / 8; } + uint32 max_data_length() const { return (field_length + 7) / 8; } + uint32 max_display_length() { return field_length; } + uint size_of() const { return sizeof(*this); } + Item_result result_type () const { return INT_RESULT; } + int reset(void) { + bzero(ptr, bytes_in_rec); + if (bit_ptr && (bit_len > 0)) // reset odd bits among null bits + clr_rec_bits(bit_ptr, bit_ofs, bit_len); + return 0; + } + Copy_func *get_copy_func(const Field *from) const + { + return do_field_int; + } + int save_in_field(Field *to) { return to->store(val_int(), true); } + bool memcpy_field_possible(const Field *from) const { return false; } + int store(const char *to, uint length, CHARSET_INFO *charset); + int store(double nr); + int store(longlong nr, bool unsigned_val); + int store_decimal(const my_decimal *); + double val_real(void); + longlong val_int(void); + String *val_str(String*, String *); + virtual bool str_needs_quotes() { return TRUE; } + my_decimal *val_decimal(my_decimal *); + bool val_bool() { return val_int() != 0; } + int cmp(const uchar *a, const uchar *b) + { + DBUG_ASSERT(ptr == a || ptr == b); + if (ptr == a) + return Field_bit::key_cmp(b, bytes_in_rec + MY_TEST(bit_len)); + else + return Field_bit::key_cmp(a, bytes_in_rec + MY_TEST(bit_len)) * -1; + } + int cmp_binary_offset(uint row_offset) + { return cmp_offset(row_offset); } + int cmp_max(const uchar *a, const uchar *b, uint max_length); + int key_cmp(const uchar *a, const uchar *b) + { return cmp_binary((uchar *) a, (uchar *) b); } + int key_cmp(const uchar *str, uint length); + int cmp_offset(uint row_offset); + bool update_min(Field *min_val, bool force_update) + { + longlong val= val_int(); + bool update_fl= force_update || val < min_val->val_int(); + if (update_fl) + { + min_val->set_notnull(); + min_val->store(val, FALSE); + } + return update_fl; + } + bool update_max(Field *max_val, bool force_update) + { + longlong val= val_int(); + bool update_fl= force_update || val > max_val->val_int(); + if (update_fl) + { + max_val->set_notnull(); + max_val->store(val, FALSE); + } + return update_fl; + } + void store_field_value(uchar *val, uint len) + { + store(*((longlong *)val), TRUE); + } + double pos_in_interval(Field *min, Field *max) + { + return pos_in_interval_val_real(min, max); + } + void get_image(uchar *buff, uint length, CHARSET_INFO *cs) + { get_key_image(buff, length, itRAW); } + void set_image(const uchar *buff,uint length, CHARSET_INFO *cs) + { Field_bit::store((char *) buff, length, cs); } + uint get_key_image(uchar *buff, uint length, imagetype type); + void set_key_image(const uchar *buff, uint length) + { Field_bit::store((char*) buff, length, &my_charset_bin); } + void sort_string(uchar *buff, uint length) + { get_key_image(buff, length, itRAW); } + uint32 pack_length() const { return (uint32) (field_length + 7) / 8; } + uint32 pack_length_in_rec() const { return bytes_in_rec; } + uint pack_length_from_metadata(uint field_metadata); + uint row_pack_length() const + { return (bytes_in_rec + ((bit_len > 0) ? 1 : 0)); } + bool compatible_field_size(uint metadata, Relay_log_info *rli, + uint16 mflags, int *order_var); + void sql_type(String &str) const; + virtual uchar *pack(uchar *to, const uchar *from, uint max_length); + virtual const uchar *unpack(uchar *to, const uchar *from, + const uchar *from_end, uint param_data); + virtual void set_default(); + + Field *new_key_field(MEM_ROOT *root, TABLE *new_table, + uchar *new_ptr, uint32 length, + uchar *new_null_ptr, uint new_null_bit); + void set_bit_ptr(uchar *bit_ptr_arg, uchar bit_ofs_arg) + { + bit_ptr= bit_ptr_arg; + bit_ofs= bit_ofs_arg; + } + bool eq(Field *field) + { + return (Field::eq(field) && + bit_ptr == ((Field_bit *)field)->bit_ptr && + bit_ofs == ((Field_bit *)field)->bit_ofs); + } + uint is_equal(Create_field *new_field); + void move_field_offset(my_ptrdiff_t ptr_diff) + { + Field::move_field_offset(ptr_diff); + bit_ptr= ADD_TO_PTR(bit_ptr, ptr_diff, uchar*); + } + void hash(ulong *nr, ulong *nr2); + +private: + virtual size_t do_last_null_byte() const; + int do_save_field_metadata(uchar *first_byte); +}; + + +/** + BIT field represented as chars for non-MyISAM tables. + + @todo The inheritance relationship is backwards since Field_bit is + an extended version of Field_bit_as_char and not the other way + around. Hence, we should refactor it to fix the hierarchy order. + */ +class Field_bit_as_char: public Field_bit { +public: + Field_bit_as_char(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, + uchar null_bit_arg, + enum utype unireg_check_arg, const char *field_name_arg); + enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; } + uint size_of() const { return sizeof(*this); } + int store(const char *to, uint length, CHARSET_INFO *charset); + int store(double nr) { return Field_bit::store(nr); } + int store(longlong nr, bool unsigned_val) + { return Field_bit::store(nr, unsigned_val); } + void sql_type(String &str) const; +}; + + +extern const LEX_STRING null_lex_str; + + +Field *make_field(TABLE_SHARE *share, MEM_ROOT *mem_root, + uchar *ptr, uint32 field_length, + uchar *null_pos, uchar null_bit, + uint pack_flag, enum_field_types field_type, + CHARSET_INFO *cs, + Field::geometry_type geom_type, uint srid, + Field::utype unireg_check, + TYPELIB *interval, const char *field_name); + +/* + Create field class for CREATE TABLE +*/ +class Column_definition: public Sql_alloc +{ + /** + Create "interval" from "interval_list". + @param mem_root - memory root to create the TYPELIB + instance and its values on + @param reuse_interval_list_values - determines if TYPELIB can reuse strings + from interval_list, or should always + allocate a copy on mem_root, even if + character set conversion is not needed + @retval false on success + @retval true on error (bad values, or EOM) + */ + bool create_interval_from_interval_list(MEM_ROOT *mem_root, + bool reuse_interval_list_values); + + /* + Calculate TYPELIB (set or enum) max and total lengths + + @param cs charset+collation pair of the interval + @param max_length length of the longest item + @param tot_length sum of the item lengths + + After this method call: + - ENUM uses max_length + - SET uses tot_length. + */ + void calculate_interval_lengths(uint32 *max_length, uint32 *tot_length) + { + const char **pos; + uint *len; + *max_length= *tot_length= 0; + for (pos= interval->type_names, len= interval->type_lengths; + *pos ; pos++, len++) + { + size_t length= charset->cset->numchars(charset, *pos, *pos + *len); + *tot_length+= length; + set_if_bigger(*max_length, (uint32)length); + } + } +public: + enum enum_column_versioning + { + VERSIONING_NOT_SET, + WITH_VERSIONING, + WITHOUT_VERSIONING + }; + + const char *field_name; + LEX_STRING comment; // Comment for field + Item *on_update; // ON UPDATE NOW() + enum enum_field_types sql_type; + /* + At various stages in execution this can be length of field in bytes or + max number of characters. + */ + ulonglong length; + /* + The value of `length' as set by parser: is the number of characters + for most of the types, or of bytes for BLOBs or numeric types. + */ + uint32 char_length; + uint decimals, flags, pack_length, key_length; + Field::utype unireg_check; + TYPELIB *interval; // Which interval to use + List interval_list; + CHARSET_INFO *charset; + uint32 srid; + Field::geometry_type geom_type; + engine_option_value *option_list; + + uint pack_flag; + + /* + This is additinal data provided for any computed(virtual) field. + In particular it includes a pointer to the item by which this field + can be computed from other fields. + */ + Virtual_column_info + *vcol_info, // Virtual field + *default_value, // Default value + *check_constraint; // Check constraint + + enum_column_versioning versioning; + + Column_definition(): + comment(null_lex_str), + on_update(NULL), sql_type(MYSQL_TYPE_NULL), length(0), decimals(0), + flags(0), pack_length(0), key_length(0), unireg_check(Field::NONE), + interval(0), charset(&my_charset_bin), + srid(0), geom_type(Field::GEOM_GEOMETRY), + option_list(NULL), + vcol_info(0), default_value(0), check_constraint(0), + versioning(VERSIONING_NOT_SET) + { + interval_list.empty(); + } + + Column_definition(THD *thd, Field *field, Field *orig_field); + void set_attributes(const Lex_field_type_st &type, CHARSET_INFO *cs); + void create_length_to_internal_length(void); + + /** + Prepare a SET/ENUM field. + Create "interval" from "interval_list" if needed, and adjust "length". + @param mem_root - Memory root to allocate TYPELIB and + its values on + @param reuse_interval_list_values - determines if TYPELIB can reuse value + buffers from interval_list, or should + always allocate a copy on mem_root, + even if character set conversion + is not needed + */ + bool prepare_interval_field(MEM_ROOT *mem_root, + bool reuse_interval_list_values); + + void prepare_interval_field_calc_length() + { + uint32 field_length, dummy; + if (sql_type == MYSQL_TYPE_SET) + { + calculate_interval_lengths(&dummy, &field_length); + length= field_length + (interval->count - 1); + } + else /* MYSQL_TYPE_ENUM */ + { + calculate_interval_lengths(&field_length, &dummy); + length= field_length; + } + set_if_smaller(length, MAX_FIELD_WIDTH - 1); + } + + bool prepare_blob_field(THD *thd); + + bool sp_prepare_create_field(THD *thd, MEM_ROOT *mem_root); + + bool prepare_create_field(uint *blob_columns, ulonglong table_flags); + + bool check(THD *thd); + + bool stored_in_db() const { return !vcol_info || vcol_info->stored_in_db; } + + ha_storage_media field_storage_type() const + { + return (ha_storage_media) + ((flags >> FIELD_FLAGS_STORAGE_MEDIA) & 3); + } + + column_format_type column_format() const + { + return (column_format_type) + ((flags >> FIELD_FLAGS_COLUMN_FORMAT) & 3); + } + + bool has_default_function() const + { + return unireg_check != Field::NONE; + } + + Field *make_field(TABLE_SHARE *share, MEM_ROOT *mem_root, + uchar *ptr, uchar *null_pos, uchar null_bit, + const char *field_name_arg) const + { + return ::make_field(share, mem_root, ptr, + (uint32)length, null_pos, null_bit, + pack_flag, sql_type, charset, + geom_type, srid, unireg_check, interval, + field_name_arg); + } + Field *make_field(TABLE_SHARE *share, MEM_ROOT *mem_root, + const char *field_name_arg) + { + return make_field(share, mem_root, (uchar *) 0, (uchar *) "", 0, + field_name_arg); + } + /* Return true if default is an expression that must be saved explicitely */ + bool has_default_expression(); + + bool has_default_now_unireg_check() const + { + return unireg_check == Field::TIMESTAMP_DN_FIELD + || unireg_check == Field::TIMESTAMP_DNUN_FIELD; + } +}; + + +class Create_field :public Column_definition +{ +public: + const char *change; // If done with alter table + const char *after; // Put column after this one + Field *field; // For alter table + TYPELIB *save_interval; // Temporary copy for the above + // Used only for UCS2 intervals + + /** structure with parsed options (for comparing fields in ALTER TABLE) */ + ha_field_option_struct *option_struct; + uint offset; + uint8 interval_id; // For rea_create_table + bool create_if_not_exists; // Used in ALTER TABLE IF NOT EXISTS + + Create_field(): + Column_definition(), change(0), after(0), + field(0), option_struct(NULL), + create_if_not_exists(false) + { } + Create_field(THD *thd, Field *old_field, Field *orig_field): + Column_definition(thd, old_field, orig_field), + change(old_field->field_name), after(0), + field(old_field), option_struct(old_field->option_struct), + create_if_not_exists(false) + { } + /* Used to make a clone of this object for ALTER/CREATE TABLE */ + Create_field *clone(MEM_ROOT *mem_root) const; +}; + + +/* + A class for sending info to the client +*/ + +class Send_field :public Sql_alloc { + public: + const char *db_name; + const char *table_name,*org_table_name; + const char *col_name,*org_col_name; + ulong length; + uint flags, decimals; + enum_field_types type; + Send_field() {} +}; + + +/* + A class for quick copying data to fields +*/ + +class Copy_field :public Sql_alloc { +public: + uchar *from_ptr,*to_ptr; + uchar *from_null_ptr,*to_null_ptr; + bool *null_row; + uint from_bit,to_bit; + /** + Number of bytes in the fields pointed to by 'from_ptr' and + 'to_ptr'. Usually this is the number of bytes that are copied from + 'from_ptr' to 'to_ptr'. + + For variable-length fields (VARCHAR), the first byte(s) describe + the actual length of the text. For VARCHARs with length + < 256 there is 1 length byte + >= 256 there is 2 length bytes + Thus, if from_field is VARCHAR(10), from_length (and in most cases + to_length) is 11. For VARCHAR(1024), the length is 1026. @see + Field_varstring::length_bytes + + Note that for VARCHARs, do_copy() will be do_varstring*() which + only copies the length-bytes (1 or 2) + the actual length of the + text instead of from/to_length bytes. + */ + uint from_length,to_length; + Field *from_field,*to_field; + String tmp; // For items + + Copy_field() {} + ~Copy_field() {} + void set(Field *to,Field *from,bool save); // Field to field + void set(uchar *to,Field *from); // Field to string + void (*do_copy)(Copy_field *); + void (*do_copy2)(Copy_field *); // Used to handle null values +}; + + +uint pack_length_to_packflag(uint type); +enum_field_types get_blob_type_from_length(ulong length); +uint32 calc_pack_length(enum_field_types type,uint32 length); +int set_field_to_null(Field *field); +int set_field_to_null_with_conversions(Field *field, bool no_conversions); +int convert_null_to_field_value_or_error(Field *field); +bool check_expression(Virtual_column_info *vcol, const char *name, + enum_vcol_info_type type); + +/* + The following are for the interface with the .frm file +*/ + +#define FIELDFLAG_DECIMAL 1U +#define FIELDFLAG_BINARY 1U // Shares same flag +#define FIELDFLAG_NUMBER 2U +#define FIELDFLAG_ZEROFILL 4U +#define FIELDFLAG_PACK 120U // Bits used for packing +#define FIELDFLAG_INTERVAL 256U // mangled with decimals! +#define FIELDFLAG_BITFIELD 512U // mangled with decimals! +#define FIELDFLAG_BLOB 1024U // mangled with decimals! +#define FIELDFLAG_GEOM 2048U // mangled with decimals! + +#define FIELDFLAG_TREAT_BIT_AS_CHAR 4096U /* use Field_bit_as_char */ +#define FIELDFLAG_LONG_DECIMAL 8192U +#define FIELDFLAG_WITHOUT_SYSTEM_VERSIONING 8192U +#define FIELDFLAG_NO_DEFAULT 16384U /* sql */ +#define FIELDFLAG_MAYBE_NULL 32768U // sql +#define FIELDFLAG_HEX_ESCAPE 0x10000U +#define FIELDFLAG_PACK_SHIFT 3 +#define FIELDFLAG_DEC_SHIFT 8 +#define FIELDFLAG_MAX_DEC 63U + +#define MTYP_TYPENR(type) (type & 127U) /* Remove bits from type */ + +#define f_is_dec(x) ((x) & FIELDFLAG_DECIMAL) +#define f_is_num(x) ((x) & FIELDFLAG_NUMBER) +#define f_is_zerofill(x) ((x) & FIELDFLAG_ZEROFILL) +#define f_is_packed(x) ((x) & FIELDFLAG_PACK) +#define f_packtype(x) (((x) >> FIELDFLAG_PACK_SHIFT) & 15) +#define f_decimals(x) ((uint8) (((x) >> FIELDFLAG_DEC_SHIFT) & FIELDFLAG_MAX_DEC)) +#define f_is_alpha(x) (!f_is_num(x)) +#define f_is_binary(x) ((x) & FIELDFLAG_BINARY) // 4.0- compatibility +#define f_is_enum(x) (((x) & (FIELDFLAG_INTERVAL | FIELDFLAG_NUMBER)) == FIELDFLAG_INTERVAL) +#define f_is_bitfield(x) (((x) & (FIELDFLAG_BITFIELD | FIELDFLAG_NUMBER)) == FIELDFLAG_BITFIELD) +#define f_is_blob(x) (((x) & (FIELDFLAG_BLOB | FIELDFLAG_NUMBER)) == FIELDFLAG_BLOB) +#define f_is_geom(x) (((x) & (FIELDFLAG_GEOM | FIELDFLAG_NUMBER)) == FIELDFLAG_GEOM) +#define f_settype(x) (((uint) (x)) << FIELDFLAG_PACK_SHIFT) +#define f_maybe_null(x) ((x) & FIELDFLAG_MAYBE_NULL) +#define f_no_default(x) ((x) & FIELDFLAG_NO_DEFAULT) +#define f_bit_as_char(x) ((x) & FIELDFLAG_TREAT_BIT_AS_CHAR) +#define f_is_hex_escape(x) ((x) & FIELDFLAG_HEX_ESCAPE) +#define f_without_system_versioning(x) ((x) & FIELDFLAG_WITHOUT_SYSTEM_VERSIONING) +#define f_hidden(x) ((x) & FIELDFLAG_HIDDEN) + +#endif /* FIELD_INCLUDED */ diff --git a/sql/handler.cc b/sql/handler.cc index 0b079676fc9..d5dff46a22e 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -6570,16 +6570,16 @@ static bool create_sys_trx_field(THD *thd, const char *field_name, memset(f, 0, sizeof(*f)); f->field_name= field_name; f->charset= system_charset_info; + f->flags= NOT_NULL_FLAG | HIDDEN_FLAG; if (integer_fields) { f->sql_type= MYSQL_TYPE_LONGLONG; - f->flags= UNSIGNED_FLAG | NOT_NULL_FLAG; + f->flags|= UNSIGNED_FLAG; f->length= MY_INT64_NUM_DECIMAL_DIGITS; } else { f->sql_type= MYSQL_TYPE_TIMESTAMP2; - f->flags= NOT_NULL_FLAG; f->length= 6; } diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 1af45c12331..090133b72ed 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -7568,6 +7568,14 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, if (!(item= field_iterator.create_item(thd))) DBUG_RETURN(TRUE); + if (item->type() == Item::FIELD_ITEM) + { + Item_field *f= static_cast(item); + DBUG_ASSERT(f->field); + if (f->field->flags & HIDDEN_FLAG) + continue; + } + /* cache the table for the Item_fields inserted by expanding stars */ if (item->type() == Item::FIELD_ITEM && tables->cacheable_table) ((Item_field *)item)->cached_table= tables; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index c4bc2028caf..a3e6565fa52 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -2991,6 +2991,8 @@ bool Column_definition::prepare_create_field(uint *blob_columns, pack_flag|= FIELDFLAG_NO_DEFAULT; if (flags & WITHOUT_SYSTEM_VERSIONING_FLAG) pack_flag|= FIELDFLAG_WITHOUT_SYSTEM_VERSIONING; + if (flags & HIDDEN_FLAG) + pack_flag|= FIELDFLAG_HIDDEN; DBUG_RETURN(false); } diff --git a/sql/table.cc b/sql/table.cc index 3a75fbe8e48..a23589f530a 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -2020,6 +2020,9 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, if (f_without_system_versioning(pack_flag)) reg_field->flags|= WITHOUT_SYSTEM_VERSIONING_FLAG; + if (f_hidden(pack_flag)) + reg_field->flags|= HIDDEN_FLAG; + if (reg_field->unireg_check == Field::NEXT_NUMBER) share->found_next_number_field= field_ptr; -- cgit v1.2.1 From 7d815be198e0c9a6ae47e45d91210d11d549cd7b Mon Sep 17 00:00:00 2001 From: Kosov Eugene Date: Wed, 26 Oct 2016 14:32:41 +0300 Subject: SQL: store versioning field flags in EXTRA2 --- sql/field.h | 10 +++------- sql/handler.cc | 4 ++-- sql/sql_table.cc | 4 ---- sql/sql_update.cc | 2 +- sql/table.cc | 26 +++++++++++++++++++------- sql/unireg.cc | 33 +++++++++++++++++++++++++++++++++ sql/unireg.h | 6 ++++++ 7 files changed, 64 insertions(+), 21 deletions(-) (limited to 'sql') diff --git a/sql/field.h b/sql/field.h index e50595df11a..16e53c72b11 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1424,7 +1424,7 @@ public: bool is_versioning_disabled() { - return flags & WITHOUT_SYSTEM_VERSIONING_FLAG; + return flags & VERS_OPTIMIZED_UPDATE_FLAG; } /* Mark a field as auto-generated row start column. */ @@ -1444,13 +1444,13 @@ public: /* Disable a field versioning for a versioned table. */ void disable_versioning() { - flags |= WITHOUT_SYSTEM_VERSIONING_FLAG; + flags |= VERS_OPTIMIZED_UPDATE_FLAG; } /* Inherit a field versioning status from the table. */ void inherit_versioning() { - flags &= ~WITHOUT_SYSTEM_VERSIONING_FLAG; + flags &= ~VERS_OPTIMIZED_UPDATE_FLAG; } /* @@ -4281,8 +4281,6 @@ bool check_expression(Virtual_column_info *vcol, const char *name, #define FIELDFLAG_BITFIELD 512U // mangled with decimals! #define FIELDFLAG_BLOB 1024U // mangled with decimals! #define FIELDFLAG_GEOM 2048U // mangled with decimals! -// Do not show field in SELECT *. Hope GEOM field is never hidden. -#define FIELDFLAG_HIDDEN 2048U #define FIELDFLAG_TREAT_BIT_AS_CHAR 4096U /* use Field_bit_as_char */ #define FIELDFLAG_LONG_DECIMAL 8192U @@ -4313,7 +4311,5 @@ bool check_expression(Virtual_column_info *vcol, const char *name, #define f_no_default(x) ((x) & FIELDFLAG_NO_DEFAULT) #define f_bit_as_char(x) ((x) & FIELDFLAG_TREAT_BIT_AS_CHAR) #define f_is_hex_escape(x) ((x) & FIELDFLAG_HEX_ESCAPE) -#define f_without_system_versioning(x) ((x) & FIELDFLAG_WITHOUT_SYSTEM_VERSIONING) -#define f_hidden(x) ((x) & FIELDFLAG_HIDDEN) #endif /* FIELD_INCLUDED */ diff --git a/sql/handler.cc b/sql/handler.cc index d5dff46a22e..0badaa3fa24 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -6616,10 +6616,10 @@ bool Vers_parse_info::add_versioning_info( if (f->versioning == Column_definition::VERSIONING_NOT_SET && without_system_versioning_by_default) - f->flags|= WITHOUT_SYSTEM_VERSIONING_FLAG; + f->flags|= VERS_OPTIMIZED_UPDATE_FLAG; else if (f->versioning == Column_definition::WITHOUT_VERSIONING) - f->flags|= WITHOUT_SYSTEM_VERSIONING_FLAG; + f->flags|= VERS_OPTIMIZED_UPDATE_FLAG; } // If user specified some of these he must specify the others too. Do nothing. diff --git a/sql/sql_table.cc b/sql/sql_table.cc index a3e6565fa52..b92b5f5c6c4 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -2989,10 +2989,6 @@ bool Column_definition::prepare_create_field(uint *blob_columns, pack_flag|= FIELDFLAG_MAYBE_NULL; if (flags & NO_DEFAULT_VALUE_FLAG) pack_flag|= FIELDFLAG_NO_DEFAULT; - if (flags & WITHOUT_SYSTEM_VERSIONING_FLAG) - pack_flag|= FIELDFLAG_WITHOUT_SYSTEM_VERSIONING; - if (flags & HIDDEN_FLAG) - pack_flag|= FIELDFLAG_HIDDEN; DBUG_RETURN(false); } diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 419ccc1bc94..c6e2736f4ce 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -161,7 +161,7 @@ static bool check_has_vers_fields(List &items) while (Item *item= it++) { if (Item_field *item_field= item->field_for_view_update()) - if (!(item_field->field->flags & WITHOUT_SYSTEM_VERSIONING_FLAG)) + if (!(item_field->field->flags & VERS_OPTIMIZED_UPDATE_FLAG)) return true; } return false; diff --git a/sql/table.cc b/sql/table.cc index a23589f530a..622ced5f60f 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -1197,7 +1197,9 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, uint len; uint ext_key_parts= 0; plugin_ref se_plugin= 0; - const uchar *system_period = 0; + const uchar *system_period= 0; + const uchar *extra2_field_flags= 0; + size_t extra2_field_flags_length= 0; MEM_ROOT *old_root= thd->mem_root; Virtual_column_info **table_check_constraints; @@ -1297,6 +1299,12 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, goto err; system_period = extra2; break; + case EXTRA2_FIELD_FLAGS: + if (extra2_field_flags) + goto err; + extra2_field_flags= extra2; + extra2_field_flags_length= length; + break; default: /* abort frm parsing if it's an unknown but important extra2 value */ if (type >= EXTRA2_ENGINE_IMPORTANT) @@ -1615,6 +1623,8 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, disk_buff= frm_image + pos + FRM_FORMINFO_SIZE; share->fields= uint2korr(forminfo+258); + if (extra2_field_flags && extra2_field_flags_length != share->fields) + goto err; pos= uint2korr(forminfo+260); /* Length of all screens */ n_length= uint2korr(forminfo+268); interval_count= uint2korr(forminfo+270); @@ -1987,6 +1997,14 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, reg_field->field_index= i; reg_field->comment=comment; reg_field->vcol_info= vcol_info; + if (extra2_field_flags) + { + uchar flags= *extra2_field_flags++; + if (flags & VERS_OPTIMIZED_UPDATE) + reg_field->disable_versioning(); + if (flags & HIDDEN) + reg_field->flags|= HIDDEN_FLAG; + } if (field_type == MYSQL_TYPE_BIT && !f_bit_as_char(pack_flag)) { null_bits_are_used= 1; @@ -2017,12 +2035,6 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, if (f_no_default(pack_flag)) reg_field->flags|= NO_DEFAULT_VALUE_FLAG; - if (f_without_system_versioning(pack_flag)) - reg_field->flags|= WITHOUT_SYSTEM_VERSIONING_FLAG; - - if (f_hidden(pack_flag)) - reg_field->flags|= HIDDEN_FLAG; - if (reg_field->unireg_check == Field::NEXT_NUMBER) share->found_next_number_field= field_ptr; diff --git a/sql/unireg.cc b/sql/unireg.cc index 445d3441af7..9b4ee324aa3 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -120,6 +120,16 @@ vers_get_field(HA_CREATE_INFO *create_info, List &create_fields, b return 0; } +bool has_extra2_field_flags(List &create_fields) +{ + List_iterator it(create_fields); + while (Create_field *f= it++) + { + if (f->flags & (VERS_OPTIMIZED_UPDATE_FLAG | HIDDEN_FLAG)) + return true; + } + return false; +} /** Create a frm (table definition) file @@ -258,6 +268,13 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table, extra2_size+= 1 + 1 + 2 * sizeof(uint16); } + bool has_extra2_field_flags_= has_extra2_field_flags(create_fields); + if (has_extra2_field_flags_) + { + extra2_size+= + 1 + (create_fields.elements <= 255 ? 1 : 3) + create_fields.elements; + } + key_buff_length= uint4korr(fileinfo+47); frm.length= FRM_HEADER_SIZE; // fileinfo; @@ -323,6 +340,22 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table, pos+= sizeof(uint16); } + if (has_extra2_field_flags_) + { + *pos++= EXTRA2_FIELD_FLAGS; + pos= extra2_write_len(pos, create_fields.elements); + List_iterator it(create_fields); + while (Create_field *field= it++) + { + uchar flags= 0; + if (field->flags & VERS_OPTIMIZED_UPDATE_FLAG) + flags|= VERS_OPTIMIZED_UPDATE; + if (field->flags & HIDDEN_FLAG) + flags|= HIDDEN; + *pos++= flags; + } + } + int4store(pos, filepos); // end of the extra2 segment pos+= 4; diff --git a/sql/unireg.h b/sql/unireg.h index 475945311e4..a47114054e1 100644 --- a/sql/unireg.h +++ b/sql/unireg.h @@ -173,12 +173,18 @@ enum extra2_frm_value_type { EXTRA2_DEFAULT_PART_ENGINE=1, EXTRA2_GIS=2, EXTRA2_PERIOD_FOR_SYSTEM_TIME=4, + EXTRA2_FIELD_FLAGS=8, #define EXTRA2_ENGINE_IMPORTANT 128 EXTRA2_ENGINE_TABLEOPTS=128, }; +enum extra2_field_flags { + VERS_OPTIMIZED_UPDATE=1, + HIDDEN=2, +}; + int rea_create_table(THD *thd, LEX_CUSTRING *frm, const char *path, const char *db, const char *table_name, HA_CREATE_INFO *create_info, handler *file, -- cgit v1.2.1 From 3c634602b9d692a9f02a35e48069bdecfb7fff86 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Sat, 29 Oct 2016 08:28:17 +0000 Subject: Parser: SYSTEM_TIME_SYM (fixes #67) --- sql/gen_lex_token.cc | 2 ++ sql/sql_lex.cc | 2 ++ sql/sql_yacc.yy | 11 ++++++----- 3 files changed, 10 insertions(+), 5 deletions(-) (limited to 'sql') diff --git a/sql/gen_lex_token.cc b/sql/gen_lex_token.cc index eefe9163819..264dde9b8a2 100644 --- a/sql/gen_lex_token.cc +++ b/sql/gen_lex_token.cc @@ -132,6 +132,8 @@ void compute_tokens() set_token(WITH_CUBE_SYM, "WITH CUBE"); set_token(WITH_ROLLUP_SYM, "WITH ROLLUP"); + set_token(WITH_SYSTEM_SYM, "WITH SYSTEM"); + set_token(FOR_SYSTEM_TIME_SYM, "FOR SYSTEM_TIME"); set_token(NOT2_SYM, "!"); set_token(OR2_SYM, "|"); set_token(PARAM_MARKER, "?"); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 242923de546..40ead5824fb 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1333,6 +1333,8 @@ int MYSQLlex(YYSTYPE *yylval, THD *thd) return WITH_CUBE_SYM; case ROLLUP_SYM: return WITH_ROLLUP_SYM; + case SYSTEM: + return WITH_SYSTEM_SYM; default: /* Save the token following 'WITH' diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 9d5640280e0..bbcb0630bd7 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -859,10 +859,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %parse-param { THD *thd } %lex-param { THD *thd } /* - Currently there are 106 shift/reduce conflicts. + Currently there are 103 shift/reduce conflicts. We should not introduce new conflicts any more. */ -%expect 106 +%expect 103 /* Comments for TOKENS. @@ -1093,7 +1093,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token FORCE_SYM %token FOREIGN /* SQL-2003-R */ %token FOR_SYM /* SQL-2003-R */ -%token FOR_SYSTEM_TIME_SYM /* internal */ +%token FOR_SYSTEM_TIME_SYM /* INTERNAL */ %token FORMAT_SYM %token FOUND_SYM /* SQL-2003-R */ %token FROM @@ -1575,6 +1575,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token WITHOUT /* SQL-2003-R */ %token WITH_CUBE_SYM /* INTERNAL */ %token WITH_ROLLUP_SYM /* INTERNAL */ +%token WITH_SYSTEM_SYM /* INTERNAL */ %token WORK_SYM /* SQL-2003-N */ %token WRAPPER_SYM %token WRITE_SYM /* SQL-2003-N */ @@ -5849,7 +5850,7 @@ create_table_option: Lex->create_info.used_fields|= HA_CREATE_USED_SEQUENCE; Lex->create_info.sequence= $3; } - | WITH SYSTEM VERSIONING + | WITH_SYSTEM_SYM VERSIONING { Vers_parse_info &info= Lex->vers_get_info(); info.declared_system_versioning= true; @@ -6666,7 +6667,7 @@ serial_attribute: new (thd->mem_root) engine_option_value($1, &Lex->last_field->option_list, &Lex->option_list_last); } - | WITH SYSTEM VERSIONING + | WITH_SYSTEM_SYM VERSIONING { Lex->last_field->versioning = Column_definition::WITH_VERSIONING; Lex->create_info.vers_info.has_versioned_fields= true; -- cgit v1.2.1 From 012e3e7e4e9929d7c40e9f98b277ad5456a4581c Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 1 Nov 2016 14:24:18 +0000 Subject: Comment: reminder for merging HIDDEN feature (closes #38) --- sql/sql_base.cc | 1 + 1 file changed, 1 insertion(+) (limited to 'sql') diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 090133b72ed..6d288483818 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -7568,6 +7568,7 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, if (!(item= field_iterator.create_item(thd))) DBUG_RETURN(TRUE); + /* This will be deprecated when HIDDEN feature will come to MariaDB. */ if (item->type() == Item::FIELD_ITEM) { Item_field *f= static_cast(item); -- cgit v1.2.1 From a22cbc453f6140ef0588328f3b4b72553286ad8e Mon Sep 17 00:00:00 2001 From: kevgs Date: Tue, 1 Nov 2016 19:51:44 +0300 Subject: IB: (0.4) foreign keys for versioned tables (#58) --- sql/share/errmsg-utf8.txt | 3 --- sql/sql_table.cc | 14 -------------- 2 files changed, 17 deletions(-) (limited to 'sql') diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 167be4d10fc..e215395c3c2 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7520,9 +7520,6 @@ ER_SYS_START_AND_SYS_END_SAME ER_GENERATED_FIELD_CANNOT_BE_SET_BY_USER eng "Generated field for System Versioning cannot be set by user" -ER_FOREIGN_KEY_ON_SYSTEM_VERSIONED - eng "Foreign key clause is not yet supported in conjunction with system versioning" - ER_UPDATE_INFO_WITH_SYSTEM_VERSIONING eng "Rows matched: %ld Changed: %ld Inserted: %ld Warnings: %ld" diff --git a/sql/sql_table.cc b/sql/sql_table.cc index b92b5f5c6c4..ae697b61eb1 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -4621,20 +4621,6 @@ handler *mysql_create_frm_image(THD *thd, if (create_info->versioned()) { - // FIXME: This test doesn't detect foreign key relationship on the side of - // parent table and System Time support will not work correctly for such - // table either. But this cannot be implemented without changes to innodb - // that are postponed for later time. - List_iterator_fast key_iterator(alter_info->key_list); - Key *key; - while ((key= key_iterator++)) - { - if (key->type == Key::FOREIGN_KEY) - { - my_error(ER_FOREIGN_KEY_ON_SYSTEM_VERSIONED, MYF(0)); - goto err; - } - } if(vers_prepare_keys(thd, create_info, alter_info, key_info, *key_count)) goto err; -- cgit v1.2.1 From 6d89a4a49b14b64c99083c2b8af32e735ff285fc Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Fri, 14 Oct 2016 13:25:28 +0000 Subject: Parser, SQL: (0.4) TRANSACTION support in queries Syntax extension: TIMESTAMP/TRANSACTION keyword can be used before FROM ... TO, BETWEEN ... AND. Example: SELECT * FROM t1 FOR SYSTEM_TIME TIMESTAMP FROM '1-1-1' TO NOW(); Closes #27 --- sql/share/errmsg-utf8.txt | 6 +++++ sql/sql_lex.cc | 4 +-- sql/sql_lex.h | 2 +- sql/sql_select.cc | 36 +++++++++++++++----------- sql/sql_yacc.yy | 64 ++++++++++++++++++++++++++++++++++++++++------- sql/table.h | 20 +++++++++++---- 6 files changed, 101 insertions(+), 31 deletions(-) (limited to 'sql') diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index e215395c3c2..5e3a725a895 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7537,3 +7537,9 @@ ER_SYS_END_FIELD_MUST_BE_BIGINT ER_NO_VERSIONED_FIELDS_IN_VERSIONED_TABLE eng "Every field specified unversioned in versioned table" + +ER_VERS_TRX_ID_UNSUPPORTED + eng "Engine does not support versioned TRX_ID" + +ER_VERS_RANGE_UNITS_MISMATCH + eng "Range units mismatch" diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 40ead5824fb..3375324b027 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -33,9 +33,9 @@ #include "sql_signal.h" -void LEX::parse_error() +void LEX::parse_error(uint err_number) { - thd->parse_error(); + thd->parse_error(err_number); } diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 826ebd03063..c0e28ab8fd2 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -2649,7 +2649,6 @@ struct LEX: public Query_tables_list private: Query_arena_memroot *arena_for_set_stmt; MEM_ROOT *mem_root_for_set_stmt; - void parse_error(); bool sp_block_finalize(THD *thd, const Lex_spblock_st spblock, class sp_label **splabel); bool sp_change_context(THD *thd, const sp_pcontext *ctx, bool exclusive); @@ -2663,6 +2662,7 @@ private: bool sp_for_loop_increment(THD *thd, const Lex_for_loop_st &loop); public: + void parse_error(uint err_number= ER_SYNTAX_ERROR); inline bool is_arena_for_set_stmt() {return arena_for_set_stmt != 0;} bool set_arena_for_set_stmt(Query_arena *backup); void reset_arena_for_set_stmt(Query_arena *backup); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 4e7ca4663b1..81ff9b7246a 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -668,7 +668,7 @@ setup_without_group(THD *thd, Ref_ptr_array ref_pointer_array, } static int -setup_for_system_time(THD *thd, TABLE_LIST *tables, COND **where_expr, SELECT_LEX *select_lex) +setup_for_system_time(THD *thd, TABLE_LIST *tables, COND **where_expr, SELECT_LEX *slex) { DBUG_ENTER("setup_for_system_time"); @@ -698,11 +698,11 @@ setup_for_system_time(THD *thd, TABLE_LIST *tables, COND **where_expr, SELECT_LE because they must outlive execution phase for multiple executions. */ arena= thd->activate_stmt_arena_if_needed(&backup); - if (select_lex->saved_where) + if (slex->saved_where) { DBUG_ASSERT(thd->stmt_arena->is_sp_execute()); /* 2. this copy_andor_structure() is also required by the same reason */ - *where_expr= select_lex->saved_where->copy_andor_structure(thd); + *where_expr= slex->saved_where->copy_andor_structure(thd); } else if (thd->stmt_arena->is_sp_execute()) { @@ -711,7 +711,7 @@ setup_for_system_time(THD *thd, TABLE_LIST *tables, COND **where_expr, SELECT_LE else if (*where_expr) // SP executed first time (STMT_INITIALIZED_FOR_SP) /* 1. copy_andor_structure() is required since this andor tree is modified later (and on shorter arena) */ - select_lex->saved_where= (*where_expr)->copy_andor_structure(thd); + slex->saved_where= (*where_expr)->copy_andor_structure(thd); } /* We have to save also non-versioned on_expr since we may have @@ -754,15 +754,23 @@ setup_for_system_time(THD *thd, TABLE_LIST *tables, COND **where_expr, SELECT_LE Field *fstart= table->table->vers_start_field(); Field *fend= table->table->vers_end_field(); - DBUG_ASSERT(select_lex->parent_lex); - Name_resolution_context *context= select_lex->parent_lex->current_context(); + DBUG_ASSERT(slex->parent_lex); + Name_resolution_context *context= slex->parent_lex->current_context(); DBUG_ASSERT(context); Item *row_start= new (thd->mem_root) Item_field(thd, context, fstart); Item *row_end= new (thd->mem_root) Item_field(thd, context, fend); Item *row_end2= row_end; - if (!table->table->versioned_by_sql()) + if (table->table->versioned_by_sql()) + { + if (slex->vers_conditions.unit == UNIT_TRX_ID) + { + my_error(ER_VERS_TRX_ID_UNSUPPORTED, MYF(0), table->table_name); + DBUG_RETURN(-1); + } + } + else if (slex->vers_conditions.unit == UNIT_TIMESTAMP) { DBUG_ASSERT(table->table->s && table->table->s->db_plugin); row_start= new (thd->mem_root) Item_func_vtq_ts( @@ -778,7 +786,7 @@ setup_for_system_time(THD *thd, TABLE_LIST *tables, COND **where_expr, SELECT_LE } Item *cond1= 0, *cond2= 0, *curr= 0; - switch (select_lex->vers_conditions.type) + switch (slex->vers_conditions.type) { case FOR_SYSTEM_TIME_UNSPECIFIED: if (table->table->versioned_by_sql()) @@ -796,21 +804,21 @@ setup_for_system_time(THD *thd, TABLE_LIST *tables, COND **where_expr, SELECT_LE break; case FOR_SYSTEM_TIME_AS_OF: cond1= new (thd->mem_root) Item_func_le(thd, row_start, - select_lex->vers_conditions.start); + slex->vers_conditions.start); cond2= new (thd->mem_root) Item_func_gt(thd, row_end, - select_lex->vers_conditions.start); + slex->vers_conditions.start); break; case FOR_SYSTEM_TIME_FROM_TO: cond1= new (thd->mem_root) Item_func_lt(thd, row_start, - select_lex->vers_conditions.end); + slex->vers_conditions.end); cond2= new (thd->mem_root) Item_func_ge(thd, row_end, - select_lex->vers_conditions.start); + slex->vers_conditions.start); break; case FOR_SYSTEM_TIME_BETWEEN: cond1= new (thd->mem_root) Item_func_le(thd, row_start, - select_lex->vers_conditions.end); + slex->vers_conditions.end); cond2= new (thd->mem_root) Item_func_ge(thd, row_end, - select_lex->vers_conditions.start); + slex->vers_conditions.start); break; default: DBUG_ASSERT(0); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index bbcb0630bd7..0f810d72f66 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -849,6 +849,7 @@ Virtual_column_info *add_virtual_expression(THD *thd, Item *expr) enum Window_frame::Frame_exclusion frame_exclusion; enum trigger_order_type trigger_action_order_type; DDL_options_st object_ddl_options; + enum vers_range_unit_t vers_range_unit; } %{ @@ -1968,6 +1969,8 @@ END_OF_INPUT %type opt_with_column_list +%type trans_or_timestamp + %% @@ -8673,14 +8676,25 @@ select_options: } ; +trans_or_timestamp: + TRANSACTION_SYM + { + $$ = UNIT_TRX_ID; + } + | TIMESTAMP + { + $$ = UNIT_TIMESTAMP; + } + ; + opt_for_system_time_clause: /* empty */ {} | FOR_SYSTEM_TIME_SYM AS OF_SYM - TIMESTAMP simple_expr + trans_or_timestamp simple_expr { - Lex->current_select->vers_conditions.init(FOR_SYSTEM_TIME_AS_OF, $5); + Lex->current_select->vers_conditions.init(FOR_SYSTEM_TIME_AS_OF, $4, $5); } | FOR_SYSTEM_TIME_SYM AS OF_SYM @@ -8689,23 +8703,55 @@ opt_for_system_time_clause: Item *item= new (thd->mem_root) Item_func_now_local(thd, 6); if (item == NULL) MYSQL_YYABORT; - Lex->current_select->vers_conditions.init(FOR_SYSTEM_TIME_AS_OF, item); + Lex->current_select->vers_conditions.init(FOR_SYSTEM_TIME_AS_OF, UNIT_TIMESTAMP, item); } | FOR_SYSTEM_TIME_SYM FROM - TIMESTAMP simple_expr + trans_or_timestamp + simple_expr TO_SYM - TIMESTAMP simple_expr + trans_or_timestamp + simple_expr { - Lex->current_select->vers_conditions.init(FOR_SYSTEM_TIME_FROM_TO, $4, $7); + if ($3 != $6) + { + Lex->parse_error(ER_VERS_RANGE_UNITS_MISMATCH); + MYSQL_YYABORT; + } + Lex->current_select->vers_conditions.init(FOR_SYSTEM_TIME_FROM_TO, $3, $4, $7); + } + | FOR_SYSTEM_TIME_SYM + trans_or_timestamp + FROM + simple_expr + TO_SYM + simple_expr + { + Lex->current_select->vers_conditions.init(FOR_SYSTEM_TIME_FROM_TO, $2, $4, $6); + } + | FOR_SYSTEM_TIME_SYM + BETWEEN_SYM + trans_or_timestamp + simple_expr + AND_SYM + trans_or_timestamp + simple_expr + { + if ($3 != $6) + { + Lex->parse_error(ER_VERS_RANGE_UNITS_MISMATCH); + MYSQL_YYABORT; + } + Lex->current_select->vers_conditions.init(FOR_SYSTEM_TIME_BETWEEN, $3, $4, $7); } | FOR_SYSTEM_TIME_SYM + trans_or_timestamp BETWEEN_SYM - TIMESTAMP simple_expr + simple_expr AND_SYM - TIMESTAMP simple_expr + simple_expr { - Lex->current_select->vers_conditions.init(FOR_SYSTEM_TIME_BETWEEN, $4, $7); + Lex->current_select->vers_conditions.init(FOR_SYSTEM_TIME_BETWEEN, $2, $4, $6); } ; diff --git a/sql/table.h b/sql/table.h index e2c055b14d5..df0300d498c 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1812,7 +1812,7 @@ class Item_in_subselect; 4) jtbm semi-join (jtbm_subselect != NULL) */ -enum for_system_time_type +enum vers_range_type_t { FOR_SYSTEM_TIME_UNSPECIFIED = 0, FOR_SYSTEM_TIME_AS_OF, @@ -1820,24 +1820,34 @@ enum for_system_time_type FOR_SYSTEM_TIME_BETWEEN }; +enum vers_range_unit_t +{ + UNIT_TIMESTAMP = 0, + UNIT_TRX_ID +}; + /** System versioning support. */ struct vers_select_conds_t { - enum for_system_time_type type; + vers_range_type_t type; + vers_range_unit_t unit; Item *start, *end; void empty() { type= FOR_SYSTEM_TIME_UNSPECIFIED; + unit= UNIT_TIMESTAMP; start= end= NULL; } void init( - const enum for_system_time_type t, - Item * const s, - Item * const e= NULL) + vers_range_type_t t, + vers_range_unit_t u, + Item * s, + Item * e= NULL) { type= t; + unit= u; start= s; end= e; } -- cgit v1.2.1 From 0581c018b79230b7ce5a4d9e3af928b6152577f1 Mon Sep 17 00:00:00 2001 From: Kosov Eugene Date: Mon, 7 Nov 2016 22:01:29 +0300 Subject: SQL: NULL instead of optimized fields in versioned queries --- sql/field.cc | 2 +- sql/field.h | 4 ++++ sql/item.cc | 17 +++++++++++++++++ sql/share/errmsg-utf8.txt | 3 +++ 4 files changed, 25 insertions(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/field.cc b/sql/field.cc index c22ae9e2df3..a7108924f57 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -1631,7 +1631,7 @@ Field::Field(uchar *ptr_arg,uint32 length_arg,uchar *null_ptr_arg, :ptr(ptr_arg), null_ptr(null_ptr_arg), table(0), orig_table(0), table_name(0), field_name(field_name_arg), option_list(0), option_struct(0), key_start(0), part_of_key(0), - part_of_key_not_clustered(0), part_of_sortkey(0), + part_of_key_not_clustered(0), force_null(false), part_of_sortkey(0), unireg_check(unireg_check_arg), field_length(length_arg), null_bit(null_bit_arg), is_created_from_null_item(FALSE), read_stats(NULL), collected_stats(0), vcol_info(0), check_constraint(0), diff --git a/sql/field.h b/sql/field.h index 16e53c72b11..f3c29c29cac 100644 --- a/sql/field.h +++ b/sql/field.h @@ -707,6 +707,8 @@ public: /* Field is part of the following keys */ key_map key_start, part_of_key, part_of_key_not_clustered; + bool force_null; + /* Bitmap of indexes that have records ordered by col1, ... this_field, ... @@ -1089,6 +1091,8 @@ public: virtual uint size_of() const =0; // For new field inline bool is_null(my_ptrdiff_t row_offset= 0) const { + if (force_null) + return true; /* The table may have been marked as containing only NULL values for all fields if it is a NULL-complemented row of an OUTER JOIN diff --git a/sql/item.cc b/sql/item.cc index a76152c14e2..ec4ed9ab07f 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -2759,6 +2759,21 @@ void Item_field::set_field(Field *field_par) fixed= 1; if (field->table->s->tmp_table == SYSTEM_TMP_TABLE) any_privileges= 0; + + if (field->is_versioning_disabled() && context && context->select_lex && + context->select_lex->vers_conditions.type != + FOR_SYSTEM_TIME_UNSPECIFIED && + !field->force_null) + { + DBUG_ASSERT(context->select_lex->parent_lex && + context->select_lex->parent_lex->thd); + field->force_null= true; + THD *thd= context->select_lex->parent_lex->thd; + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_NON_VERSIONED_FIELD_IN_VERSIONED_QUERY, + ER_THD(thd, ER_NON_VERSIONED_FIELD_IN_VERSIONED_QUERY), + field_name); + } } @@ -5907,6 +5922,8 @@ void Item_field::cleanup() it will be linked correctly next time by name of field and table alias. I.e. we can drop 'field'. */ + if (field) + field->force_null= false; field= 0; item_equal= NULL; null_value= FALSE; diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 5e3a725a895..e39677765a2 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7543,3 +7543,6 @@ ER_VERS_TRX_ID_UNSUPPORTED ER_VERS_RANGE_UNITS_MISMATCH eng "Range units mismatch" + +ER_NON_VERSIONED_FIELD_IN_VERSIONED_QUERY + eng "Attempt to read unversioned field '%s' in historical query" -- cgit v1.2.1 From d54d36c45e1eb14bb549dd225a28bea1d168754a Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Mon, 14 Nov 2016 06:14:28 +0000 Subject: IB, SQL: (0.4) COMMIT_ID-based ordering of transactions IB: * removed CONCURR_TRX from VTQ; * new fields in VTQ: COMMIT_ID, ISO_LEVEL. SQL: * renamed BEGIN_TS, COMMIT_TS to VTQ_BEGIN_TS, VTQ_COMMIT_TS; * new functions: VTQ_COMMIT_ID, VTQ_ISO_LEVEL, VTQ_TRX_ID, VTQ_TRX_SEES, VTQ_TRX_SEES_EQ; * versioned SELECT for IB uses VTQ_TRX_SEES, VTQ_TRX_SEES_EQ. Closes #71 --- sql/handler.h | 4 +- sql/item.h | 13 ++- sql/item_create.cc | 122 +++++++++++++++++----------- sql/item_timefunc.cc | 222 ++++++++++++++++++++++++++++++++++++++++++++++----- sql/item_timefunc.h | 101 ++++++++++++++++++++++- sql/sql_select.cc | 98 ++++++++++++++++++----- sql/vtq.h | 18 ++++- 7 files changed, 481 insertions(+), 97 deletions(-) (limited to 'sql') diff --git a/sql/handler.h b/sql/handler.h index 11b71a6f03d..964880b0b62 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -1391,7 +1391,9 @@ struct handlerton System Versioning */ bool versioned() const; - bool (*vers_get_vtq_ts)(THD* thd, MYSQL_TIME *out, ulonglong trx_id, vtq_field_t field); + bool (*vers_query_trx_id)(THD* thd, void *out, ulonglong trx_id, vtq_field_t field); + bool (*vers_query_commit_ts)(THD* thd, void *out, const MYSQL_TIME &commit_ts, vtq_field_t field, bool backwards); + bool (*vers_trx_sees)(THD *thd, bool &result, ulonglong trx_id1, ulonglong trx_id0, ulonglong commit_id1, uchar iso_level1, ulonglong commit_id0); }; diff --git a/sql/item.h b/sql/item.h index 35b76a3278e..b7381bde260 100644 --- a/sql/item.h +++ b/sql/item.h @@ -480,9 +480,20 @@ public: String_copier_for_item(THD *thd): m_thd(thd) { } }; +/* System versioning */ +class Vers_extended_item +{ +public: + virtual vtq_record_t* vtq_cached_result() + { + return NULL; + } +}; + class Item: public Value_source, - public Type_std_attributes + public Type_std_attributes, + public Vers_extended_item { void operator=(Item &); /** diff --git a/sql/item_create.cc b/sql/item_create.cc index 48055ccb11e..7e8c60591e6 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -6678,24 +6678,26 @@ Create_func_year_week::create_native(THD *thd, LEX_STRING name, } -/* System Versioning: BEGIN_TS(), COMMIT_TS() */ - -class Create_func_begin_ts : public Create_native_func +/* System Versioning: VTQ_TRX_ID(), VTQ_COMMIT_ID(), VTQ_BEGIN_TS(), VTQ_COMMIT_TS(), VTQ_ISO_LEVEL() */ +template +class Create_func_vtq : public Create_native_func { public: virtual Item *create_native(THD *thd, LEX_STRING name, List *item_list); - static Create_func_begin_ts s_singleton; + static Create_func_vtq s_singleton; protected: - Create_func_begin_ts() {} - virtual ~Create_func_begin_ts() {} + Create_func_vtq() {} + virtual ~Create_func_vtq() {} }; -Create_func_begin_ts Create_func_begin_ts::s_singleton; +template +Create_func_vtq Create_func_vtq::s_singleton; +template Item* -Create_func_begin_ts::create_native(THD *thd, LEX_STRING name, +Create_func_vtq::create_native(THD *thd, LEX_STRING name, List *item_list) { Item *func= NULL; @@ -6708,9 +6710,38 @@ Create_func_begin_ts::create_native(THD *thd, LEX_STRING name, case 1: { Item *param_1= item_list->pop(); - func= new (thd->mem_root) Item_func_vtq_ts(thd, param_1, VTQ_BEGIN_TS); + switch (VTQ_FIELD) + { + case VTQ_BEGIN_TS: + case VTQ_COMMIT_TS: + func= new (thd->mem_root) Item_func_vtq_ts(thd, param_1, VTQ_FIELD); + break; + case VTQ_TRX_ID: + case VTQ_COMMIT_ID: + case VTQ_ISO_LEVEL: + func= new (thd->mem_root) Item_func_vtq_id(thd, param_1, VTQ_FIELD); + break; + default: + DBUG_ASSERT(0); + } + break; + } + case 2: + { + Item *param_1= item_list->pop(); + Item *param_2= item_list->pop(); + switch (VTQ_FIELD) + { + case VTQ_TRX_ID: + case VTQ_COMMIT_ID: + func= new (thd->mem_root) Item_func_vtq_id(thd, param_1, param_2, VTQ_FIELD); + break; + default: + goto error; + } break; } + error: default: { my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); @@ -6719,49 +6750,45 @@ Create_func_begin_ts::create_native(THD *thd, LEX_STRING name, } return func; -} +}; -class Create_func_commit_ts : public Create_native_func +template +class Create_func_vtq_trx_sees : public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List *item_list); - - static Create_func_commit_ts s_singleton; - -protected: - Create_func_commit_ts() {} - virtual ~Create_func_commit_ts() {} -}; - -Create_func_commit_ts Create_func_commit_ts::s_singleton; + virtual Item *create_native(THD *thd, LEX_STRING name, List *item_list) + { + Item *func= NULL; + int arg_count= 0; -Item* -Create_func_commit_ts::create_native(THD *thd, LEX_STRING name, - List *item_list) -{ - Item *func= NULL; - int arg_count= 0; + if (item_list != NULL) + arg_count= item_list->elements; - if (item_list != NULL) - arg_count= item_list->elements; + switch (arg_count) { + case 2: + { + Item *param_1= item_list->pop(); + Item *param_2= item_list->pop(); + func= new (thd->mem_root) Item_func_vtq_trx_seesX(thd, param_1, param_2); + break; + } + default: + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + break; + } - switch (arg_count) { - case 1: - { - Item *param_1= item_list->pop(); - func= new (thd->mem_root) Item_func_vtq_ts(thd, param_1, VTQ_COMMIT_TS); - break; - } - default: - { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); - break; - } + return func; } - return func; -} + static Create_func_vtq_trx_sees s_singleton; + +protected: + Create_func_vtq_trx_sees() {} + virtual ~Create_func_vtq_trx_sees() {} +}; +template +Create_func_vtq_trx_sees Create_func_vtq_trx_sees::s_singleton; struct Native_func_registry @@ -6804,7 +6831,6 @@ static Native_func_registry func_array[] = { { C_STRING_WITH_LEN("ASWKT") }, GEOM_BUILDER(Create_func_as_wkt)}, { { C_STRING_WITH_LEN("ATAN") }, BUILDER(Create_func_atan)}, { { C_STRING_WITH_LEN("ATAN2") }, BUILDER(Create_func_atan)}, - { { C_STRING_WITH_LEN("BEGIN_TS") }, BUILDER(Create_func_begin_ts)}, { { C_STRING_WITH_LEN("BENCHMARK") }, BUILDER(Create_func_benchmark)}, { { C_STRING_WITH_LEN("BIN") }, BUILDER(Create_func_bin)}, { { C_STRING_WITH_LEN("BINLOG_GTID_POS") }, BUILDER(Create_func_binlog_gtid_pos)}, @@ -6822,7 +6848,6 @@ static Native_func_registry func_array[] = { { C_STRING_WITH_LEN("COLUMN_EXISTS") }, BUILDER(Create_func_dyncol_exists)}, { { C_STRING_WITH_LEN("COLUMN_LIST") }, BUILDER(Create_func_dyncol_list)}, { { C_STRING_WITH_LEN("COLUMN_JSON") }, BUILDER(Create_func_dyncol_json)}, - { { C_STRING_WITH_LEN("COMMIT_TS") }, BUILDER(Create_func_commit_ts)}, { { C_STRING_WITH_LEN("COMPRESS") }, BUILDER(Create_func_compress)}, { { C_STRING_WITH_LEN("CONCAT") }, BUILDER(Create_func_concat)}, { { C_STRING_WITH_LEN("CONCAT_WS") }, BUILDER(Create_func_concat_ws)}, @@ -7121,6 +7146,13 @@ static Native_func_registry func_array[] = { { C_STRING_WITH_LEN("UUID") }, BUILDER(Create_func_uuid)}, { { C_STRING_WITH_LEN("UUID_SHORT") }, BUILDER(Create_func_uuid_short)}, { { C_STRING_WITH_LEN("VERSION") }, BUILDER(Create_func_version)}, + { { C_STRING_WITH_LEN("VTQ_BEGIN_TS") }, BUILDER(Create_func_vtq)}, + { { C_STRING_WITH_LEN("VTQ_COMMIT_ID") }, BUILDER(Create_func_vtq)}, + { { C_STRING_WITH_LEN("VTQ_COMMIT_TS") }, BUILDER(Create_func_vtq)}, + { { C_STRING_WITH_LEN("VTQ_ISO_LEVEL") }, BUILDER(Create_func_vtq)}, + { { C_STRING_WITH_LEN("VTQ_TRX_ID") }, BUILDER(Create_func_vtq)}, + { { C_STRING_WITH_LEN("VTQ_TRX_SEES") }, BUILDER(Create_func_vtq_trx_sees)}, + { { C_STRING_WITH_LEN("VTQ_TRX_SEES_EQ") }, BUILDER(Create_func_vtq_trx_sees)}, { { C_STRING_WITH_LEN("WEEKDAY") }, BUILDER(Create_func_weekday)}, { { C_STRING_WITH_LEN("WEEKOFYEAR") }, BUILDER(Create_func_weekofyear)}, { { C_STRING_WITH_LEN("WITHIN") }, GEOM_BUILDER(Create_func_within)}, diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 3ceb87246ce..1f9d1076060 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -3278,10 +3278,9 @@ Item_func_vtq_ts::Item_func_vtq_ts( THD *thd, Item* a, vtq_field_t _vtq_field, - handlerton* _hton) : - Item_datetimefunc(thd, a), - vtq_field(_vtq_field), - hton(_hton) + handlerton* hton) : + VTQ_common(thd, a, hton), + vtq_field(_vtq_field) { decimals= 6; null_value= true; @@ -3292,33 +3291,24 @@ Item_func_vtq_ts::Item_func_vtq_ts( THD *thd, Item* a, vtq_field_t _vtq_field) : - Item_datetimefunc(thd, a), - vtq_field(_vtq_field), - hton(NULL) + VTQ_common(thd, a), + vtq_field(_vtq_field) { decimals= 6; null_value= true; DBUG_ASSERT(arg_count == 1 && args[0]); } -bool Item_func_vtq_ts::get_date(MYSQL_TIME *res, ulonglong fuzzy_date) +template +void +VTQ_common::init_hton() { - THD *thd= current_thd; // can it differ from constructor's? - DBUG_ASSERT(thd); - ulonglong trx_id= args[0]->val_uint(); - if (trx_id == ULONGLONG_MAX) - { - null_value= false; - thd->variables.time_zone->gmt_sec_to_TIME(res, TIMESTAMP_MAX_VALUE); - return false; - } - if (!hton) { - if (args[0]->type() == Item::FIELD_ITEM) + if (Item_func_X::args[0]->type() == Item::FIELD_ITEM) { Item_field *f= - static_cast(args[0]); + static_cast(Item_func_X::args[0]); DBUG_ASSERT( f->field && f->field->table && @@ -3333,11 +3323,201 @@ bool Item_func_vtq_ts::get_date(MYSQL_TIME *res, ulonglong fuzzy_date) DBUG_ASSERT(hton); } } +} + +bool +Item_func_vtq_ts::get_date(MYSQL_TIME *res, ulonglong fuzzy_date) +{ + THD *thd= current_thd; // can it differ from constructor's? + DBUG_ASSERT(thd); + ulonglong trx_id= args[0]->val_uint(); + if (trx_id == ULONGLONG_MAX) + { + null_value= false; + thd->variables.time_zone->gmt_sec_to_TIME(res, TIMESTAMP_MAX_VALUE); + return false; + } + + init_hton(); if (!hton) return true; - null_value= !hton->vers_get_vtq_ts(thd, res, trx_id, vtq_field); + null_value= !hton->vers_query_trx_id(thd, res, trx_id, vtq_field); return false; } + + +Item_func_vtq_id::Item_func_vtq_id( + THD *thd, + Item* a, + vtq_field_t _vtq_field, + bool _backwards) : + VTQ_common(thd, a), + vtq_field(_vtq_field), + backwards(_backwards) +{ + memset(&cached_result, 0, sizeof(cached_result)); + decimals= 0; + unsigned_flag= 1; + null_value= true; + DBUG_ASSERT(arg_count == 1 && args[0]); +} + +Item_func_vtq_id::Item_func_vtq_id( + THD *thd, + Item* a, + Item* b, + vtq_field_t _vtq_field) : + VTQ_common(thd, a, b), + vtq_field(_vtq_field), + backwards(false) +{ + memset(&cached_result, 0, sizeof(cached_result)); + decimals= 0; + unsigned_flag= 1; + null_value= true; + DBUG_ASSERT(arg_count == 2 && args[0] && args[1]); +} + +longlong +Item_func_vtq_id::get_by_trx_id(ulonglong trx_id) +{ + ulonglong res; + THD *thd= current_thd; // can it differ from constructor's? + DBUG_ASSERT(thd); + + if (trx_id == ULONGLONG_MAX) + { + null_value= true; + return 0; + } + + null_value= !hton->vers_query_trx_id(thd, &res, trx_id, vtq_field); + return res; +} + +longlong +Item_func_vtq_id::get_by_commit_ts(MYSQL_TIME &commit_ts, bool backwards) +{ + THD *thd= current_thd; // can it differ from constructor's? + DBUG_ASSERT(thd); + + null_value= !hton->vers_query_commit_ts(thd, &cached_result, commit_ts, VTQ_ALL, backwards); + if (null_value) + { + return 0; + } + + switch (vtq_field) + { + case VTQ_COMMIT_ID: + return cached_result.commit_id; + case VTQ_ISO_LEVEL: + return cached_result.iso_level; + case VTQ_TRX_ID: + return cached_result.trx_id; + default: + DBUG_ASSERT(0); + null_value= true; + } + + return 0; +} + +longlong +Item_func_vtq_id::val_int() +{ + init_hton(); + + if (!hton) + { + null_value= true; + return 0; + } + + if (args[0]->is_null()) + { + if (arg_count < 2 || vtq_field == VTQ_TRX_ID) + { + null_value= true; + return 0; + } + return get_by_trx_id(args[1]->val_uint()); + } + else + { + MYSQL_TIME commit_ts; + if (args[0]->get_date(&commit_ts, 0)) + { + null_value= true; + return 0; + } + if (arg_count > 1) + { + backwards= args[1]->val_bool(); + DBUG_ASSERT(arg_count == 2); + } + return get_by_commit_ts(commit_ts, backwards); + } +} + +Item_func_vtq_trx_sees::Item_func_vtq_trx_sees( + THD *thd, + Item* a, + Item* b) : + VTQ_common(thd, a, b), + accept_eq(false) +{ + null_value= true; + DBUG_ASSERT(arg_count == 2 && args[0] && args[1]); +} + +longlong +Item_func_vtq_trx_sees::val_int() +{ + THD *thd= current_thd; + DBUG_ASSERT(thd); + + init_hton(); + + if (!hton) + { + null_value= true; + return 0; + } + + ulonglong trx_id1, trx_id0; + ulonglong commit_id1= 0; + ulonglong commit_id0= 0; + uchar iso_level1= 0; + + DBUG_ASSERT(arg_count > 1); + trx_id1= args[0]->val_uint(); + trx_id0= args[1]->val_uint(); + + vtq_record_t *cached= args[0]->vtq_cached_result(); + if (cached && cached->commit_id) + { + commit_id1= cached->commit_id; + iso_level1= cached->iso_level; + } + + cached= args[1]->vtq_cached_result(); + if (cached && cached->commit_id) + { + commit_id0= cached->commit_id; + } + + if (accept_eq && trx_id1 && trx_id1 == trx_id0) + { + null_value= false; + return true; + } + + bool result= false; + null_value= !hton->vers_trx_sees(thd, result, trx_id1, trx_id0, commit_id1, iso_level1, commit_id0); + return result; +} + diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index a8281124b38..4d7935b8506 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -1288,10 +1288,28 @@ public: #include "vtq.h" -class Item_func_vtq_ts :public Item_datetimefunc +template +class VTQ_common : public Item_func_X { - vtq_field_t vtq_field; +protected: handlerton *hton; + void init_hton(); +public: + VTQ_common(THD *thd, Item* a) : + Item_func_X(thd, a), + hton(NULL) {} + VTQ_common(THD *thd, Item* a, Item* b) : + Item_func_X(thd, a, b), + hton(NULL) {} + VTQ_common(THD *thd, Item* a, handlerton* _hton) : + Item_func_X(thd, a), + hton(_hton) {} +}; + +class Item_func_vtq_ts : + public VTQ_common +{ + vtq_field_t vtq_field; public: Item_func_vtq_ts(THD *thd, Item* a, vtq_field_t _vtq_field, handlerton *hton); Item_func_vtq_ts(THD *thd, Item* a, vtq_field_t _vtq_field); @@ -1299,13 +1317,88 @@ public: { if (vtq_field == VTQ_BEGIN_TS) { - return "begin_ts"; + return "vtq_begin_ts"; } - return "commit_ts"; + return "vtq_commit_ts"; } bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy(thd, mem_root, this); } }; +class Item_func_vtq_id : + public VTQ_common +{ + vtq_field_t vtq_field; + vtq_record_t cached_result; + bool backwards; + + longlong get_by_trx_id(ulonglong trx_id); + longlong get_by_commit_ts(MYSQL_TIME &commit_ts, bool backwards); + +public: + Item_func_vtq_id(THD *thd, Item* a, vtq_field_t _vtq_field, bool _backwards= false); + Item_func_vtq_id(THD *thd, Item* a, Item* b, vtq_field_t _vtq_field); + + vtq_record_t *vtq_cached_result() { return &cached_result; } + + const char *func_name() const + { + switch (vtq_field) + { + case VTQ_TRX_ID: + return "vtq_trx_id"; + case VTQ_COMMIT_ID: + return "vtq_commit_id"; + case VTQ_ISO_LEVEL: + return "vtq_iso_level"; + default: + DBUG_ASSERT(0); + } + return NULL; + } + + void fix_length_and_dec() + { + Item_int_func::fix_length_and_dec(); + max_length= 20; + } + + longlong val_int(); + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy(thd, mem_root, this); } +}; + +class Item_func_vtq_trx_sees : + public VTQ_common +{ +protected: + bool accept_eq; + +public: + Item_func_vtq_trx_sees(THD *thd, Item* a, Item* b); + const char *func_name() const + { + return "vtq_trx_sees"; + } + longlong val_int(); + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy(thd, mem_root, this); } +}; + +class Item_func_vtq_trx_sees_eq : + public Item_func_vtq_trx_sees +{ +public: + Item_func_vtq_trx_sees_eq(THD *thd, Item* a, Item* b) : + Item_func_vtq_trx_sees(thd, a, b) + { + accept_eq= true; + } + const char *func_name() const + { + return "vtq_trx_sees_eq"; + } +}; + #endif /* ITEM_TIMEFUNC_INCLUDED */ diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 81ff9b7246a..4c2ba3caa02 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -671,6 +671,7 @@ static int setup_for_system_time(THD *thd, TABLE_LIST *tables, COND **where_expr, SELECT_LEX *slex) { DBUG_ENTER("setup_for_system_time"); +#define newx new (thd->mem_root) TABLE_LIST *table; int versioned_tables= 0; @@ -741,6 +742,8 @@ setup_for_system_time(THD *thd, TABLE_LIST *tables, COND **where_expr, SELECT_LE } } + const static bool vers_simple_select= false; + for (table= tables; table; table= table->next_local) { if (table->table && table->table->versioned()) @@ -758,8 +761,8 @@ setup_for_system_time(THD *thd, TABLE_LIST *tables, COND **where_expr, SELECT_LE Name_resolution_context *context= slex->parent_lex->current_context(); DBUG_ASSERT(context); - Item *row_start= new (thd->mem_root) Item_field(thd, context, fstart); - Item *row_end= new (thd->mem_root) Item_field(thd, context, fend); + Item *row_start= newx Item_field(thd, context, fstart); + Item *row_end= newx Item_field(thd, context, fend); Item *row_end2= row_end; if (table->table->versioned_by_sql()) @@ -770,58 +773,108 @@ setup_for_system_time(THD *thd, TABLE_LIST *tables, COND **where_expr, SELECT_LE DBUG_RETURN(-1); } } - else if (slex->vers_conditions.unit == UNIT_TIMESTAMP) + else if (vers_simple_select && slex->vers_conditions.unit == UNIT_TIMESTAMP + && slex->vers_conditions.type != FOR_SYSTEM_TIME_UNSPECIFIED) { DBUG_ASSERT(table->table->s && table->table->s->db_plugin); - row_start= new (thd->mem_root) Item_func_vtq_ts( + handlerton *hton= plugin_hton(table->table->s->db_plugin); + DBUG_ASSERT(hton); + row_start= newx Item_func_vtq_ts( thd, row_start, VTQ_COMMIT_TS, - plugin_hton(table->table->s->db_plugin)); - row_end= new (thd->mem_root) Item_func_vtq_ts( + // FIXME: is it needed to pass hton or it can be deduced from arg 'a'? + hton); + row_end= newx Item_func_vtq_ts( thd, row_end, VTQ_COMMIT_TS, - plugin_hton(table->table->s->db_plugin)); + hton); } Item *cond1= 0, *cond2= 0, *curr= 0; - switch (slex->vers_conditions.type) + if (table->table->versioned_by_sql() || vers_simple_select) { + switch (slex->vers_conditions.type) + { case FOR_SYSTEM_TIME_UNSPECIFIED: if (table->table->versioned_by_sql()) { MYSQL_TIME max_time; thd->variables.time_zone->gmt_sec_to_TIME(&max_time, TIMESTAMP_MAX_VALUE); - curr= new (thd->mem_root) Item_datetime_literal(thd, &max_time); - cond1= new (thd->mem_root) Item_func_eq(thd, row_end, curr); + curr= newx Item_datetime_literal(thd, &max_time); + cond1= newx Item_func_eq(thd, row_end, curr); } else { - curr= new (thd->mem_root) Item_int(thd, ULONGLONG_MAX); - cond1= new (thd->mem_root) Item_func_eq(thd, row_end2, curr); + curr= newx Item_int(thd, ULONGLONG_MAX); + cond1= newx Item_func_eq(thd, row_end2, curr); } break; case FOR_SYSTEM_TIME_AS_OF: - cond1= new (thd->mem_root) Item_func_le(thd, row_start, + cond1= newx Item_func_le(thd, row_start, slex->vers_conditions.start); - cond2= new (thd->mem_root) Item_func_gt(thd, row_end, + cond2= newx Item_func_gt(thd, row_end, slex->vers_conditions.start); break; case FOR_SYSTEM_TIME_FROM_TO: - cond1= new (thd->mem_root) Item_func_lt(thd, row_start, - slex->vers_conditions.end); - cond2= new (thd->mem_root) Item_func_ge(thd, row_end, - slex->vers_conditions.start); + cond1= newx Item_func_lt(thd, row_start, + slex->vers_conditions.end); + cond2= newx Item_func_ge(thd, row_end, + slex->vers_conditions.start); + break; + case FOR_SYSTEM_TIME_BETWEEN: + cond1= newx Item_func_le(thd, row_start, + slex->vers_conditions.end); + cond2= newx Item_func_ge(thd, row_end, + slex->vers_conditions.start); + break; + default: + DBUG_ASSERT(0); + } + } + else + { + DBUG_ASSERT(table->table->s && table->table->s->db_plugin); + handlerton *hton= plugin_hton(table->table->s->db_plugin); + DBUG_ASSERT(hton); + + Item *trx_id0, *trx_id1; + + switch (slex->vers_conditions.type) + { + case FOR_SYSTEM_TIME_UNSPECIFIED: + curr= newx Item_int(thd, ULONGLONG_MAX); + cond1= newx Item_func_eq(thd, row_end2, curr); break; + case FOR_SYSTEM_TIME_AS_OF: + trx_id0= slex->vers_conditions.unit == UNIT_TIMESTAMP ? + newx Item_func_vtq_id(thd, slex->vers_conditions.start, VTQ_TRX_ID) : + slex->vers_conditions.start; + cond1= newx Item_func_vtq_trx_sees_eq(thd, trx_id0, row_start); + cond2= newx Item_func_vtq_trx_sees(thd, row_end, trx_id0); + break; + case FOR_SYSTEM_TIME_FROM_TO: case FOR_SYSTEM_TIME_BETWEEN: - cond1= new (thd->mem_root) Item_func_le(thd, row_start, - slex->vers_conditions.end); - cond2= new (thd->mem_root) Item_func_ge(thd, row_end, - slex->vers_conditions.start); + if (slex->vers_conditions.unit == UNIT_TIMESTAMP) + { + trx_id0= newx Item_func_vtq_id(thd, slex->vers_conditions.start, VTQ_TRX_ID, true); + trx_id1= newx Item_func_vtq_id(thd, slex->vers_conditions.end, VTQ_TRX_ID, false); + } + else + { + trx_id0= slex->vers_conditions.start; + trx_id1= slex->vers_conditions.end; + } + + cond1= slex->vers_conditions.type == FOR_SYSTEM_TIME_FROM_TO ? + newx Item_func_vtq_trx_sees(thd, trx_id1, row_start) : + newx Item_func_vtq_trx_sees_eq(thd, trx_id1, row_start); + cond2= newx Item_func_vtq_trx_sees_eq(thd, row_end, trx_id0); break; default: DBUG_ASSERT(0); + } } if (cond1) @@ -848,6 +901,7 @@ setup_for_system_time(THD *thd, TABLE_LIST *tables, COND **where_expr, SELECT_LE } DBUG_RETURN(0); +#undef newx } /***************************************************************************** diff --git a/sql/vtq.h b/sql/vtq.h index 3a235352853..68c01990b04 100755 --- a/sql/vtq.h +++ b/sql/vtq.h @@ -17,9 +17,21 @@ enum vtq_field_t { - VTQ_BEGIN_TS = 0, - VTQ_COMMIT_TS + VTQ_ALL = 0, + VTQ_TRX_ID, + VTQ_COMMIT_ID, + VTQ_BEGIN_TS, + VTQ_COMMIT_TS, + VTQ_ISO_LEVEL }; -#endif /* VTQ_INCLUDED */ +struct vtq_record_t +{ + ulonglong trx_id; + ulonglong commit_id; + timeval begin_ts; + timeval commit_ts; + uchar iso_level; +}; +#endif /* VTQ_INCLUDED */ -- cgit v1.2.1 From 303d72a0f411fa8094d69ef6300966c5d13bd595 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Sat, 19 Nov 2016 12:29:07 +0000 Subject: SQL: redundant error codes reduced Replaced codes: ER_NO_VERSIONED_FIELDS_IN_VERSIONED_TABLE ER_MISSING_WITH_SYSTEM_VERSIONING ER_SYS_START_NOT_SPECIFIED ER_SYS_END_NOT_SPECIFIED ER_MISSING_PERIOD_FOR_SYSTEM_TIME ER_PERIOD_FOR_SYSTEM_TIME_CONTAINS_WRONG_START_COLUMN ER_PERIOD_FOR_SYSTEM_TIME_CONTAINS_WRONG_END_COLUMN ER_SYS_START_AND_SYS_END_SAME ER_SYS_START_MORE_THAN_ONCE ER_SYS_END_MORE_THAN_ONCE with: ER_VERS_WRONG_PARAMS ER_VERS_FIELD_WRONG_TYPE --- sql/handler.cc | 75 ++++++++++++++++++++++------------------------- sql/handler.h | 6 ++-- sql/share/errmsg-utf8.txt | 44 +++------------------------ sql/sql_parse.cc | 7 ++++- sql/sql_table.cc | 17 ----------- sql/sql_yacc.yy | 14 +++++---- sql/table.cc | 8 ++--- 7 files changed, 62 insertions(+), 109 deletions(-) (limited to 'sql') diff --git a/sql/handler.cc b/sql/handler.cc index 0badaa3fa24..c62dcb067be 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -6593,15 +6593,11 @@ static bool create_sys_trx_field(THD *thd, const char *field_name, return false; } -bool Vers_parse_info::add_versioning_info( +bool Vers_parse_info::fix_implicit( THD *thd, Alter_info *alter_info, bool integer_fields) { - if (!declared_system_versioning && !has_versioned_fields) - return false; - - bool without_system_versioning_by_default= !declared_system_versioning; List_iterator it(alter_info->create_list); while (Create_field *f= it++) { @@ -6615,11 +6611,11 @@ bool Vers_parse_info::add_versioning_info( continue; if (f->versioning == Column_definition::VERSIONING_NOT_SET && - without_system_versioning_by_default) - f->flags|= VERS_OPTIMIZED_UPDATE_FLAG; - - else if (f->versioning == Column_definition::WITHOUT_VERSIONING) + !declared_system_versioning || + f->versioning == Column_definition::WITHOUT_VERSIONING) + { f->flags|= VERS_OPTIMIZED_UPDATE_FLAG; + } } // If user specified some of these he must specify the others too. Do nothing. @@ -6637,7 +6633,11 @@ bool Vers_parse_info::add_versioning_info( "sys_trx_end"); } -bool Vers_parse_info::check(THD *thd, Alter_info *alter_info, bool integer_fields) +bool Vers_parse_info::check_and_fix_implicit( + THD *thd, + Alter_info *alter_info, + bool integer_fields, + const char* table_name) { if (!( has_versioned_fields || @@ -6651,10 +6651,14 @@ bool Vers_parse_info::check(THD *thd, Alter_info *alter_info, bool integer_field return false; } - if (add_versioning_info(thd, alter_info, integer_fields)) + if (!declared_system_versioning && !has_versioned_fields) + { + my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, "'WITH SYSTEM VERSIONING' missing"); return true; + } - bool r= false; + if (fix_implicit(thd, alter_info, integer_fields)) + return true; { int not_set= 0; @@ -6685,51 +6689,42 @@ bool Vers_parse_info::check(THD *thd, Alter_info *alter_info, bool integer_field if ((table_with_system_versioning && not_set == 0 && with == 0) || (!table_with_system_versioning && with == 0)) { - r= true; - my_error(ER_NO_VERSIONED_FIELDS_IN_VERSIONED_TABLE, MYF(0)); + my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, "versioned fields missing"); + return true; } } - if (!declared_system_versioning && !has_versioned_fields) - { - r= true; - my_error(ER_MISSING_WITH_SYSTEM_VERSIONING, MYF(0)); - } - if (!generated_as_row.start) { - r= true; - my_error(ER_SYS_START_NOT_SPECIFIED, MYF(0)); + my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, "'GENERATED AS ROW START' column missing"); + return true; } if (!generated_as_row.end) { - r= true; - my_error(ER_SYS_END_NOT_SPECIFIED, MYF(0)); + my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, "'GENERATED AS ROW END' column missing"); + return true; } if (!period_for_system_time.start || !period_for_system_time.end) { - r= true; - my_error(ER_MISSING_PERIOD_FOR_SYSTEM_TIME, MYF(0)); + my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, "'PERIOD FOR SYSTEM_TIME' missing"); + return true; } - if (!r) + if (my_strcasecmp(system_charset_info, generated_as_row.start->c_ptr(), + period_for_system_time.start->c_ptr())) { - if (my_strcasecmp(system_charset_info, generated_as_row.start->c_ptr(), - period_for_system_time.start->c_ptr())) - { - r= true; - my_error(ER_PERIOD_FOR_SYSTEM_TIME_CONTAINS_WRONG_START_COLUMN, MYF(0)); - } + my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, "'PERIOD FOR SYSTEM_TIME' and 'GENERATED AS ROW START' mismatch"); + return true; + } - if (my_strcasecmp(system_charset_info, generated_as_row.end->c_ptr(), - period_for_system_time.end->c_ptr())) - { - r= true; - my_error(ER_PERIOD_FOR_SYSTEM_TIME_CONTAINS_WRONG_END_COLUMN, MYF(0)); - } + if (my_strcasecmp(system_charset_info, generated_as_row.end->c_ptr(), + period_for_system_time.end->c_ptr())) + { + my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, "'PERIOD FOR SYSTEM_TIME' and 'GENERATED AS ROW END' mismatch"); + return true; } - return r; // false means no error + return false; } diff --git a/sql/handler.h b/sql/handler.h index 964880b0b62..394d6276e4f 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -1697,8 +1697,10 @@ struct Vers_parse_info period_for_system_time.end = end; } - bool add_versioning_info(THD *thd, Alter_info *alter_info, bool integer_fields); - bool check(THD *thd, Alter_info *alter_info, bool integer_fields); +private: + bool fix_implicit(THD *thd, Alter_info *alter_info, bool integer_fields); +public: + bool check_and_fix_implicit(THD *thd, Alter_info *alter_info, bool integer_fields, const char* table_name); /** User has added 'WITH SYSTEM VERSIONING' to table definition */ bool declared_system_versioning : 1; diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index e39677765a2..c8615943ff2 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7487,56 +7487,20 @@ ER_UNKNOWN_VIEW 42S02 # MariaDB error numbers related to System Versioning -ER_SYS_START_NOT_SPECIFIED - eng "'Generated as row start' not specified" - -ER_SYS_END_NOT_SPECIFIED - eng "'Generated as row end' not specified" - -ER_SYS_START_MORE_THAN_ONCE - eng "'Generated as row start' specified more than once" - -ER_SYS_END_MORE_THAN_ONCE - eng "Generated as row end specified more than once" - -ER_MISSING_PERIOD_FOR_SYSTEM_TIME - eng "'Period for system time' is missing" - ER_TABLE_DOESNT_SUPPORT_SYSTEM_VERSIONING eng "Table '%s' doesn't support system versioning" -ER_MISSING_WITH_SYSTEM_VERSIONING - eng "'With system versioning' is missing" - -ER_PERIOD_FOR_SYSTEM_TIME_CONTAINS_WRONG_START_COLUMN - eng "First column in 'period for system time' must be equal to 'generated as row start' column" - -ER_PERIOD_FOR_SYSTEM_TIME_CONTAINS_WRONG_END_COLUMN - eng "Second column in 'period for system time' must be equal to 'generated as row end' column" - -ER_SYS_START_AND_SYS_END_SAME - eng "'Period for system_time' must contain two different columns" - ER_GENERATED_FIELD_CANNOT_BE_SET_BY_USER eng "Generated field for System Versioning cannot be set by user" ER_UPDATE_INFO_WITH_SYSTEM_VERSIONING eng "Rows matched: %ld Changed: %ld Inserted: %ld Warnings: %ld" -ER_SYS_START_FIELD_MUST_BE_TIMESTAMP - eng "System start field must be of type TIMESTAMP" - -ER_SYS_END_FIELD_MUST_BE_TIMESTAMP - eng "System end field must be of type TIMESTAMP" - -ER_SYS_START_FIELD_MUST_BE_BIGINT - eng "System start field must be of type BIGINT UNSIGNED" - -ER_SYS_END_FIELD_MUST_BE_BIGINT - eng "System end field must be of type BIGINT UNSIGNED" +ER_VERS_FIELD_WRONG_TYPE + eng "%`s must be of type %`s for versioned table %`s" -ER_NO_VERSIONED_FIELDS_IN_VERSIONED_TABLE - eng "Every field specified unversioned in versioned table" +ER_VERS_WRONG_PARAMS + eng "Wrong parameters for versioned table %`s: %s" ER_VERS_TRX_ID_UNSUPPORTED eng "Engine does not support versioned TRX_ID" diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 611e1c1a021..75e608a0377 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3877,8 +3877,13 @@ mysql_execute_command(THD *thd) create_info.use_default_db_type(thd); DBUG_ASSERT(create_info.db_type); - if (create_info.vers_info.check(thd, &alter_info, create_info.db_type->versioned())) + if (create_info.vers_info.check_and_fix_implicit(thd, + &alter_info, + create_info.db_type->flags & HTON_SUPPORTS_SYS_VERSIONING, + create_table->table_name)) + { goto end_with_restore_list; + } /* If we are using SET CHARSET without DEFAULT, add an implicit diff --git a/sql/sql_table.cc b/sql/sql_table.cc index ae697b61eb1..288c36ee7ab 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -3470,23 +3470,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, */ if (sql_field->stored_in_db()) record_offset+= sql_field->pack_length; - - if (create_info->versioned()) - { - const bool is_generated_as_row_start = - !my_strcasecmp(system_charset_info, - create_info->vers_info.generated_as_row.start->c_ptr(), - sql_field->field_name); - const bool is_generated_as_row_end = - !my_strcasecmp(system_charset_info, - create_info->vers_info.generated_as_row.end->c_ptr(), - sql_field->field_name); - if (is_generated_as_row_start && is_generated_as_row_end) - { - my_error(ER_SYS_START_AND_SYS_END_SAME, MYF(0), sql_field->field_name); - DBUG_RETURN(TRUE); - } - } } /* Update virtual fields' offset*/ it.rewind(); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 0f810d72f66..6b55582be0b 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -6065,7 +6065,9 @@ period_for_system_time: Vers_parse_info &info= Lex->vers_get_info(); if (!my_strcasecmp(system_charset_info, $4->c_ptr(), $6->c_ptr())) { - my_error(ER_SYS_START_AND_SYS_END_SAME, MYF(0), $4->c_ptr()); + my_error(ER_VERS_WRONG_PARAMS, MYF(0), + Lex->create_last_non_select_table->table_name, + "'PERIOD FOR SYSTEM_TIME' columns must be different"); MYSQL_YYABORT; } info.set_period_for_system_time($4, $6); @@ -6174,17 +6176,19 @@ field_def: if (!field_name) MYSQL_YYABORT; + const char *table_name= Lex->create_last_non_select_table->table_name; + String **p= NULL; - int err_nr= 0; + const char* err; switch ($4) { case 1: p= &info.generated_as_row.start; - err_nr= ER_SYS_START_MORE_THAN_ONCE; + err= "multiple 'GENERATED ALWAYS AS ROW START'"; break; case 0: p= &info.generated_as_row.end; - err_nr= ER_SYS_END_MORE_THAN_ONCE; + err= "multiple 'GENERATED ALWAYS AS ROW END'"; break; default: /* Not Reachable */ @@ -6193,7 +6197,7 @@ field_def: } if (*p) { - my_error(err_nr, MYF(0), field_name->c_ptr()); + my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, err); MYSQL_YYABORT; } *p= field_name; diff --git a/sql/table.cc b/sql/table.cc index 622ced5f60f..1bda4607dcf 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -2566,13 +2566,13 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, if (vers_start_field()->type() != MYSQL_TYPE_LONGLONG || !(vers_start_field()->flags & UNSIGNED_FLAG)) { - my_error(ER_SYS_START_FIELD_MUST_BE_BIGINT, MYF(0), share->table_name); + my_error(ER_VERS_FIELD_WRONG_TYPE, MYF(0), vers_start_field()->field_name, "BIGINT UNSIGNED", share->table_name); goto err; } if (vers_end_field()->type() != MYSQL_TYPE_LONGLONG || !(vers_end_field()->flags & UNSIGNED_FLAG)) { - my_error(ER_SYS_END_FIELD_MUST_BE_BIGINT, MYF(0), share->table_name); + my_error(ER_VERS_FIELD_WRONG_TYPE, MYF(0), vers_end_field()->field_name, "BIGINT UNSIGNED", share->table_name); goto err; } } @@ -2580,12 +2580,12 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, { if (vers_start_field()->type() != MYSQL_TYPE_TIMESTAMP) { - my_error(ER_SYS_START_FIELD_MUST_BE_TIMESTAMP, MYF(0), share->table_name); + my_error(ER_VERS_FIELD_WRONG_TYPE, MYF(0), vers_start_field()->field_name, "TIMESTAMP", share->table_name); goto err; } if (vers_end_field()->type() != MYSQL_TYPE_TIMESTAMP) { - my_error(ER_SYS_END_FIELD_MUST_BE_TIMESTAMP, MYF(0), share->table_name); + my_error(ER_VERS_FIELD_WRONG_TYPE, MYF(0), vers_end_field()->field_name, "TIMESTAMP", share->table_name); goto err; } } // if (db_type()->versioned()) -- cgit v1.2.1 From a1c36f2e15170ca6b14db8848b95427df03ab3b8 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Sun, 20 Nov 2016 18:17:28 +0000 Subject: SQL: default NULL for sys fields + misc fixes * sys fields are NULL by default (with exceptions, see comment about NOT_NULL_FLAG in #77); * error codes renamed, messages cleared out; * SHOW CREATE TABLE fixed; * set_max() fix; * redundant flag setters/getters removed; * flags are set in sql_yacc.yy, redundant copy_info_about_generated_fields() eliminated. --- sql/field.cc | 2 ++ sql/field.h | 49 ++--------------------------------------------- sql/handler.cc | 17 +++++++++------- sql/item.cc | 2 +- sql/share/errmsg-utf8.txt | 8 ++++---- sql/sql_base.cc | 8 ++++---- sql/sql_insert.cc | 2 +- sql/sql_show.cc | 14 +++++++------- sql/sql_table.cc | 35 +-------------------------------- sql/sql_yacc.yy | 9 ++++++--- sql/table.cc | 8 ++++---- 11 files changed, 42 insertions(+), 112 deletions(-) (limited to 'sql') diff --git a/sql/field.cc b/sql/field.cc index a7108924f57..295b22ad8ec 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -4359,6 +4359,7 @@ void Field_longlong::sql_type(String &res) const bool Field_longlong::set_max() { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + set_notnull(); int8store(ptr, unsigned_flag ? ULONGLONG_MAX : LONGLONG_MAX); return FALSE; } @@ -5448,6 +5449,7 @@ bool Field_timestampf::set_max() DBUG_ENTER("Field_timestampf::set_max"); ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + set_notnull(); mi_int4store(ptr, TIMESTAMP_MAX_VALUE); memset(ptr + 4, 0x0, value_length() - 4); diff --git a/sql/field.h b/sql/field.h index f3c29c29cac..a479ec569ff 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1407,54 +1407,9 @@ public: FIELD_FLAGS_COLUMN_FORMAT; } - /* - System versioning support. - */ - - bool is_generated() - { - return flags & (GENERATED_ROW_START_FLAG | GENERATED_ROW_END_FLAG); - } - - bool is_generated_row_start() - { - return flags & GENERATED_ROW_START_FLAG; - } - - bool is_generated_row_end() - { - return flags & GENERATED_ROW_END_FLAG; - } - - bool is_versioning_disabled() - { - return flags & VERS_OPTIMIZED_UPDATE_FLAG; - } - - /* Mark a field as auto-generated row start column. */ - void set_generated_row_start() - { - //DBUG_ASSERT((flags & GENERATED_ROW_END_FLAG) == 0); - flags |= GENERATED_ROW_START_FLAG; - } - - /* Mark a field as auto-generated row start column. */ - void set_generated_row_end() - { - //DBUG_ASSERT((flags & GENERATED_ROW_START_FLAG) == 0); - flags |= GENERATED_ROW_END_FLAG; - } - - /* Disable a field versioning for a versioned table. */ - void disable_versioning() - { - flags |= VERS_OPTIMIZED_UPDATE_FLAG; - } - - /* Inherit a field versioning status from the table. */ - void inherit_versioning() + bool vers_sys_field() { - flags &= ~VERS_OPTIMIZED_UPDATE_FLAG; + return flags & (VERS_SYS_START_FLAG | VERS_SYS_END_FLAG); } /* diff --git a/sql/handler.cc b/sql/handler.cc index c62dcb067be..14b751c0628 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -6561,6 +6561,7 @@ static bool create_string(MEM_ROOT *mem_root, String **s, const char *value) static bool create_sys_trx_field(THD *thd, const char *field_name, Alter_info *alter_info, String **s, + int flags, bool integer_fields) { Create_field *f= new (thd->mem_root) Create_field(); @@ -6570,12 +6571,12 @@ static bool create_sys_trx_field(THD *thd, const char *field_name, memset(f, 0, sizeof(*f)); f->field_name= field_name; f->charset= system_charset_info; - f->flags= NOT_NULL_FLAG | HIDDEN_FLAG; + f->flags= flags | HIDDEN_FLAG; if (integer_fields) { f->sql_type= MYSQL_TYPE_LONGLONG; f->flags|= UNSIGNED_FLAG; - f->length= MY_INT64_NUM_DECIMAL_DIGITS; + f->length= MY_INT64_NUM_DECIMAL_DIGITS - 1; } else { @@ -6610,8 +6611,8 @@ bool Vers_parse_info::fix_implicit( !strncmp(name, generated_as_row.end->c_ptr(), len)) continue; - if (f->versioning == Column_definition::VERSIONING_NOT_SET && - !declared_system_versioning || + if ((f->versioning == Column_definition::VERSIONING_NOT_SET && + !declared_system_versioning) || f->versioning == Column_definition::WITHOUT_VERSIONING) { f->flags|= VERS_OPTIMIZED_UPDATE_FLAG; @@ -6623,10 +6624,12 @@ bool Vers_parse_info::fix_implicit( period_for_system_time.start || period_for_system_time.end) return false; - return create_sys_trx_field(thd, "sys_trx_start", alter_info, - &generated_as_row.start, integer_fields) || + return create_sys_trx_field(thd,"sys_trx_start", alter_info, + &generated_as_row.start, VERS_SYS_START_FLAG, + integer_fields) || create_sys_trx_field(thd, "sys_trx_end", alter_info, - &generated_as_row.end, integer_fields) || + &generated_as_row.end, VERS_SYS_END_FLAG, + integer_fields) || create_string(thd->mem_root, &period_for_system_time.start, "sys_trx_start") || create_string(thd->mem_root, &period_for_system_time.end, diff --git a/sql/item.cc b/sql/item.cc index ec4ed9ab07f..e36fadea957 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -2760,7 +2760,7 @@ void Item_field::set_field(Field *field_par) if (field->table->s->tmp_table == SYSTEM_TMP_TABLE) any_privileges= 0; - if (field->is_versioning_disabled() && context && context->select_lex && + if (field->flags & VERS_OPTIMIZED_UPDATE_FLAG && context && context->select_lex && context->select_lex->vers_conditions.type != FOR_SYSTEM_TIME_UNSPECIFIED && !field->force_null) diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index c8615943ff2..19bda09cc39 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7488,10 +7488,10 @@ ER_UNKNOWN_VIEW 42S02 # MariaDB error numbers related to System Versioning ER_TABLE_DOESNT_SUPPORT_SYSTEM_VERSIONING - eng "Table '%s' doesn't support system versioning" + eng "Table %`s doesn't support system versioning" -ER_GENERATED_FIELD_CANNOT_BE_SET_BY_USER - eng "Generated field for System Versioning cannot be set by user" +ER_VERS_READONLY_FIELD + eng "System field %`s is read-only" ER_UPDATE_INFO_WITH_SYSTEM_VERSIONING eng "Rows matched: %ld Changed: %ld Inserted: %ld Warnings: %ld" @@ -7509,4 +7509,4 @@ ER_VERS_RANGE_UNITS_MISMATCH eng "Range units mismatch" ER_NON_VERSIONED_FIELD_IN_VERSIONED_QUERY - eng "Attempt to read unversioned field '%s' in historical query" + eng "Attempt to read unversioned field %`s in historical query" diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 6d288483818..eb8bcf2148f 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -7971,10 +7971,10 @@ fill_record(THD *thd, TABLE *table_arg, List &fields, List &values, ER_THD(thd, ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN), rfield->field_name, table->s->table_name.str); } - if (table->versioned() && rfield->is_generated() && + if (table->versioned() && rfield->vers_sys_field() && !ignore_errors) { - my_error(ER_GENERATED_FIELD_CANNOT_BE_SET_BY_USER, MYF(0)); + my_error(ER_VERS_READONLY_FIELD, MYF(0), rfield->field_name); goto err; } @@ -8225,10 +8225,10 @@ fill_record(THD *thd, TABLE *table, Field **ptr, List &values, } } - if (table->versioned() && field->is_generated() && + if (table->versioned() && field->vers_sys_field() && !ignore_errors) { - my_error(ER_GENERATED_FIELD_CANNOT_BE_SET_BY_USER, MYF(0)); + my_error(ER_VERS_READONLY_FIELD, MYF(0), field->field_name); goto err; } diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 4a588ea0cc2..50b98678271 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -2045,8 +2045,8 @@ int check_that_all_fields_are_given_values(THD *thd, TABLE *entry, TABLE_LIST *t for (Field **field=entry->field ; *field ; field++) { if (!bitmap_is_set(write_set, (*field)->field_index) && + !(*field)->vers_sys_field() && has_no_default_value(thd, *field, table_list) && - !((*field)->flags & (GENERATED_ROW_START_FLAG | GENERATED_ROW_END_FLAG)) && ((*field)->real_type() != MYSQL_TYPE_ENUM)) err=1; } diff --git a/sql/sql_show.cc b/sql/sql_show.cc index cc7f2b0a017..5a640ccb613 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1666,7 +1666,7 @@ static bool get_field_default_value(THD *thd, Field *field, String *def_value, has_default= (field->default_value || (!(field->flags & NO_DEFAULT_VALUE_FLAG) && - !field->is_generated() && + !field->vers_sys_field() && field->unireg_check != Field::NEXT_NUMBER)); def_value->length(0); @@ -2109,7 +2109,7 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, { if (flags & NOT_NULL_FLAG) packet->append(STRING_WITH_LEN(" NOT NULL")); - else if (field->type() == MYSQL_TYPE_TIMESTAMP) + else if (field->type() == MYSQL_TYPE_TIMESTAMP && !field->vers_sys_field()) { /* TIMESTAMP field require explicit NULL flag, because unlike @@ -2124,16 +2124,16 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, packet->append(STRING_WITH_LEN(" DEFAULT ")); packet->append(def_value.ptr(), def_value.length(), system_charset_info); } - else if (field->is_generated_row_start()) + else if (field->flags & VERS_SYS_START_FLAG) { - packet->append(STRING_WITH_LEN(" GENERATED AS ROW START")); + packet->append(STRING_WITH_LEN(" GENERATED ALWAYS AS ROW START")); } - else if (field->is_generated_row_end()) + else if (field->flags & VERS_SYS_END_FLAG) { - packet->append(STRING_WITH_LEN(" GENERATED AS ROW END")); + packet->append(STRING_WITH_LEN(" GENERATED ALWAYS AS ROW END")); } - if (field->is_versioning_disabled()) + if (field->flags & VERS_OPTIMIZED_UPDATE_FLAG) { packet->append(STRING_WITH_LEN(" WITHOUT SYSTEM VERSIONING")); } diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 288c36ee7ab..68504ffddba 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -3051,7 +3051,7 @@ void promote_first_timestamp_column(List *column_definitions) column_definition->default_value == NULL && // no constant default, column_definition->unireg_check == Field::NONE && // no function default column_definition->vcol_info == NULL && - !(column_definition->flags & (GENERATED_ROW_START_FLAG | GENERATED_ROW_END_FLAG))) // column isn't generated + !(column_definition->flags & (VERS_SYS_START_FLAG | VERS_SYS_END_FLAG))) // column isn't generated { DBUG_PRINT("info", ("First TIMESTAMP column '%s' was promoted to " "DEFAULT CURRENT_TIMESTAMP ON UPDATE " @@ -3146,37 +3146,6 @@ static void check_duplicate_key(THD *thd, Key *key, KEY *key_info, } } -static void -copy_info_about_generated_fields(Alter_info *dst_alter_info, - HA_CREATE_INFO *src_create_info) -{ - if (!src_create_info->versioned()) - return; - - const char *row_start_field = src_create_info->vers_info.generated_as_row.start->c_ptr(); - DBUG_ASSERT(row_start_field); - const char *row_end_field = src_create_info->vers_info.generated_as_row.end->c_ptr(); - DBUG_ASSERT(row_end_field); - - List_iterator it(dst_alter_info->create_list); - Create_field *column_definition = NULL; - while ( (column_definition = it++) ) - { - if (!my_strcasecmp(system_charset_info, - row_start_field, - column_definition->field_name)) - { - column_definition->flags |= GENERATED_ROW_START_FLAG; - } - else if (!my_strcasecmp(system_charset_info, - row_end_field, - column_definition->field_name)) - { - column_definition->flags |= GENERATED_ROW_END_FLAG; - } - } -} - /* Preparation for table creation @@ -5039,8 +5008,6 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table, else create_table_mode= C_ASSISTED_DISCOVERY; - copy_info_about_generated_fields(alter_info, create_info); - if (!opt_explicit_defaults_for_timestamp) promote_first_timestamp_column(&alter_info->create_list); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 6b55582be0b..fb2bbb9ad64 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -6170,13 +6170,14 @@ field_def: vcol_opt_specifier vcol_opt_attribute | opt_generated_always AS ROW_SYM start_or_end { - Vers_parse_info &info= Lex->vers_get_info(); + LEX *lex= Lex; + Vers_parse_info &info= lex->vers_get_info(); String *field_name= new (thd->mem_root) - String((const char*)Lex->last_field->field_name, system_charset_info); + String((const char*)lex->last_field->field_name, system_charset_info); if (!field_name) MYSQL_YYABORT; - const char *table_name= Lex->create_last_non_select_table->table_name; + const char *table_name= lex->create_last_non_select_table->table_name; String **p= NULL; const char* err; @@ -6185,10 +6186,12 @@ field_def: case 1: p= &info.generated_as_row.start; err= "multiple 'GENERATED ALWAYS AS ROW START'"; + lex->last_field->flags|= VERS_SYS_START_FLAG; break; case 0: p= &info.generated_as_row.end; err= "multiple 'GENERATED ALWAYS AS ROW END'"; + lex->last_field->flags|= VERS_SYS_END_FLAG; break; default: /* Not Reachable */ diff --git a/sql/table.cc b/sql/table.cc index 1bda4607dcf..4b41a59a750 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -2001,7 +2001,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, { uchar flags= *extra2_field_flags++; if (flags & VERS_OPTIMIZED_UPDATE) - reg_field->disable_versioning(); + reg_field->flags|= VERS_OPTIMIZED_UPDATE_FLAG; if (flags & HIDDEN) reg_field->flags|= HIDDEN_FLAG; } @@ -2557,8 +2557,8 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, goto err; DBUG_PRINT("info", ("Columns with system versioning: [%d, %d]", row_start, row_end)); share->enable_system_versioning(row_start, row_end); - vers_start_field()->set_generated_row_start(); - vers_end_field()->set_generated_row_end(); + vers_start_field()->flags|= VERS_SYS_START_FLAG; + vers_end_field()->flags|= VERS_SYS_END_FLAG; DBUG_ASSERT(db_type()); if (db_type()->versioned()) @@ -3148,7 +3148,7 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share, outparam->non_generated_field = fptr; for (i=0 ; i < share->fields; i++) { - if (outparam->field[i]->is_generated()) + if (outparam->field[i]->vers_sys_field()) continue; *fptr++ = outparam->field[i]; } -- cgit v1.2.1 From eff649eba4cb6b9d8b0aaf5ea4d0c8945a3c4fda Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Mon, 21 Nov 2016 16:41:51 +0000 Subject: Parser: syntax extension FOR SYSTEM_TIME ALL --- sql/sql_yacc.yy | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'sql') diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index fb2bbb9ad64..a38c0861656 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -8712,6 +8712,16 @@ opt_for_system_time_clause: MYSQL_YYABORT; Lex->current_select->vers_conditions.init(FOR_SYSTEM_TIME_AS_OF, UNIT_TIMESTAMP, item); } + | FOR_SYSTEM_TIME_SYM ALL + { + static MYSQL_TIME min= { TIMESTAMP_MIN_YEAR, 1, 1, 0, 0, 0, 0, false, MYSQL_TIMESTAMP_DATETIME }; + static MYSQL_TIME max= { TIMESTAMP_MAX_YEAR, 12, 31, 23, 59, 59, 0, false, MYSQL_TIMESTAMP_DATETIME }; + Item *t0= new (thd->mem_root) Item_datetime_literal(thd, &min); + Item *t1= new (thd->mem_root) Item_datetime_literal(thd, &max); + if (!t0 || !t1) + MYSQL_YYABORT; + Lex->current_select->vers_conditions.init(FOR_SYSTEM_TIME_BETWEEN, UNIT_TIMESTAMP, t0, t1); + } | FOR_SYSTEM_TIME_SYM FROM trans_or_timestamp -- cgit v1.2.1 From dd3099a00d81fe9365d83f811e8ebc34a0d8c4f4 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Thu, 24 Nov 2016 04:00:28 +0000 Subject: SQL: unsupported engine fix for VTQ funcs --- sql/item_timefunc.cc | 9 +++++++++ sql/share/errmsg-utf8.txt | 4 ++-- sql/sql_select.cc | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) (limited to 'sql') diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 1f9d1076060..9a0c8efb031 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -3322,6 +3322,11 @@ VTQ_common::init_hton() hton= plugin_hton(plugin_int_to_ref(innodb_plugin)); DBUG_ASSERT(hton); } + if (hton && !hton->versioned()) + { + my_error(ER_VERS_ENGINE_UNSUPPORTED, MYF(0), Item::name ? Item::name : this->func_name()); + hton= NULL; + } } } @@ -3343,6 +3348,7 @@ Item_func_vtq_ts::get_date(MYSQL_TIME *res, ulonglong fuzzy_date) if (!hton) return true; + DBUG_ASSERT(hton->vers_query_trx_id); null_value= !hton->vers_query_trx_id(thd, res, trx_id, vtq_field); return false; @@ -3394,6 +3400,7 @@ Item_func_vtq_id::get_by_trx_id(ulonglong trx_id) return 0; } + DBUG_ASSERT(hton->vers_query_trx_id); null_value= !hton->vers_query_trx_id(thd, &res, trx_id, vtq_field); return res; } @@ -3404,6 +3411,7 @@ Item_func_vtq_id::get_by_commit_ts(MYSQL_TIME &commit_ts, bool backwards) THD *thd= current_thd; // can it differ from constructor's? DBUG_ASSERT(thd); + DBUG_ASSERT(hton->vers_query_commit_ts); null_value= !hton->vers_query_commit_ts(thd, &cached_result, commit_ts, VTQ_ALL, backwards); if (null_value) { @@ -3516,6 +3524,7 @@ Item_func_vtq_trx_sees::val_int() return true; } + DBUG_ASSERT(hton->vers_trx_sees); bool result= false; null_value= !hton->vers_trx_sees(thd, result, trx_id1, trx_id0, commit_id1, iso_level1, commit_id0); return result; diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 19bda09cc39..695c118db4a 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7502,8 +7502,8 @@ ER_VERS_FIELD_WRONG_TYPE ER_VERS_WRONG_PARAMS eng "Wrong parameters for versioned table %`s: %s" -ER_VERS_TRX_ID_UNSUPPORTED - eng "Engine does not support versioned TRX_ID" +ER_VERS_ENGINE_UNSUPPORTED + eng "Engine does not support System Versioning for %`s" ER_VERS_RANGE_UNITS_MISMATCH eng "Range units mismatch" diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 4c2ba3caa02..68212eb8fef 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -769,7 +769,7 @@ setup_for_system_time(THD *thd, TABLE_LIST *tables, COND **where_expr, SELECT_LE { if (slex->vers_conditions.unit == UNIT_TRX_ID) { - my_error(ER_VERS_TRX_ID_UNSUPPORTED, MYF(0), table->table_name); + my_error(ER_VERS_ENGINE_UNSUPPORTED, MYF(0), table->table_name); DBUG_RETURN(-1); } } -- cgit v1.2.1 From 1bedafb733fd2bffe1c896ba7d01ca91cafd4df5 Mon Sep 17 00:00:00 2001 From: kevg Date: Thu, 24 Nov 2016 17:36:02 +0300 Subject: fix build and some warnings --- sql/sql_class.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 06a592b8d51..1a422fd9966 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1979,7 +1979,7 @@ bool THD::notify_shared_lock(MDL_context_owner *ctx_in_use, if (!thd_table->needs_reopen()) { signalled|= mysql_lock_abort_for_thread(this, thd_table); - if (this && WSREP(this) && wsrep_thd_is_BF(this, FALSE)) + if (WSREP(this) && wsrep_thd_is_BF(this, FALSE)) { WSREP_DEBUG("remove_table_from_cache: %llu", (unsigned long long) this->real_id); -- cgit v1.2.1 From 695c5aabadbb08eed58f963e4af9c2cc268ad6c1 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Fri, 9 Dec 2016 06:36:31 +0000 Subject: SQL: error on FOR SYSTEM_TIME without any versioned tables [fixes #88] --- sql/share/errmsg-utf8.txt | 4 ++-- sql/sql_select.cc | 13 ++++++++++--- 2 files changed, 12 insertions(+), 5 deletions(-) (limited to 'sql') diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 695c118db4a..f37070d9394 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7487,8 +7487,8 @@ ER_UNKNOWN_VIEW 42S02 # MariaDB error numbers related to System Versioning -ER_TABLE_DOESNT_SUPPORT_SYSTEM_VERSIONING - eng "Table %`s doesn't support system versioning" +ER_VERSIONING_REQUIRED + eng "System Versioning required: %s" ER_VERS_READONLY_FIELD eng "System field %`s is read-only" diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 68212eb8fef..7078936ca6f 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -668,9 +668,9 @@ setup_without_group(THD *thd, Ref_ptr_array ref_pointer_array, } static int -setup_for_system_time(THD *thd, TABLE_LIST *tables, COND **where_expr, SELECT_LEX *slex) +vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, SELECT_LEX *slex) { - DBUG_ENTER("setup_for_system_time"); + DBUG_ENTER("vers_setup_select"); #define newx new (thd->mem_root) TABLE_LIST *table; @@ -693,7 +693,14 @@ setup_for_system_time(THD *thd, TABLE_LIST *tables, COND **where_expr, SELECT_LE } if (versioned_tables == 0) + { + if (slex->vers_conditions.type != FOR_SYSTEM_TIME_UNSPECIFIED) + { + my_error(ER_VERSIONING_REQUIRED, MYF(0), "`FOR SYSTEM_TIME` query"); + DBUG_RETURN(-1); + } DBUG_RETURN(0); + } /* For prepared statements we create items on statement arena, because they must outlive execution phase for multiple executions. */ @@ -980,7 +987,7 @@ JOIN::prepare(TABLE_LIST *tables_init, } /* Handle FOR SYSTEM_TIME clause. */ - if (setup_for_system_time(thd, tables_list, &conds, select_lex) < 0) + if (vers_setup_select(thd, tables_list, &conds, select_lex) < 0) DBUG_RETURN(-1); /* -- cgit v1.2.1 From a17b8f707fb6e3bb7f665ea7617daf310f3a3d6b Mon Sep 17 00:00:00 2001 From: kevg Date: Wed, 7 Dec 2016 15:15:47 +0300 Subject: 0.5: basic support for ALTER TABLE for InnoDB and other storage engines [closes #57] --- sql/field.h | 1 + sql/handler.cc | 290 ++++++++++++++++++++++++++++++++++++++++++------------- sql/handler.h | 31 +++++- sql/sql_table.cc | 44 +++++++++ sql/sql_yacc.yy | 37 ++++++- sql/table.cc | 37 +------ 6 files changed, 336 insertions(+), 104 deletions(-) (limited to 'sql') diff --git a/sql/field.h b/sql/field.h index a479ec569ff..ab480b84fda 100644 --- a/sql/field.h +++ b/sql/field.h @@ -4208,6 +4208,7 @@ public: uint from_length,to_length; Field *from_field,*to_field; String tmp; // For items + MYSQL_TIME now; // Used for sys_trx_start Copy_field() {} ~Copy_field() {} diff --git a/sql/handler.cc b/sql/handler.cc index 14b751c0628..f9391729e99 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -6553,6 +6553,18 @@ int del_global_index_stat(THD *thd, TABLE* table, KEY* key_info) DBUG_RETURN(res); } +bool Vers_parse_info::is_trx_start(const char *name) const +{ + DBUG_ASSERT(name); + return generated_as_row.start && + !strcmp(generated_as_row.start->c_ptr(), name); +} +bool Vers_parse_info::is_trx_end(const char *name) const +{ + DBUG_ASSERT(name); + return generated_as_row.end && !strcmp(generated_as_row.end->c_ptr(), name); +} + static bool create_string(MEM_ROOT *mem_root, String **s, const char *value) { *s= new (mem_root) String(value, system_charset_info); @@ -6581,7 +6593,7 @@ static bool create_sys_trx_field(THD *thd, const char *field_name, else { f->sql_type= MYSQL_TYPE_TIMESTAMP2; - f->length= 6; + f->length= MAX_DATETIME_PRECISION; } if (f->check(thd)) @@ -6594,37 +6606,17 @@ static bool create_sys_trx_field(THD *thd, const char *field_name, return false; } -bool Vers_parse_info::fix_implicit( - THD *thd, - Alter_info *alter_info, - bool integer_fields) +bool Vers_parse_info::fix_implicit(THD *thd, Alter_info *alter_info, + bool integer_fields) { - List_iterator it(alter_info->create_list); - while (Create_field *f= it++) - { - const char *name= f->field_name; - size_t len= strlen(name); - if (generated_as_row.start && - !strncmp(name, generated_as_row.start->c_ptr(), len)) - continue; - if (generated_as_row.end && - !strncmp(name, generated_as_row.end->c_ptr(), len)) - continue; - - if ((f->versioning == Column_definition::VERSIONING_NOT_SET && - !declared_system_versioning) || - f->versioning == Column_definition::WITHOUT_VERSIONING) - { - f->flags|= VERS_OPTIMIZED_UPDATE_FLAG; - } - } - // If user specified some of these he must specify the others too. Do nothing. if (generated_as_row.start || generated_as_row.end || period_for_system_time.start || period_for_system_time.end) return false; - return create_sys_trx_field(thd,"sys_trx_start", alter_info, + alter_info->flags|= Alter_info::ALTER_ADD_COLUMN; + + return create_sys_trx_field(thd, "sys_trx_start", alter_info, &generated_as_row.start, VERS_SYS_START_FLAG, integer_fields) || create_sys_trx_field(thd, "sys_trx_end", alter_info, @@ -6642,92 +6634,256 @@ bool Vers_parse_info::check_and_fix_implicit( bool integer_fields, const char* table_name) { - if (!( - has_versioned_fields || - has_unversioned_fields || - declared_system_versioning || - period_for_system_time.start || - period_for_system_time.end || - generated_as_row.start || - generated_as_row.end)) - { + if (!need_to_check()) return false; + + if (declared_without_system_versioning) + { + my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, + "'WITHOUT SYSTEM VERSIONING' is not allowed"); + return true; } - if (!declared_system_versioning && !has_versioned_fields) + if (!declared_with_system_versioning && !has_versioned_fields) { - my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, "'WITH SYSTEM VERSIONING' missing"); + my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, + "'WITH SYSTEM VERSIONING' missing"); return true; } + List_iterator it(alter_info->create_list); + while (Create_field *f= it++) + { + if (is_trx_start(f->field_name) || is_trx_end(f->field_name)) + continue; + + if ((f->versioning == Column_definition::VERSIONING_NOT_SET && + !declared_with_system_versioning) || + f->versioning == Column_definition::WITHOUT_VERSIONING) + { + f->flags|= VERS_OPTIMIZED_UPDATE_FLAG; + } + } + if (fix_implicit(thd, alter_info, integer_fields)) return true; + int not_set= 0; + int with= 0; + it.rewind(); + while (const Create_field *f= it++) { - int not_set= 0; - int with= 0; - List_iterator it(alter_info->create_list); - while (const Create_field *f= it++) - { - const char *name= f->field_name; - size_t len= strlen(name); - if (generated_as_row.start && - !strncmp(name, generated_as_row.start->c_ptr(), len)) - continue; - if (generated_as_row.end && - !strncmp(name, generated_as_row.end->c_ptr(), len)) - continue; + if (is_trx_start(f->field_name) || is_trx_end(f->field_name)) + continue; - if (f->versioning == Column_definition::VERSIONING_NOT_SET) - not_set++; - else if (f->versioning == Column_definition::WITH_VERSIONING) - with++; - } + if (f->versioning == Column_definition::VERSIONING_NOT_SET) + not_set++; + else if (f->versioning == Column_definition::WITH_VERSIONING) + with++; + } + + bool table_with_system_versioning= + generated_as_row.start || generated_as_row.end || + period_for_system_time.start || period_for_system_time.end; + + if (with == 0 && (not_set == 0 || !table_with_system_versioning)) + { + my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, + "versioned fields missing"); + return true; + } + + return check_with_conditions(table_name) || + check_generated_type(table_name, alter_info, integer_fields); +} + +static bool add_field_to_drop_list(THD *thd, Alter_info *alter_info, + Field *field) +{ + DBUG_ASSERT(field); + DBUG_ASSERT(field->field_name); + Alter_drop *ad= new (thd->mem_root) + Alter_drop(Alter_drop::COLUMN, field->field_name, false); + return !ad || alter_info->drop_list.push_back(ad, thd->mem_root); +} + +bool Vers_parse_info::check_and_fix_alter(THD *thd, Alter_info *alter_info, + HA_CREATE_INFO *create_info, + TABLE_SHARE *share) +{ + bool integer_fields= + create_info->db_type->flags & HTON_SUPPORTS_SYS_VERSIONING; + const char *table_name= share->table_name.str; - bool table_with_system_versioning= - declared_system_versioning || generated_as_row.start || - generated_as_row.end || period_for_system_time.start || - period_for_system_time.end; + if (!need_to_check() && !share->versioned) + return false; - if ((table_with_system_versioning && not_set == 0 && with == 0) || - (!table_with_system_versioning && with == 0)) + if (declared_without_system_versioning) + { + if (!share->versioned) { - my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, "versioned fields missing"); + my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, + "table is not versioned"); + return true; + } + + if (add_field_to_drop_list(thd, alter_info, share->vers_start_field()) || + add_field_to_drop_list(thd, alter_info, share->vers_end_field())) return true; + + alter_info->flags|= Alter_info::ALTER_DROP_COLUMN; + return false; + } + + if ((has_versioned_fields || has_unversioned_fields) && !share->versioned) + { + my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, + "Can not change fields versioning mode in a non-versioned table"); + return true; + } + + if (share->versioned) + { + // copy info from existing table + create_info->options|= HA_VERSIONED_TABLE; + + DBUG_ASSERT(share->vers_start_field() && share->vers_end_field()); + const char *start= share->vers_start_field()->field_name; + const char *end= share->vers_end_field()->field_name; + DBUG_ASSERT(start && end); + + if (create_string(thd->mem_root, &generated_as_row.start, start) || + create_string(thd->mem_root, &generated_as_row.end, end) || + create_string(thd->mem_root, &period_for_system_time.start, start) || + create_string(thd->mem_root, &period_for_system_time.end, end)) + return true; + + if (alter_info->create_list.elements) + { + DBUG_ASSERT(share->fields > 2); + const char *after_this= share->field[share->fields - 3]->field_name; + List_iterator it(alter_info->create_list); + while (Create_field *f= it++) + { + if (f->versioning == Column_definition::WITHOUT_VERSIONING) + f->flags|= VERS_OPTIMIZED_UPDATE_FLAG; + + if (f->change) + continue; + + if (f->after) + { + if (is_trx_start(f->after) || is_trx_end(f->after)) + { + my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, + "Can not put new field after system versioning field"); + return true; + } + + continue; + } + + // TODO: ALTER_COLUMN_ORDER? + f->after= after_this; + } } + + if (alter_info->drop_list.elements) + { + List_iterator it(alter_info->drop_list); + while (Alter_drop* d= it++) + { + if (is_trx_start(d->name) || is_trx_end(d->name)) + { + my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, + "Can not drop system versioning field"); + return true; + } + } + } + + return false; } + return fix_implicit(thd, alter_info, integer_fields) || + (declared_with_system_versioning && + (check_with_conditions(table_name) || + check_generated_type(table_name, alter_info, integer_fields))); +} + +bool Vers_parse_info::check_with_conditions(const char *table_name) const +{ if (!generated_as_row.start) { - my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, "'GENERATED AS ROW START' column missing"); + my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, + "'GENERATED AS ROW START' column missing"); return true; } if (!generated_as_row.end) { - my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, "'GENERATED AS ROW END' column missing"); + my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, + "'GENERATED AS ROW END' column missing"); return true; } if (!period_for_system_time.start || !period_for_system_time.end) { - my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, "'PERIOD FOR SYSTEM_TIME' missing"); + my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, + "'PERIOD FOR SYSTEM_TIME' missing"); return true; } if (my_strcasecmp(system_charset_info, generated_as_row.start->c_ptr(), period_for_system_time.start->c_ptr())) { - my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, "'PERIOD FOR SYSTEM_TIME' and 'GENERATED AS ROW START' mismatch"); + my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, + "'PERIOD FOR SYSTEM_TIME' and 'GENERATED AS ROW START' mismatch"); return true; } if (my_strcasecmp(system_charset_info, generated_as_row.end->c_ptr(), period_for_system_time.end->c_ptr())) { - my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, "'PERIOD FOR SYSTEM_TIME' and 'GENERATED AS ROW END' mismatch"); + my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, + "'PERIOD FOR SYSTEM_TIME' and 'GENERATED AS ROW END' mismatch"); return true; } return false; } + +bool Vers_parse_info::check_generated_type(const char *table_name, + Alter_info *alter_info, + bool integer_fields) const +{ + List_iterator it(alter_info->create_list); + while (Create_field *f= it++) + { + if (is_trx_start(f->field_name) || is_trx_end(f->field_name)) + { + if (integer_fields) + { + if (f->sql_type != MYSQL_TYPE_LONGLONG || !(f->flags & UNSIGNED_FLAG) || + f->length != (MY_INT64_NUM_DECIMAL_DIGITS - 1)) + { + my_error(ER_VERS_FIELD_WRONG_TYPE, MYF(0), f->field_name, + "BIGINT(20) UNSIGNED", table_name); + return true; + } + } + else + { + if (f->sql_type != MYSQL_TYPE_TIMESTAMP2 || + f->length != MAX_DATETIME_FULL_WIDTH) + { + my_error(ER_VERS_FIELD_WRONG_TYPE, MYF(0), f->field_name, + "TIMESTAMP(6)", table_name); + return true; + } + } + } + } + + return false; +} diff --git a/sql/handler.h b/sql/handler.h index 394d6276e4f..ced716ed11a 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -1674,7 +1674,8 @@ struct Schema_specification_st struct Vers_parse_info { Vers_parse_info() : - declared_system_versioning(false), + declared_with_system_versioning(false), + declared_without_system_versioning(false), has_versioned_fields(false), has_unversioned_fields(false) {} @@ -1698,12 +1699,36 @@ struct Vers_parse_info } private: + bool is_trx_start(const char *name) const; + bool is_trx_end(const char *name) const; bool fix_implicit(THD *thd, Alter_info *alter_info, bool integer_fields); + bool need_to_check() const + { + return + has_versioned_fields || + has_unversioned_fields || + declared_with_system_versioning || + declared_without_system_versioning || + period_for_system_time.start || + period_for_system_time.end || + generated_as_row.start || + generated_as_row.end; + } + bool check_with_conditions(const char *table_name) const; + bool check_generated_type(const char *table_name, Alter_info *alter_info, + bool integer_fields) const; + public: - bool check_and_fix_implicit(THD *thd, Alter_info *alter_info, bool integer_fields, const char* table_name); + bool check_and_fix_implicit(THD *thd, Alter_info *alter_info, + bool integer_fields, const char *table_name); + bool check_and_fix_alter(THD *thd, Alter_info *alter_info, + HA_CREATE_INFO *create_info, TABLE_SHARE *share); /** User has added 'WITH SYSTEM VERSIONING' to table definition */ - bool declared_system_versioning : 1; + bool declared_with_system_versioning : 1; + + /** Use has added 'WITHOUT SYSTEM VERSIONING' to ALTER TABLE */ + bool declared_without_system_versioning : 1; /** At least one field was specified 'WITH SYSTEM VERSIONING'. Useful for diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 68504ffddba..9ad37134c86 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -55,6 +55,8 @@ #include "transaction.h" #include "sql_audit.h" #include "sql_sequence.h" +#include "tztime.h" + #ifdef __WIN__ #include @@ -8718,6 +8720,12 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, if (check_engine(thd, alter_ctx.new_db, alter_ctx.new_name, create_info)) DBUG_RETURN(true); + if (create_info->vers_info.check_and_fix_alter(thd, alter_info, create_info, + table->s)) + { + DBUG_RETURN(true); + } + if ((create_info->db_type != table->s->db_type() || alter_info->flags & Alter_info::ALTER_PARTITION) && !table->file->can_switch_engines()) @@ -9654,6 +9662,10 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, sql_mode_t save_sql_mode= thd->variables.sql_mode; ulonglong prev_insert_id, time_to_report_progress; Field **dfield_ptr= to->default_field; + bool make_versioned= !from->versioned() && to->versioned(); + bool make_unversioned= from->versioned() && !to->versioned(); + Field *to_sys_trx_start= NULL, *from_sys_trx_end= NULL, *to_sys_trx_end= NULL; + MYSQL_TIME now; DBUG_ENTER("copy_data_between_tables"); /* Two or 3 stages; Sorting, copying data and update indexes */ @@ -9753,6 +9765,19 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, thd_progress_next_stage(thd); } + if (make_versioned) + { + thd->variables.time_zone->gmt_sec_to_TIME(&now, thd->query_start()); + now.second_part= thd->query_start_sec_part(); + thd->time_zone_used= 1; + to_sys_trx_start= to->field[to->s->row_start_field]; + to_sys_trx_end= to->field[to->s->row_end_field]; + } + else if (make_unversioned) + { + from_sys_trx_end= from->field[from->s->row_end_field]; + } + THD_STAGE_INFO(thd, stage_copy_to_tmp_table); /* Tell handler that we have values for all columns in the to table */ to->use_all_columns(); @@ -9806,6 +9831,25 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, { copy_ptr->do_copy(copy_ptr); } + + if (make_versioned) + { + to_sys_trx_start->set_notnull(to_sys_trx_start->null_offset()); + // TODO: write directly to record bypassing the same checks on every call + to_sys_trx_start->store_time(&now); + + static const timeval max_tv= {0x7fffffff, 0}; + static const uint dec= 6; + to_sys_trx_end->set_notnull(to_sys_trx_end->null_offset()); + my_timestamp_to_binary(&max_tv, to_sys_trx_end->ptr, dec); + } + else if (make_unversioned) + { + // Drop history rows. + if (!from_sys_trx_end->is_max()) + continue; + } + prev_insert_id= to->file->next_insert_id; if (to->default_field) to->update_default_fields(0, ignore); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index a38c0861656..bc5d65264a0 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -5855,10 +5855,31 @@ create_table_option: } | WITH_SYSTEM_SYM VERSIONING { + const char *table_name= + Lex->create_last_non_select_table->table_name; Vers_parse_info &info= Lex->vers_get_info(); - info.declared_system_versioning= true; + if (info.declared_with_system_versioning || + info.declared_without_system_versioning) + my_yyabort_error( + (ER_VERS_WRONG_PARAMS, MYF(0), table_name, + "Versioning specified more than once for the same table")); + + info.declared_with_system_versioning= true; Lex->create_info.options|= HA_VERSIONED_TABLE; } + | WITHOUT SYSTEM VERSIONING + { + const char *table_name= + Lex->create_last_non_select_table->table_name; + Vers_parse_info &info= Lex->vers_get_info(); + if (info.declared_with_system_versioning || + info.declared_without_system_versioning) + my_yyabort_error( + (ER_VERS_WRONG_PARAMS, MYF(0), table_name, + "Versioning specified more than once for the same table")); + + info.declared_without_system_versioning= true; + } ; default_charset: @@ -6679,12 +6700,26 @@ serial_attribute: } | WITH_SYSTEM_SYM VERSIONING { + if (Lex->last_field->versioning != + Column_definition::VERSIONING_NOT_SET) + my_yyabort_error( + (ER_VERS_WRONG_PARAMS, MYF(0), + Lex->create_last_non_select_table->table_name, + "Versioning specified more than once for the same field")); + Lex->last_field->versioning = Column_definition::WITH_VERSIONING; Lex->create_info.vers_info.has_versioned_fields= true; Lex->create_info.options|= HA_VERSIONED_TABLE; } | WITHOUT SYSTEM VERSIONING { + if (Lex->last_field->versioning != + Column_definition::VERSIONING_NOT_SET) + my_yyabort_error( + (ER_VERS_WRONG_PARAMS, MYF(0), + Lex->create_last_non_select_table->table_name, + "Versioning specified more than once for the same field")); + Lex->last_field->versioning = Column_definition::WITHOUT_VERSIONING; Lex->create_info.vers_info.has_unversioned_fields= true; Lex->create_info.options|= HA_VERSIONED_TABLE; diff --git a/sql/table.cc b/sql/table.cc index 4b41a59a750..ad44f896621 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -2551,44 +2551,15 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, else { DBUG_PRINT("info", ("Setting system versioning informations")); - uint16 row_start = uint2korr(system_period); - uint16 row_end = uint2korr(system_period + sizeof(uint16)); + uint16 row_start= uint2korr(system_period); + uint16 row_end= uint2korr(system_period + sizeof(uint16)); if (row_start >= share->fields || row_end >= share->fields) goto err; - DBUG_PRINT("info", ("Columns with system versioning: [%d, %d]", row_start, row_end)); + DBUG_PRINT("info", ("Columns with system versioning: [%d, %d]", row_start, + row_end)); share->enable_system_versioning(row_start, row_end); vers_start_field()->flags|= VERS_SYS_START_FLAG; vers_end_field()->flags|= VERS_SYS_END_FLAG; - - DBUG_ASSERT(db_type()); - if (db_type()->versioned()) - { - if (vers_start_field()->type() != MYSQL_TYPE_LONGLONG - || !(vers_start_field()->flags & UNSIGNED_FLAG)) - { - my_error(ER_VERS_FIELD_WRONG_TYPE, MYF(0), vers_start_field()->field_name, "BIGINT UNSIGNED", share->table_name); - goto err; - } - if (vers_end_field()->type() != MYSQL_TYPE_LONGLONG - || !(vers_end_field()->flags & UNSIGNED_FLAG)) - { - my_error(ER_VERS_FIELD_WRONG_TYPE, MYF(0), vers_end_field()->field_name, "BIGINT UNSIGNED", share->table_name); - goto err; - } - } - else - { - if (vers_start_field()->type() != MYSQL_TYPE_TIMESTAMP) - { - my_error(ER_VERS_FIELD_WRONG_TYPE, MYF(0), vers_start_field()->field_name, "TIMESTAMP", share->table_name); - goto err; - } - if (vers_end_field()->type() != MYSQL_TYPE_TIMESTAMP) - { - my_error(ER_VERS_FIELD_WRONG_TYPE, MYF(0), vers_end_field()->field_name, "TIMESTAMP", share->table_name); - goto err; - } - } // if (db_type()->versioned()) } // if (system_period == NULL) delete handler_file; -- cgit v1.2.1 From 1742561b4e750b9ec1b1640f4725a218d79b50b1 Mon Sep 17 00:00:00 2001 From: kevg Date: Sat, 10 Dec 2016 18:33:40 +0300 Subject: fix use-after-free [closes #89] --- sql/item.cc | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) (limited to 'sql') diff --git a/sql/item.cc b/sql/item.cc index e36fadea957..4bcdb477a51 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -2760,19 +2760,17 @@ void Item_field::set_field(Field *field_par) if (field->table->s->tmp_table == SYSTEM_TMP_TABLE) any_privileges= 0; - if (field->flags & VERS_OPTIMIZED_UPDATE_FLAG && context && context->select_lex && - context->select_lex->vers_conditions.type != - FOR_SYSTEM_TIME_UNSPECIFIED && - !field->force_null) + field->force_null= false; + if (field->flags & VERS_OPTIMIZED_UPDATE_FLAG && context && + context->select_lex && + context->select_lex->vers_conditions.type != FOR_SYSTEM_TIME_UNSPECIFIED) { - DBUG_ASSERT(context->select_lex->parent_lex && - context->select_lex->parent_lex->thd); field->force_null= true; - THD *thd= context->select_lex->parent_lex->thd; - push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, - ER_NON_VERSIONED_FIELD_IN_VERSIONED_QUERY, - ER_THD(thd, ER_NON_VERSIONED_FIELD_IN_VERSIONED_QUERY), - field_name); + push_warning_printf( + current_thd, Sql_condition::WARN_LEVEL_WARN, + ER_NON_VERSIONED_FIELD_IN_VERSIONED_QUERY, + ER_THD(current_thd, ER_NON_VERSIONED_FIELD_IN_VERSIONED_QUERY), + field_name); } } @@ -5922,8 +5920,6 @@ void Item_field::cleanup() it will be linked correctly next time by name of field and table alias. I.e. we can drop 'field'. */ - if (field) - field->force_null= false; field= 0; item_equal= NULL; null_value= FALSE; -- cgit v1.2.1 From dc4ef66fee4efd9e8edd9c14288b5c58274c0800 Mon Sep 17 00:00:00 2001 From: kevg Date: Sat, 10 Dec 2016 21:56:32 +0300 Subject: SQL: optimize FOR SYSTEM_TIME ALL queries [closes #85] --- sql/sql_select.cc | 11 +++++++---- sql/sql_yacc.yy | 8 +------- sql/table.h | 5 +++-- 3 files changed, 11 insertions(+), 13 deletions(-) (limited to 'sql') diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 7078936ca6f..1aef44397b2 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -676,11 +676,9 @@ vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, SELECT_LEX *s TABLE_LIST *table; int versioned_tables= 0; Query_arena *arena= 0, backup; - bool is_prepare= thd->stmt_arena->is_stmt_prepare(); - if (!thd->stmt_arena->is_conventional() - && !is_prepare - && !thd->stmt_arena->is_sp_execute()) + if (!thd->stmt_arena->is_conventional() && + !thd->stmt_arena->is_stmt_prepare() && !thd->stmt_arena->is_sp_execute()) { // statement is already prepared DBUG_RETURN(0); @@ -702,6 +700,11 @@ vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, SELECT_LEX *s DBUG_RETURN(0); } + if (slex->vers_conditions.type == FOR_SYSTEM_TIME_ALL) + { + DBUG_RETURN(0); + } + /* For prepared statements we create items on statement arena, because they must outlive execution phase for multiple executions. */ arena= thd->activate_stmt_arena_if_needed(&backup); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index bc5d65264a0..f91568e9fb1 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -8749,13 +8749,7 @@ opt_for_system_time_clause: } | FOR_SYSTEM_TIME_SYM ALL { - static MYSQL_TIME min= { TIMESTAMP_MIN_YEAR, 1, 1, 0, 0, 0, 0, false, MYSQL_TIMESTAMP_DATETIME }; - static MYSQL_TIME max= { TIMESTAMP_MAX_YEAR, 12, 31, 23, 59, 59, 0, false, MYSQL_TIMESTAMP_DATETIME }; - Item *t0= new (thd->mem_root) Item_datetime_literal(thd, &min); - Item *t1= new (thd->mem_root) Item_datetime_literal(thd, &max); - if (!t0 || !t1) - MYSQL_YYABORT; - Lex->current_select->vers_conditions.init(FOR_SYSTEM_TIME_BETWEEN, UNIT_TIMESTAMP, t0, t1); + Lex->current_select->vers_conditions.init(FOR_SYSTEM_TIME_ALL, UNIT_TIMESTAMP); } | FOR_SYSTEM_TIME_SYM FROM diff --git a/sql/table.h b/sql/table.h index df0300d498c..b93bb3df5e3 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1817,7 +1817,8 @@ enum vers_range_type_t FOR_SYSTEM_TIME_UNSPECIFIED = 0, FOR_SYSTEM_TIME_AS_OF, FOR_SYSTEM_TIME_FROM_TO, - FOR_SYSTEM_TIME_BETWEEN + FOR_SYSTEM_TIME_BETWEEN, + FOR_SYSTEM_TIME_ALL }; enum vers_range_unit_t @@ -1843,7 +1844,7 @@ struct vers_select_conds_t void init( vers_range_type_t t, vers_range_unit_t u, - Item * s, + Item * s= NULL, Item * e= NULL) { type= t; -- cgit v1.2.1 From e851c140f4a4fdfb7d450aa19a91f4eb65758b81 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Sun, 11 Dec 2016 17:04:11 +0000 Subject: SQL: (0.5) Versioned partitions [closes #77] * one `AS OF NOW`, multiple `VERSIONING` partitions; * rotation of `VERSIONING` partitions by record count, time period; * rotation is multi-threaded; * conventional subpartitions as bottom level for versioned partitions; * `DEFAULT` keyword selects first `VERSIONING` partition; * ALTER TABLE ADD/DROP partition; * REBUILD PARTITION basic operation. --- sql/field.cc | 11 +- sql/field.h | 9 +- sql/ha_partition.cc | 7 +- sql/ha_partition.h | 34 +++++- sql/handler.cc | 13 ++- sql/handler.h | 14 +-- sql/item_timefunc.cc | 4 +- sql/lex.h | 4 +- sql/mysqld.cc | 7 +- sql/mysqld.h | 4 +- sql/partition_element.h | 55 ++++++++- sql/partition_info.cc | 291 +++++++++++++++++++++++++++++++++++++++++++++- sql/partition_info.h | 109 +++++++++++++++++ sql/share/errmsg-utf8.txt | 20 +++- sql/sql_delete.cc | 3 +- sql/sql_partition.cc | 172 +++++++++++++++++++++++++-- sql/sql_show.cc | 5 + sql/sql_yacc.yy | 99 ++++++++++++++-- sql/table.cc | 26 ++++- sql/table.h | 47 ++++++-- 20 files changed, 861 insertions(+), 73 deletions(-) (limited to 'sql') diff --git a/sql/field.cc b/sql/field.cc index 295b22ad8ec..a83cbb20eff 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -5468,8 +5468,15 @@ my_time_t Field_timestampf::get_timestamp(const uchar *pos, ulong *sec_part) const { struct timeval tm; - my_timestamp_from_binary(&tm, pos, dec); - *sec_part= tm.tv_usec; + if (sec_part) + { + my_timestamp_from_binary(&tm, pos ? pos : ptr, dec); + *sec_part= tm.tv_usec; + } + else + { + my_timestamp_from_binary(&tm, pos ? pos : ptr, 0); + } return tm.tv_sec; } diff --git a/sql/field.h b/sql/field.h index ab480b84fda..a6b1cace962 100644 --- a/sql/field.h +++ b/sql/field.h @@ -679,10 +679,6 @@ public: */ virtual bool set_max() { DBUG_ASSERT(0); return false; } - - /** - Used by System Versioning. - */ virtual bool is_max() { DBUG_ASSERT(0); return false; } @@ -971,6 +967,9 @@ public: } virtual bool set_explicit_default(Item *value); + virtual my_time_t get_timestamp(const uchar *pos= NULL, ulong *sec_part= NULL) const + { DBUG_ASSERT(0); return 0; } + /** Evaluates the @c UPDATE default function, if one exists, and stores the result in the record buffer. If no such function exists for the column, @@ -2544,7 +2543,7 @@ public: bool set_max(); bool is_max(); void store_TIME(my_time_t timestamp, ulong sec_part); - my_time_t get_timestamp(const uchar *pos, ulong *sec_part) const; + my_time_t get_timestamp(const uchar *pos= NULL, ulong *sec_part= NULL) const; uint size_of() const { return sizeof(*this); } }; diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index b27de9dad4d..1ba3210d62b 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -2025,12 +2025,15 @@ int ha_partition::copy_partitions(ulonglong * const copied, else { THD *thd= ha_thd(); + handler *new_file= m_new_file[new_part]; /* Copy record to new handler */ (*copied)++; + if (new_file->ha_external_lock(thd, F_UNLCK) || new_file->ha_external_lock(thd, F_WRLCK)) + goto error; tmp_disable_binlog(thd); /* Do not replicate the low-level changes. */ - result= m_new_file[new_part]->ha_write_row(m_rec0); + result= new_file->ha_write_row(m_rec0); reenable_binlog(thd); - if (result) + if (new_file->ha_external_lock(thd, F_UNLCK) || new_file->ha_external_lock(thd, F_RDLCK) || result) goto error; } } diff --git a/sql/ha_partition.h b/sql/ha_partition.h index 70ec4ae8edb..1ca320eef1a 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -23,7 +23,7 @@ enum partition_keywords { - PKW_HASH= 0, PKW_RANGE, PKW_LIST, PKW_KEY, PKW_MAXVALUE, PKW_LINEAR, + PKW_HASH= 0, PKW_RANGE, PKW_LIST, PKW_SYSTEM_TIME, PKW_KEY, PKW_MAXVALUE, PKW_LINEAR, PKW_COLUMNS, PKW_ALGORITHM }; @@ -1277,6 +1277,38 @@ public: return h; } + virtual bool versioned() const + { + return m_innodb; + } + + virtual ha_rows part_recs_slow(void *_part_elem) + { + partition_element *part_elem= reinterpret_cast(_part_elem); + DBUG_ASSERT(m_part_info); + uint32 sub_factor= m_part_info->num_subparts ? m_part_info->num_subparts : 1; + uint32 part_id= part_elem->id * sub_factor; + uint32 part_id_end= part_id + sub_factor; + DBUG_ASSERT(part_id_end <= m_tot_parts); + ha_rows part_recs= 0; + for (; part_id < part_id_end; ++part_id) + { + handler *file= m_file[part_id]; + DBUG_ASSERT(bitmap_is_set(&(m_part_info->read_partitions), part_id)); + file->info(HA_STATUS_TIME | HA_STATUS_VARIABLE | + HA_STATUS_VARIABLE_EXTRA | HA_STATUS_NO_LOCK); + + part_recs+= file->stats.records; + } + return part_recs; + } + + virtual handler* part_handler(uint32 part_id) + { + DBUG_ASSERT(part_id < m_tot_parts); + return m_file[part_id]; + } + friend int cmp_key_rowid_part_id(void *ptr, uchar *ref1, uchar *ref2); }; diff --git a/sql/handler.cc b/sql/handler.cc index f9391729e99..62c628a01dc 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -6571,7 +6571,7 @@ static bool create_string(MEM_ROOT *mem_root, String **s, const char *value) return *s == NULL; } -static bool create_sys_trx_field(THD *thd, const char *field_name, +static bool vers_create_sys_field(THD *thd, const char *field_name, Alter_info *alter_info, String **s, int flags, bool integer_fields) @@ -6616,16 +6616,19 @@ bool Vers_parse_info::fix_implicit(THD *thd, Alter_info *alter_info, alter_info->flags|= Alter_info::ALTER_ADD_COLUMN; - return create_sys_trx_field(thd, "sys_trx_start", alter_info, + static const char * sys_trx_start= "sys_trx_start"; + static const char * sys_trx_end= "sys_trx_end"; + + return vers_create_sys_field(thd, sys_trx_start, alter_info, &generated_as_row.start, VERS_SYS_START_FLAG, integer_fields) || - create_sys_trx_field(thd, "sys_trx_end", alter_info, + vers_create_sys_field(thd, sys_trx_end, alter_info, &generated_as_row.end, VERS_SYS_END_FLAG, integer_fields) || create_string(thd->mem_root, &period_for_system_time.start, - "sys_trx_start") || + sys_trx_start) || create_string(thd->mem_root, &period_for_system_time.end, - "sys_trx_end"); + sys_trx_end); } bool Vers_parse_info::check_and_fix_implicit( diff --git a/sql/handler.h b/sql/handler.h index ced716ed11a..5bcacf1f50b 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -1390,7 +1390,6 @@ struct handlerton /* System Versioning */ - bool versioned() const; bool (*vers_query_trx_id)(THD* thd, void *out, ulonglong trx_id, vtq_field_t field); bool (*vers_query_commit_ts)(THD* thd, void *out, const MYSQL_TIME &commit_ts, vtq_field_t field, bool backwards); bool (*vers_trx_sees)(THD *thd, bool &result, ulonglong trx_id1, ulonglong trx_id0, ulonglong commit_id1, uchar iso_level1, ulonglong commit_id0); @@ -4332,6 +4331,13 @@ public: */ virtual int find_unique_row(uchar *record, uint unique_ref) { return -1; /*unsupported */} + + virtual bool versioned() const + { DBUG_ASSERT(ht); return partition_ht()->flags & HTON_SUPPORTS_SYS_VERSIONING; } + virtual ha_rows part_recs_slow(void *_part_elem) + { DBUG_ASSERT(0); return false; } + virtual handler* part_handler(uint32 part_id) + { DBUG_ASSERT(0); return NULL; } protected: Handler_share *get_ha_share_ptr(); void set_ha_share_ptr(Handler_share *arg_ha_share); @@ -4522,10 +4528,4 @@ void print_keydup_error(TABLE *table, KEY *key, myf errflag); int del_global_index_stat(THD *thd, TABLE* table, KEY* key_info); int del_global_table_stat(THD *thd, LEX_STRING *db, LEX_STRING *table); - -inline -bool handlerton::versioned() const -{ - return flags & HTON_SUPPORTS_SYS_VERSIONING; -} #endif /* HANDLER_INCLUDED */ diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 9a0c8efb031..2f9b467408f 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -3314,7 +3314,7 @@ VTQ_common::init_hton() f->field->table && f->field->table->s && f->field->table->s->db_plugin); - hton= plugin_hton(f->field->table->s->db_plugin); + hton= f->field->table->file->partition_ht(); DBUG_ASSERT(hton); } else if (innodb_plugin) @@ -3322,7 +3322,7 @@ VTQ_common::init_hton() hton= plugin_hton(plugin_int_to_ref(innodb_plugin)); DBUG_ASSERT(hton); } - if (hton && !hton->versioned()) + if (hton && !(hton->flags & HTON_SUPPORTS_SYS_VERSIONING)) { my_error(ER_VERS_ENGINE_UNSUPPORTED, MYF(0), Item::name ? Item::name : this->func_name()); hton= NULL; diff --git a/sql/lex.h b/sql/lex.h index dff6624518a..73d661e376a 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -428,6 +428,7 @@ static SYMBOL symbols[] = { { "NONE", SYM(NONE_SYM)}, { "NOT", SYM(NOT_SYM)}, { "NOTFOUND", SYM(NOTFOUND_SYM)}, + { "NOW", SYM(NOW_SYM)}, { "NO_WRITE_TO_BINLOG", SYM(NO_WRITE_TO_BINLOG)}, { "NULL", SYM(NULL_SYM)}, { "NUMBER", SYM(NUMBER_SYM)}, @@ -693,7 +694,7 @@ static SYMBOL symbols[] = { { "VIA", SYM(VIA_SYM)}, { "VIEW", SYM(VIEW_SYM)}, { "VIRTUAL", SYM(VIRTUAL_SYM)}, - { "VERSIONING", SYM(VERSIONING)}, + { "VERSIONING", SYM(VERSIONING_SYM)}, { "WAIT", SYM(WAIT_SYM)}, { "WARNINGS", SYM(WARNINGS)}, { "WEEK", SYM(WEEK_SYM)}, @@ -740,7 +741,6 @@ static SYMBOL sql_functions[] = { { "MAX", SYM(MAX_SYM)}, { "MID", SYM(SUBSTRING)}, /* unireg function */ { "MIN", SYM(MIN_SYM)}, - { "NOW", SYM(NOW_SYM)}, { "NTH_VALUE", SYM(NTH_VALUE_SYM)}, { "NTILE", SYM(NTILE_SYM)}, { "POSITION", SYM(POSITION_SYM)}, diff --git a/sql/mysqld.cc b/sql/mysqld.cc index c497a41673e..acda4b51627 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -933,6 +933,9 @@ PSI_mutex_key key_LOCK_prepare_ordered, key_LOCK_commit_ordered, key_LOCK_slave_background; PSI_mutex_key key_TABLE_SHARE_LOCK_share; +PSI_mutex_key key_TABLE_SHARE_LOCK_rotation; +PSI_cond_key key_TABLE_SHARE_COND_rotation; + static PSI_mutex_info all_server_mutexes[]= { #ifdef HAVE_MMAP @@ -993,6 +996,7 @@ static PSI_mutex_info all_server_mutexes[]= { &key_structure_guard_mutex, "Query_cache::structure_guard_mutex", 0}, { &key_TABLE_SHARE_LOCK_ha_data, "TABLE_SHARE::LOCK_ha_data", 0}, { &key_TABLE_SHARE_LOCK_share, "TABLE_SHARE::LOCK_share", 0}, + { &key_TABLE_SHARE_LOCK_rotation, "TABLE_SHARE::LOCK_rotation", 0}, { &key_LOCK_error_messages, "LOCK_error_messages", PSI_FLAG_GLOBAL}, { &key_LOCK_prepare_ordered, "LOCK_prepare_ordered", PSI_FLAG_GLOBAL}, { &key_LOCK_after_binlog_sync, "LOCK_after_binlog_sync", PSI_FLAG_GLOBAL}, @@ -1108,7 +1112,8 @@ static PSI_cond_info all_server_conds[]= { &key_COND_slave_background, "COND_slave_background", 0}, { &key_COND_start_thread, "COND_start_thread", PSI_FLAG_GLOBAL}, { &key_COND_wait_gtid, "COND_wait_gtid", 0}, - { &key_COND_gtid_ignore_duplicates, "COND_gtid_ignore_duplicates", 0} + { &key_COND_gtid_ignore_duplicates, "COND_gtid_ignore_duplicates", 0}, + { &key_TABLE_SHARE_COND_rotation, "TABLE_SHARE::COND_rotation", 0} }; PSI_thread_key key_thread_bootstrap, key_thread_delayed_insert, diff --git a/sql/mysqld.h b/sql/mysqld.h index 9f9e9a3ae66..8ac0625b755 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -308,7 +308,8 @@ extern PSI_mutex_key key_LOCK_slave_state, key_LOCK_binlog_state, extern PSI_mutex_key key_TABLE_SHARE_LOCK_share, key_LOCK_stats, key_LOCK_global_user_client_stats, key_LOCK_global_table_stats, - key_LOCK_global_index_stats, key_LOCK_wakeup_ready, key_LOCK_wait_commit; + key_LOCK_global_index_stats, key_LOCK_wakeup_ready, key_LOCK_wait_commit, + key_TABLE_SHARE_LOCK_rotation; extern PSI_mutex_key key_LOCK_gtid_waiting; extern PSI_rwlock_key key_rwlock_LOCK_grant, key_rwlock_LOCK_logger, @@ -342,6 +343,7 @@ extern PSI_cond_key key_COND_rpl_thread, key_COND_rpl_thread_queue, key_COND_rpl_thread_stop, key_COND_rpl_thread_pool, key_COND_parallel_entry, key_COND_group_commit_orderer; extern PSI_cond_key key_COND_wait_gtid, key_COND_gtid_ignore_duplicates; +extern PSI_cond_key key_TABLE_SHARE_COND_rotation; extern PSI_thread_key key_thread_bootstrap, key_thread_delayed_insert, key_thread_handle_manager, key_thread_kill_server, key_thread_main, diff --git a/sql/partition_element.h b/sql/partition_element.h index b979b7a58e6..e76558bf7ae 100644 --- a/sql/partition_element.h +++ b/sql/partition_element.h @@ -26,7 +26,8 @@ enum partition_type { NOT_A_PARTITION= 0, RANGE_PARTITION, HASH_PARTITION, - LIST_PARTITION + LIST_PARTITION, + VERSIONING_PARTITION }; enum partition_state { @@ -89,6 +90,37 @@ typedef struct p_elem_val struct st_ddl_log_memory_entry; +class Stat_timestampf : public Sql_alloc +{ + static const uint buf_size= 4 + (TIME_SECOND_PART_DIGITS + 1) / 2; + uchar min_buf[buf_size]; + uchar max_buf[buf_size]; + Field_timestampf min_value; + Field_timestampf max_value; + +public: + Stat_timestampf(const char *field_name, TABLE_SHARE *share) : + min_value(min_buf, NULL, 0, Field::NONE, field_name, share, 6), + max_value(max_buf, NULL, 0, Field::NONE, field_name, share, 6) + { + min_value.set_max(); + memset(max_buf, 0, buf_size); + } + void update(Field *from) + { + from->update_min(&min_value, false); + from->update_max(&max_value, false); + } + my_time_t min_time() + { + return min_value.get_timestamp(); + } + my_time_t max_time() + { + return max_value.get_timestamp(); + } +}; + class partition_element :public Sql_alloc { public: List subpartitions; @@ -109,6 +141,17 @@ public: bool has_null_value; bool signed_flag; // Range value signed bool max_value; // MAXVALUE range + uint32 id; + + enum elem_type + { + CONVENTIONAL= 0, + AS_OF_NOW, + VERSIONING + }; + + elem_type type; + Stat_timestampf *stat_trx_end; partition_element() : part_max_rows(0), part_min_rows(0), range_value(0), @@ -117,7 +160,10 @@ public: data_file_name(NULL), index_file_name(NULL), engine_type(NULL), connect_string(null_lex_str), part_state(PART_NORMAL), nodegroup_id(UNDEF_NODEGROUP), has_null_value(FALSE), - signed_flag(FALSE), max_value(FALSE) + signed_flag(FALSE), max_value(FALSE), + id(UINT32_MAX), + type(CONVENTIONAL), + stat_trx_end(NULL) { } partition_element(partition_element *part_elem) @@ -132,7 +178,10 @@ public: connect_string(null_lex_str), part_state(part_elem->part_state), nodegroup_id(part_elem->nodegroup_id), - has_null_value(FALSE) + has_null_value(FALSE), + id(part_elem->id), + type(part_elem->type), + stat_trx_end(NULL) { } ~partition_element() {} diff --git a/sql/partition_info.cc b/sql/partition_info.cc index 24506434a76..e83fced4495 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -30,6 +30,7 @@ #include "sql_parse.h" #include "sql_acl.h" // *_ACL #include "sql_base.h" // fill_record +#include "sql_statistics.h" // vers_stat_end #ifdef WITH_PARTITION_STORAGE_ENGINE #include "ha_partition.h" @@ -114,6 +115,19 @@ partition_info *partition_info::get_clone(THD *thd) part_clone->list_val_list.push_back(new_val, mem_root); } } + if (part_type == VERSIONING_PARTITION && vers_info) + { + // clone Vers_part_info; set now_part, hist_part + clone->vers_info= new (mem_root) Vers_part_info(*vers_info); + List_iterator it(clone->partitions); + while ((part= it++)) + { + if (vers_info->now_part && part->id == vers_info->now_part->id) + clone->vers_info->now_part= part; + else if (vers_info->hist_part && part->id == vers_info->hist_part->id) + clone->vers_info->hist_part= part; + } // while ((part= it++)) + } // if (part_type == VERSIONING_PARTITION ... DBUG_RETURN(clone); } @@ -777,6 +791,216 @@ bool partition_info::has_unique_name(partition_element *element) DBUG_RETURN(TRUE); } +bool partition_info::vers_init_info(THD * thd) +{ + part_type= VERSIONING_PARTITION; + list_of_part_fields= TRUE; + column_list= TRUE; + vers_info= new (thd->mem_root) Vers_part_info; + if (!vers_info) + { + mem_alloc_error(sizeof(Vers_part_info)); + return true; + } + return false; +} + +bool partition_info::vers_set_interval(const INTERVAL & i) +{ + if (i.neg) + { + my_error(ER_VERS_WRONG_PARAMS, MYF(0), "BY SYSTEM_TIME", "negative INTERVAL"); + return true; + } + if (i.second_part) + { + my_error(ER_VERS_WRONG_PARAMS, MYF(0), "BY SYSTEM_TIME", "second fractions in INTERVAL"); + return true; + } + + DBUG_ASSERT(vers_info); + + // TODO: INTERVAL conversion to seconds leads to mismatch with calendar intervals (MONTH and YEAR) + vers_info->interval= + i.second + + i.minute * 60 + + i.hour * 60 * 60 + + i.day * 24 * 60 * 60 + + i.month * 30 * 24 * 60 * 60 + + i.year * 365 * 30 * 24 * 60 * 60; + + if (vers_info->interval == 0) + { + my_error(ER_VERS_WRONG_PARAMS, MYF(0), "BY SYSTEM_TIME", "zero INTERVAL"); + return true; + } + return false; +} + +bool partition_info::vers_set_limit(ulonglong limit) +{ + if (limit < 1) + { + my_error(ER_VERS_WRONG_PARAMS, MYF(0), "BY SYSTEM_TIME", "non-positive LIMIT"); + return true; + } + DBUG_ASSERT(vers_info); + + vers_info->limit= limit; + return false; +} + +partition_element* +partition_info::vers_part_rotate(THD * thd) +{ + DBUG_ASSERT(table && table->s); + if (table->s->free_parts.is_empty()) + { + push_warning_printf(thd, + Sql_condition::WARN_LEVEL_WARN, + WARN_VERS_PART_FULL, + ER_THD(thd, WARN_VERS_PART_FULL), + vers_info->hist_part->partition_name); + return vers_info->hist_part; + } + + table->s->vers_part_rotate(); + DBUG_ASSERT(table->s->hist_part_id < num_parts); + const char* old_part_name= vers_info->hist_part->partition_name; + vers_hist_part(); + + push_warning_printf(thd, + Sql_condition::WARN_LEVEL_NOTE, + WARN_VERS_PART_ROTATION, + ER_THD(thd, WARN_VERS_PART_ROTATION), + old_part_name, + vers_info->hist_part->partition_name); + + return vers_info->hist_part; +} + +bool partition_info::vers_setup_1(THD * thd) +{ + DBUG_ASSERT(part_type == VERSIONING_PARTITION); + if (!table->versioned()) + { + my_error(ER_VERSIONING_REQUIRED, MYF(0), "`BY SYSTEM_TIME` partitioning"); + return true; + } + Field *sys_trx_end= table->vers_end_field(); + part_field_list.empty(); + part_field_list.push_back(const_cast(sys_trx_end->field_name), thd->mem_root); + sys_trx_end->flags|= GET_FIXED_FIELDS_FLAG; // needed in handle_list_of_fields() + return false; +} + + +// scan table for min/max sys_trx_end +inline +bool partition_info::vers_scan_min_max(THD *thd, partition_element *part) +{ + uint32 sub_factor= num_subparts ? num_subparts : 1; + uint32 part_id= part->id * sub_factor; + uint32 part_id_end= part_id + sub_factor; + for (; part_id < part_id_end; ++part_id) + { + handler *file= table->file->part_handler(part_id); + int rc= file->ha_external_lock(thd, F_RDLCK); + if (rc) + goto error; + rc= file->ha_rnd_init(true); + if (!rc) + { + while ((rc= file->ha_rnd_next(table->record[0])) != HA_ERR_END_OF_FILE) + { + if (thd->killed) + { + file->ha_rnd_end(); + return true; + } + if (rc) + { + if (rc == HA_ERR_RECORD_DELETED) + continue; + break; + } + part->stat_trx_end->update(table->vers_end_field()); + } + file->ha_rnd_end(); + } + file->ha_external_lock(thd, F_UNLCK); + if (rc != HA_ERR_END_OF_FILE) + { + error: + my_error(ER_INTERNAL_ERROR, MYF(0), "partition/subpartition scan failed in versioned partitions setup"); + return true; + } + } + return false; +} + + +// setup at open stage (TABLE_SHARE is initialized) +bool partition_info::vers_setup_2(THD * thd, bool is_create_table_ind) +{ + DBUG_ASSERT(part_type == VERSIONING_PARTITION); + DBUG_ASSERT(vers_info && vers_info->initialized(is_create_table_ind) && vers_info->hist_default != UINT32_MAX); + DBUG_ASSERT(table && table->s); + if (!table->versioned_by_sql()) + { + my_error(ER_VERS_WRONG_PARAMS, MYF(0), table->s->table_name.str, "selected engine is not supported in `BY SYSTEM_TIME` partitioning"); + return true; + } + mysql_mutex_lock(&table->s->LOCK_rotation); + if (table->s->busy_rotation) + { + table->s->vers_wait_rotation(); + vers_hist_part(); + } + else + { + table->s->busy_rotation= true; + mysql_mutex_unlock(&table->s->LOCK_rotation); + // build freelist, scan min/max, assign hist_part + List_iterator it(partitions); + partition_element *el; + while ((el= it++)) + { + DBUG_ASSERT(el->type != partition_element::CONVENTIONAL); + if (el->type == partition_element::VERSIONING) + { + DBUG_ASSERT(!el->stat_trx_end); + el->stat_trx_end= new (&table->mem_root) + Stat_timestampf(table->s->vers_end_field()->field_name, table->s); + if (!is_create_table_ind && vers_scan_min_max(thd, el)) + return true; + } + if (el == vers_info->now_part || el == vers_info->hist_part) + continue; + if (!vers_info->hist_part && el->id == vers_info->hist_default) + { + vers_info->hist_part= el; + } + if (is_create_table_ind || ( + table->s->free_parts_init && + !vers_limit_exceed(el) && + !vers_interval_exceed(el))) + { + table->s->free_parts.push_back((void *) el->id, &table->s->mem_root); + } + } + table->s->hist_part_id= vers_info->hist_part->id; + if (!is_create_table_ind && (vers_limit_exceed() || vers_interval_exceed())) + vers_part_rotate(thd); + table->s->free_parts_init= false; + mysql_mutex_lock(&table->s->LOCK_rotation); + mysql_cond_broadcast(&table->s->COND_rotation); + table->s->busy_rotation= false; + } + mysql_mutex_unlock(&table->s->LOCK_rotation); + return false; +} + /* Check that the partition/subpartition is setup to use the correct @@ -1383,6 +1607,9 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type, uint i, tot_partitions; bool result= TRUE, table_engine_set; char *same_name; + uint32 hist_parts= 0; + uint32 now_parts= 0; + const char* hist_default= NULL; DBUG_ENTER("partition_info::check_partition_info"); DBUG_ASSERT(default_engine_type != partition_hton); @@ -1424,7 +1651,8 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type, } if (unlikely(is_sub_partitioned() && (!(part_type == RANGE_PARTITION || - part_type == LIST_PARTITION)))) + part_type == LIST_PARTITION || + part_type == VERSIONING_PARTITION)))) { /* Only RANGE and LIST partitioning can be subpartitioned */ my_error(ER_SUBPARTITION_ERROR, MYF(0)); @@ -1486,6 +1714,23 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type, my_error(ER_SAME_NAME_PARTITION, MYF(0), same_name); goto end; } + + if (part_type == VERSIONING_PARTITION) + { + if (num_parts < 2) + { + my_error(ER_VERS_WRONG_PARAMS, MYF(0), "BY SYSTEM_TIME", "unexpected number of partitions (expected > 1)"); + goto end; + } + DBUG_ASSERT(vers_info); + if (!vers_info->now_part) + { + my_error(ER_VERS_WRONG_PARAMS, MYF(0), "BY SYSTEM_TIME", "no `AS OF NOW` partition defined"); + goto end; + } + DBUG_ASSERT(vers_info->initialized(false)); + DBUG_ASSERT(num_parts == partitions.elements); + } i= 0; { List_iterator part_it(partitions); @@ -1566,6 +1811,25 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type, } } } + if (part_type == VERSIONING_PARTITION) + { + if (part_elem->type == partition_element::VERSIONING) + { + hist_parts++; + if (vers_info->hist_default == UINT32_MAX) + { + vers_info->hist_default= part_elem->id; + hist_default= part_elem->partition_name; + } + if (vers_info->hist_default == part_elem->id) + vers_info->hist_part= part_elem; + } + else + { + DBUG_ASSERT(part_elem->type == partition_element::AS_OF_NOW); + now_parts++; + } + } } while (++i < num_parts); if (!table_engine_set && num_parts_not_set != 0 && @@ -1603,6 +1867,31 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type, check_list_constants(thd)))) goto end; } + + if (hist_parts > 1) + { + if (vers_info->limit == 0 && vers_info->interval == 0) + { + push_warning_printf(thd, + Sql_condition::WARN_LEVEL_WARN, + WARN_VERS_PARAMETERS, + ER_THD(thd, WARN_VERS_PARAMETERS), + "no rotation condition for multiple `VERSIONING` partitions."); + } + if (hist_default) + { + push_warning_printf(thd, + Sql_condition::WARN_LEVEL_WARN, + WARN_VERS_PARAMETERS, + "No `DEFAULT` for `VERSIONING` partitions. Setting `%s` as default.", + hist_default); + } + } + if (now_parts > 1) + { + my_error(ER_VERS_WRONG_PARAMS, MYF(0), "BY SYSTEM_TIME", "multiple `AS OF NOW` partitions"); + goto end; + } result= FALSE; end: DBUG_RETURN(result); diff --git a/sql/partition_info.h b/sql/partition_info.h index d4c180ddcdd..844956594ff 100644 --- a/sql/partition_info.h +++ b/sql/partition_info.h @@ -34,6 +34,46 @@ typedef int (*get_subpart_id_func)(partition_info *part_info, struct st_ddl_log_memory_entry; +struct Vers_part_info : public Sql_alloc +{ + Vers_part_info() : + interval(0), + limit(0), + now_part(NULL), + hist_part(NULL), + hist_default(UINT32_MAX) + { + } + Vers_part_info(Vers_part_info &src) : + interval(src.interval), + limit(src.limit), + now_part(NULL), + hist_part(NULL), + hist_default(src.hist_default) + { + } + bool initialized(bool fully= true) + { + if (now_part) + { + DBUG_ASSERT( + now_part->id != UINT32_MAX && + now_part->type == partition_element::AS_OF_NOW && + (fully ? (bool) hist_part : true) && + (!hist_part || ( + hist_part->id != UINT32_MAX && + hist_part->type == partition_element::VERSIONING))); + return true; + } + return false; + } + my_time_t interval; + ulonglong limit; + partition_element *now_part; + partition_element *hist_part; + uint32 hist_default; +}; + class partition_info : public Sql_alloc { public: @@ -142,6 +182,7 @@ public: LIST_PART_ENTRY *list_array; part_column_list_val *range_col_array; part_column_list_val *list_col_array; + Vers_part_info *vers_info; }; /******************************************** @@ -354,6 +395,74 @@ private: bool add_named_partition(const char *part_name, uint length); public: bool has_unique_name(partition_element *element); + + bool vers_init_info(THD *thd); + bool vers_set_interval(const INTERVAL &i); + bool vers_set_limit(ulonglong limit); + partition_element* vers_part_rotate(THD *thd); + bool vers_setup_1(THD *thd); + bool vers_setup_2(THD *thd, bool is_create_table_ind); + bool vers_scan_min_max(THD *thd, partition_element *part); + + partition_element *vers_hist_part() + { + DBUG_ASSERT(table && table->s); + DBUG_ASSERT(vers_info && vers_info->initialized()); + DBUG_ASSERT(table->s->hist_part_id != UINT32_MAX); + if (table->s->hist_part_id == vers_info->hist_part->id) + return vers_info->hist_part; + + List_iterator it(partitions); + partition_element *el; + while ((el= it++)) + { + DBUG_ASSERT(el->type != partition_element::CONVENTIONAL); + if (el->type == partition_element::VERSIONING && + el->id == table->s->hist_part_id) + { + vers_info->hist_part= el; + return vers_info->hist_part; + } + } + DBUG_ASSERT(0); + return NULL; + } + bool vers_limit_exceed(partition_element *part= NULL) + { + DBUG_ASSERT(vers_info); + if (!vers_info->limit) + return false; + if (!part) + { + DBUG_ASSERT(vers_info->initialized()); + part= vers_hist_part(); + } + // TODO: cache thread-shared part_recs and increment on INSERT + return table->file->part_recs_slow(part) >= vers_info->limit; + } + bool vers_interval_exceed(my_time_t max_time, partition_element *part= NULL) + { + DBUG_ASSERT(vers_info); + if (!vers_info->interval) + return false; + if (!part) + { + DBUG_ASSERT(vers_info->initialized()); + part= vers_hist_part(); + } + DBUG_ASSERT(part->stat_trx_end); + max_time-= part->stat_trx_end->min_time(); + return max_time > vers_info->interval; + } + bool vers_interval_exceed(partition_element *part) + { + DBUG_ASSERT(part->stat_trx_end); + return vers_interval_exceed(part->stat_trx_end->max_time(), part); + } + bool vers_interval_exceed() + { + return vers_interval_exceed(vers_hist_part()); + } }; uint32 get_next_partition_id_range(struct st_partition_iter* part_iter); diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index f37070d9394..c25b8b655cd 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -5716,9 +5716,9 @@ ER_TOO_MANY_PARTITIONS_ERROR ger "Es wurden zu vielen Partitionen (einschließlich Unterpartitionen) definiert" swe "För mÃ¥nga partitioner (inkluderande subpartitioner) definierades" ER_SUBPARTITION_ERROR - eng "It is only possible to mix RANGE/LIST partitioning with HASH/KEY partitioning for subpartitioning" - ger "RANGE/LIST-Partitionierung kann bei Unterpartitionen nur zusammen mit HASH/KEY-Partitionierung verwendet werden" - swe "Det är endast möjligt att blanda RANGE/LIST partitionering med HASH/KEY partitionering för subpartitionering" + eng "It is only possible to mix RANGE/LIST/SYSTEM_TIME partitioning with HASH/KEY partitioning for subpartitioning" + ger "RANGE/LIST-Partitionierung kann bei Unterpartitionen nur zusammen mit HASH/KEY/SYSTEM_TIME-Partitionierung verwendet werden" + swe "Det är endast möjligt att blanda RANGE/LIST/SYSTEM_TIME partitionering med HASH/KEY partitionering för subpartitionering" ER_CANT_CREATE_HANDLER_FILE eng "Failed to create specific handler file" ger "Erzeugen einer spezifischen Handler-Datei fehlgeschlagen" @@ -7500,7 +7500,7 @@ ER_VERS_FIELD_WRONG_TYPE eng "%`s must be of type %`s for versioned table %`s" ER_VERS_WRONG_PARAMS - eng "Wrong parameters for versioned table %`s: %s" + eng "Wrong parameters for %`s: %s" ER_VERS_ENGINE_UNSUPPORTED eng "Engine does not support System Versioning for %`s" @@ -7510,3 +7510,15 @@ ER_VERS_RANGE_UNITS_MISMATCH ER_NON_VERSIONED_FIELD_IN_VERSIONED_QUERY eng "Attempt to read unversioned field %`s in historical query" + +ER_PARTITION_WRONG_TYPE + eng "Wrong partition type, expected type: %`s" + +WARN_VERS_PART_FULL + eng "Using full partition %`s, need more VERSIONING partitions!" + +WARN_VERS_PARAMETERS + eng "Maybe missing parameters: %s" + +WARN_VERS_PART_ROTATION + eng "Switching from partition %`s to %`s" diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 5af9b326eba..775d0c0d023 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -221,7 +221,8 @@ int TABLE::delete_row() else { store_record(this, record[1]); - vers_end_field()->set_time(); + Field *sys_trx_end= vers_end_field(); + sys_trx_end->set_time(); error= file->ha_update_row(record[1], record[0]); } return error; diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 4e71e792a08..816cd374e67 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -87,7 +87,8 @@ const LEX_STRING partition_keywords[]= { { C_STRING_WITH_LEN("HASH") }, { C_STRING_WITH_LEN("RANGE") }, - { C_STRING_WITH_LEN("LIST") }, + { C_STRING_WITH_LEN("LIST") }, + { C_STRING_WITH_LEN("SYSTEM_TIME") }, { C_STRING_WITH_LEN("KEY") }, { C_STRING_WITH_LEN("MAXVALUE") }, { C_STRING_WITH_LEN("LINEAR ") }, @@ -108,6 +109,7 @@ static int get_partition_id_list_col(partition_info *, uint32 *, longlong *); static int get_partition_id_list(partition_info *, uint32 *, longlong *); static int get_partition_id_range_col(partition_info *, uint32 *, longlong *); static int get_partition_id_range(partition_info *, uint32 *, longlong *); +static int vers_get_partition_id(partition_info *, uint32 *, longlong *); static int get_part_id_charset_func_part(partition_info *, uint32 *, longlong *); static int get_part_id_charset_func_subpart(partition_info *, uint32 *); static int get_partition_id_hash_nosub(partition_info *, uint32 *, longlong *); @@ -1318,6 +1320,24 @@ static void set_up_partition_func_pointers(partition_info *part_info) part_info->get_subpartition_id= get_partition_id_hash_sub; } } + else if (part_info->part_type == VERSIONING_PARTITION) + { + part_info->get_part_partition_id= vers_get_partition_id; + if (part_info->list_of_subpart_fields) + { + if (part_info->linear_hash_ind) + part_info->get_subpartition_id= get_partition_id_linear_key_sub; + else + part_info->get_subpartition_id= get_partition_id_key_sub; + } + else + { + if (part_info->linear_hash_ind) + part_info->get_subpartition_id= get_partition_id_linear_hash_sub; + else + part_info->get_subpartition_id= get_partition_id_hash_sub; + } + } else /* LIST Partitioning */ { if (part_info->column_list) @@ -1358,6 +1378,10 @@ static void set_up_partition_func_pointers(partition_info *part_info) else part_info->get_partition_id= get_partition_id_list; } + else if (part_info->part_type == VERSIONING_PARTITION) + { + part_info->get_partition_id= vers_get_partition_id; + } else /* HASH partitioning */ { if (part_info->list_of_part_fields) @@ -1630,6 +1654,7 @@ bool fix_partition_func(THD *thd, TABLE *table, } } DBUG_ASSERT(part_info->part_type != NOT_A_PARTITION); + DBUG_ASSERT(part_info->part_type != VERSIONING_PARTITION || part_info->column_list); /* Partition is defined. We need to verify that partitioning function is correct. @@ -1662,6 +1687,9 @@ bool fix_partition_func(THD *thd, TABLE *table, const char *error_str; if (part_info->column_list) { + if (part_info->part_type == VERSIONING_PARTITION && + part_info->vers_setup_1(thd)) + goto end; List_iterator it(part_info->part_field_list); if (unlikely(handle_list_of_fields(thd, it, table, part_info, FALSE))) goto end; @@ -1685,6 +1713,10 @@ bool fix_partition_func(THD *thd, TABLE *table, if (unlikely(part_info->check_list_constants(thd))) goto end; } + else if (part_info->part_type == VERSIONING_PARTITION) + { + error_str= partition_keywords[PKW_SYSTEM_TIME].str; + } else { DBUG_ASSERT(0); @@ -2355,6 +2387,23 @@ static int add_partition_values(File fptr, partition_info *part_info, } while (++i < num_items); err+= add_end_parenthesis(fptr); } + else if (part_info->part_type == VERSIONING_PARTITION) + { + switch (p_elem->type) + { + case partition_element::AS_OF_NOW: + err+= add_string(fptr, " AS OF NOW"); + break; + case partition_element::VERSIONING: + err+= add_string(fptr, " VERSIONING"); + DBUG_ASSERT(part_info->vers_info); + if (part_info->vers_info->hist_default == p_elem->id) + err+= add_string(fptr, " DEFAULT"); + break; + default: + DBUG_ASSERT(0 && "wrong p_elem->type"); + } + } end: return err; } @@ -2497,13 +2546,32 @@ char *generate_partition_syntax(THD *thd, partition_info *part_info, else err+= add_part_key_word(fptr, partition_keywords[PKW_HASH].str); break; + case VERSIONING_PARTITION: + err+= add_part_key_word(fptr, partition_keywords[PKW_SYSTEM_TIME].str); + break; default: DBUG_ASSERT(0); /* We really shouldn't get here, no use in continuing from here */ my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR)); DBUG_RETURN(NULL); } - if (part_info->part_expr) + if (part_info->part_type == VERSIONING_PARTITION) + { + Vers_part_info *vers_info= part_info->vers_info; + DBUG_ASSERT(vers_info); + if (vers_info->interval) + { + err+= add_string(fptr, "INTERVAL "); + err+= add_int(fptr, vers_info->interval); + err+= add_string(fptr, " SECOND "); + } + if (vers_info->limit) + { + err+= add_string(fptr, "LIMIT "); + err+= add_int(fptr, vers_info->limit); + } + } + else if (part_info->part_expr) { err+= add_begin_parenthesis(fptr); err+= add_string_len(fptr, part_info->part_func_string, @@ -3350,6 +3418,73 @@ int get_partition_id_range_col(partition_info *part_info, } +int vers_get_partition_id(partition_info *part_info, + uint32 *part_id, + longlong *func_value) +{ + DBUG_ENTER("vers_get_partition_id"); + Field *sys_trx_end= part_info->part_field_array[0]; + DBUG_ASSERT(sys_trx_end); + DBUG_ASSERT(part_info->table); + Vers_part_info *vers_info= part_info->vers_info; + DBUG_ASSERT(vers_info && vers_info->initialized()); + DBUG_ASSERT(sys_trx_end->table == part_info->table && part_info->table->versioned()); + DBUG_ASSERT(part_info->table->vers_end_field() == sys_trx_end); + + // new rows have NULL in sys_trx_end + if (sys_trx_end->is_max() || sys_trx_end->is_null()) + { + *part_id= vers_info->now_part->id; + } + else // row is historical + { + partition_element *part= vers_info->hist_part; + THD *thd= current_thd; + TABLE *table= part_info->table; + + switch (thd->lex->sql_command) + { + case SQLCOM_DELETE: + case SQLCOM_DELETE_MULTI: + case SQLCOM_UPDATE: + case SQLCOM_UPDATE_MULTI: + case SQLCOM_ALTER_TABLE: + mysql_mutex_lock(&table->s->LOCK_rotation); + if (table->s->busy_rotation) + { + table->s->vers_wait_rotation(); + part_info->vers_hist_part(); + } + else + { + table->s->busy_rotation= true; + mysql_mutex_unlock(&table->s->LOCK_rotation); + if (part_info->vers_limit_exceed() || part_info->vers_interval_exceed(sys_trx_end->get_timestamp())) + { + part= part_info->vers_part_rotate(thd); + } + mysql_mutex_lock(&table->s->LOCK_rotation); + mysql_cond_broadcast(&table->s->COND_rotation); + table->s->busy_rotation= false; + } + mysql_mutex_unlock(&table->s->LOCK_rotation); + if (vers_info->interval) + { + DBUG_ASSERT(part->stat_trx_end); + part->stat_trx_end->update(sys_trx_end); + } + break; + default: + ; + } + *part_id= vers_info->hist_part->id; + } + + DBUG_PRINT("exit",("partition: %d", *part_id)); + DBUG_RETURN(0); +} + + int get_partition_id_range(partition_info *part_info, uint32 *part_id, longlong *func_value) @@ -4954,7 +5089,8 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, must know the number of new partitions in this case. */ if (thd->lex->no_write_to_binlog && - tab_part_info->part_type != HASH_PARTITION) + tab_part_info->part_type != HASH_PARTITION && + tab_part_info->part_type != VERSIONING_PARTITION) { my_error(ER_NO_BINLOG_ERROR, MYF(0)); goto err; @@ -5206,16 +5342,27 @@ that are reorganised. List_iterator part_it(tab_part_info->partitions); tab_part_info->is_auto_partitioned= FALSE; - if (!(tab_part_info->part_type == RANGE_PARTITION || - tab_part_info->part_type == LIST_PARTITION)) + if (tab_part_info->part_type == VERSIONING_PARTITION) { - my_error(ER_ONLY_ON_RANGE_LIST_PARTITION, MYF(0), "DROP"); - goto err; + if (num_parts_dropped >= tab_part_info->num_parts - 1) + { + my_error(ER_VERS_WRONG_PARAMS, MYF(0), "BY SYSTEM_TIME", "one `AS OF NOW` and at least one `VERSIONING` partition required"); + goto err; + } } - if (num_parts_dropped >= tab_part_info->num_parts) + else { - my_error(ER_DROP_LAST_PARTITION, MYF(0)); - goto err; + if (!(tab_part_info->part_type == RANGE_PARTITION || + tab_part_info->part_type == LIST_PARTITION)) + { + my_error(ER_ONLY_ON_RANGE_LIST_PARTITION, MYF(0), "DROP"); + goto err; + } + if (num_parts_dropped >= tab_part_info->num_parts) + { + my_error(ER_DROP_LAST_PARTITION, MYF(0)); + goto err; + } } do { @@ -5223,6 +5370,11 @@ that are reorganised. if (is_name_in_list(part_elem->partition_name, alter_info->partition_names)) { + if (part_elem->type == partition_element::AS_OF_NOW) + { + my_error(ER_VERS_WRONG_PARAMS, MYF(0), "BY SYSTEM_TIME", "`AS OF NOW` partition can not be dropped"); + goto err; + } /* Set state to indicate that the partition is to be dropped. */ diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 5a640ccb613..93836c82085 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -7030,6 +7030,11 @@ static int get_schema_partitions_record(THD *thd, TABLE_LIST *tables, partition_keywords[PKW_HASH].length); table->field[7]->store(tmp_res.ptr(), tmp_res.length(), cs); break; + case VERSIONING_PARTITION: + tmp_res.length(0); + tmp_res.append(partition_keywords[PKW_SYSTEM_TIME].str, + partition_keywords[PKW_SYSTEM_TIME].length); + break; default: DBUG_ASSERT(0); my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR)); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index f91568e9fb1..f591566d91a 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1560,7 +1560,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token VARIANCE_SYM %token VARYING /* SQL-2003-R */ %token VAR_SAMP_SYM -%token VERSIONING /* 32N2439 */ +%token VERSIONING_SYM /* 32N2439 */ %token VIA_SYM %token VIEW_SYM /* SQL-2003-N */ %token VIRTUAL_SYM @@ -4910,6 +4910,10 @@ part_type_def: { Lex->part_info->part_type= LIST_PARTITION; } | LIST_SYM part_column_list { Lex->part_info->part_type= LIST_PARTITION; } + | SYSTEM_TIME_SYM + { if (Lex->part_info->vers_init_info(thd)) MYSQL_YYABORT; } + opt_versioning_interval + opt_versioning_limit ; opt_linear: @@ -5117,6 +5121,7 @@ part_definition: MYSQL_YYABORT; } p_elem->part_state= PART_NORMAL; + p_elem->id= part_info->partitions.elements - 1; part_info->curr_part_elem= p_elem; part_info->current_partition= p_elem; part_info->use_default_partitions= FALSE; @@ -5185,6 +5190,42 @@ opt_part_values: part_info->part_type= LIST_PARTITION; } part_values_in {} + | AS OF_SYM NOW_SYM + { + LEX *lex= Lex; + partition_info *part_info= lex->part_info; + if (! lex->is_partition_management()) + { + if (part_info->part_type != VERSIONING_PARTITION) + my_yyabort_error((ER_PARTITION_WRONG_TYPE, MYF(0), + "BY SYSTEM_TIME")); + } + else + { + part_info->vers_init_info(thd); + } + partition_element *elem= part_info->curr_part_elem; + elem->type= partition_element::AS_OF_NOW; + DBUG_ASSERT(part_info->vers_info); + part_info->vers_info->now_part= elem; + } + | VERSIONING_SYM + { + LEX *lex= Lex; + partition_info *part_info= lex->part_info; + if (! lex->is_partition_management()) + { + if (part_info->part_type != VERSIONING_PARTITION) + my_yyabort_error((ER_PARTITION_WRONG_TYPE, MYF(0), + "BY SYSTEM_TIME")); + } + else + { + part_info->vers_init_info(thd); + } + part_info->curr_part_elem->type= partition_element::VERSIONING; + } + opt_default_hist_part | DEFAULT { LEX *lex= Lex; @@ -5430,6 +5471,7 @@ sub_part_definition: mem_alloc_error(sizeof(partition_element)); MYSQL_YYABORT; } + sub_p_elem->id= curr_part->subpartitions.elements - 1; part_info->curr_part_elem= sub_p_elem; part_info->use_default_subpartitions= FALSE; part_info->use_default_num_subpartitions= FALSE; @@ -5486,6 +5528,45 @@ opt_part_option: { Lex->part_info->curr_part_elem->part_comment= $3.str; } ; +opt_versioning_interval: + /* empty */ {} + | INTERVAL_SYM expr interval + { + partition_info *part_info= Lex->part_info; + DBUG_ASSERT(part_info->part_type == VERSIONING_PARTITION); + INTERVAL interval; + if (get_interval_value($2, $3, &interval)) + my_yyabort_error((ER_VERS_WRONG_PARAMS, MYF(0), "BY SYSTEM_TIME", "wrong INTERVAL value")); + if (part_info->vers_set_interval(interval)) + MYSQL_YYABORT; + } + ; + +opt_versioning_limit: + /* empty */ {} + | LIMIT ulonglong_num + { + partition_info *part_info= Lex->part_info; + DBUG_ASSERT(part_info->part_type == VERSIONING_PARTITION); + if (part_info->vers_set_limit($2)) + MYSQL_YYABORT; + } + ; + +opt_default_hist_part: + /* empty */ {} + | DEFAULT + { + partition_info *part_info= Lex->part_info; + DBUG_ASSERT(part_info && part_info->vers_info && part_info->curr_part_elem); + if (part_info->vers_info->hist_part) + my_yyabort_error((ER_VERS_WRONG_PARAMS, MYF(0), + "BY SYSTEM_TIME", "multiple `DEFAULT` partitions")); + part_info->vers_info->hist_part= part_info->curr_part_elem; + part_info->vers_info->hist_default= part_info->curr_part_elem->id; + } + ; + /* End of partition parser part */ @@ -5853,7 +5934,7 @@ create_table_option: Lex->create_info.used_fields|= HA_CREATE_USED_SEQUENCE; Lex->create_info.sequence= $3; } - | WITH_SYSTEM_SYM VERSIONING + | WITH_SYSTEM_SYM VERSIONING_SYM { const char *table_name= Lex->create_last_non_select_table->table_name; @@ -5867,7 +5948,7 @@ create_table_option: info.declared_with_system_versioning= true; Lex->create_info.options|= HA_VERSIONED_TABLE; } - | WITHOUT SYSTEM VERSIONING + | WITHOUT SYSTEM VERSIONING_SYM { const char *table_name= Lex->create_last_non_select_table->table_name; @@ -6086,10 +6167,9 @@ period_for_system_time: Vers_parse_info &info= Lex->vers_get_info(); if (!my_strcasecmp(system_charset_info, $4->c_ptr(), $6->c_ptr())) { - my_error(ER_VERS_WRONG_PARAMS, MYF(0), + my_yyabort_error((ER_VERS_WRONG_PARAMS, MYF(0), Lex->create_last_non_select_table->table_name, - "'PERIOD FOR SYSTEM_TIME' columns must be different"); - MYSQL_YYABORT; + "'PERIOD FOR SYSTEM_TIME' columns must be different")); } info.set_period_for_system_time($4, $6); } @@ -6221,8 +6301,7 @@ field_def: } if (*p) { - my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, err); - MYSQL_YYABORT; + my_yyabort_error((ER_VERS_WRONG_PARAMS, MYF(0), table_name, err)); } *p= field_name; } @@ -6698,7 +6777,7 @@ serial_attribute: new (thd->mem_root) engine_option_value($1, &Lex->last_field->option_list, &Lex->option_list_last); } - | WITH_SYSTEM_SYM VERSIONING + | WITH_SYSTEM_SYM VERSIONING_SYM { if (Lex->last_field->versioning != Column_definition::VERSIONING_NOT_SET) @@ -6711,7 +6790,7 @@ serial_attribute: Lex->create_info.vers_info.has_versioned_fields= true; Lex->create_info.options|= HA_VERSIONED_TABLE; } - | WITHOUT SYSTEM VERSIONING + | WITHOUT SYSTEM VERSIONING_SYM { if (Lex->last_field->versioning != Column_definition::VERSIONING_NOT_SET) diff --git a/sql/table.cc b/sql/table.cc index ad44f896621..ef8b3104165 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -426,6 +426,9 @@ void TABLE_SHARE::destroy() DBUG_ENTER("TABLE_SHARE::destroy"); DBUG_PRINT("info", ("db: %s table: %s", db.str, table_name.str)); + if (versioned) + vers_destroy(); + if (ha_share) { delete ha_share; @@ -2546,7 +2549,9 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, /* Set system versioning information. */ if (system_period == NULL) { - share->disable_system_versioning(); + versioned= false; + row_start_field = 0; + row_end_field = 0; } else { @@ -2555,9 +2560,11 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, uint16 row_end= uint2korr(system_period + sizeof(uint16)); if (row_start >= share->fields || row_end >= share->fields) goto err; - DBUG_PRINT("info", ("Columns with system versioning: [%d, %d]", row_start, - row_end)); - share->enable_system_versioning(row_start, row_end); + DBUG_PRINT("info", ("Columns with system versioning: [%d, %d]", row_start, row_end)); + versioned= true; + vers_init(); + row_start_field = row_start; + row_end_field = row_end; vers_start_field()->flags|= VERS_SYS_START_FLAG; vers_end_field()->flags|= VERS_SYS_END_FLAG; } // if (system_period == NULL) @@ -3403,6 +3410,17 @@ partititon_err: if (outparam->no_replicate || !binlog_filter->db_ok(outparam->s->db.str)) outparam->s->cached_row_logging_check= 0; // No row based replication +#ifdef WITH_PARTITION_STORAGE_ENGINE + if (outparam->part_info && + outparam->part_info->part_type == VERSIONING_PARTITION && + outparam->part_info->vers_setup_2(thd, is_create_table)) + { + error= OPEN_FRM_OPEN_ERROR; + error_reported= true; + goto err; + } +#endif + /* Increment the opened_tables counter, only when open flags set. */ if (db_stat) thd->status_var.opened_tables++; diff --git a/sql/table.h b/sql/table.h index b93bb3df5e3..fc7e639f162 100644 --- a/sql/table.h +++ b/sql/table.h @@ -561,6 +561,9 @@ struct TABLE_STATISTICS_CB bool histograms_are_read; }; +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif /** This structure is shared between different table objects. There is one @@ -748,19 +751,27 @@ struct TABLE_SHARE bool versioned; uint16 row_start_field; uint16 row_end_field; - - void enable_system_versioning(uint16 row_start, uint row_end) - { - versioned = true; - row_start_field = row_start; - row_end_field = row_end; + uint32 hist_part_id; + List free_parts; + bool free_parts_init; + bool busy_rotation; + mysql_mutex_t LOCK_rotation; + mysql_cond_t COND_rotation; + + void vers_init() + { + hist_part_id= UINT32_MAX; + busy_rotation= false; + free_parts.empty(); + free_parts_init= true; + mysql_mutex_init(key_TABLE_SHARE_LOCK_rotation, &LOCK_rotation, MY_MUTEX_INIT_FAST); + mysql_cond_init(key_TABLE_SHARE_COND_rotation, &COND_rotation, NULL); } - void disable_system_versioning() + void vers_destroy() { - versioned = false; - row_start_field = 0; - row_end_field = 0; + mysql_mutex_destroy(&LOCK_rotation); + mysql_cond_destroy(&COND_rotation); } Field *vers_start_field() @@ -773,6 +784,18 @@ struct TABLE_SHARE return field[row_end_field]; } + void vers_part_rotate() + { + DBUG_ASSERT(!free_parts.is_empty()); + hist_part_id= (ulong)(void *)(free_parts.pop()); + } + + void vers_wait_rotation() + { + while (busy_rotation) + mysql_cond_wait(&COND_rotation, &LOCK_rotation); + } + /** Cache the checked structure of this table. @@ -1495,8 +1518,8 @@ public: /* Versioned by SQL layer */ bool versioned_by_sql() const { - DBUG_ASSERT(s->db_type()); - return s->versioned && !s->db_type()->versioned(); + DBUG_ASSERT(file); + return s->versioned && !file->versioned(); } Field *vers_start_field() const -- cgit v1.2.1 From 41d9840850746aa250fb63d9c83c44e1a9042941 Mon Sep 17 00:00:00 2001 From: kevg Date: Thu, 15 Dec 2016 13:04:45 +0300 Subject: SQL: remove unneded return value --- sql/field.cc | 7 +++---- sql/field.h | 8 ++++---- sql/table.cc | 3 +-- 3 files changed, 8 insertions(+), 10 deletions(-) (limited to 'sql') diff --git a/sql/field.cc b/sql/field.cc index a83cbb20eff..3c34d973bf5 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -4356,12 +4356,11 @@ void Field_longlong::sql_type(String &res) const add_zerofill_and_unsigned(res); } -bool Field_longlong::set_max() +void Field_longlong::set_max() { ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; set_notnull(); int8store(ptr, unsigned_flag ? ULONGLONG_MAX : LONGLONG_MAX); - return FALSE; } bool Field_longlong::is_max() @@ -5444,7 +5443,7 @@ void Field_timestampf::store_TIME(my_time_t timestamp, ulong sec_part) my_timestamp_to_binary(&tm, ptr, dec); } -bool Field_timestampf::set_max() +void Field_timestampf::set_max() { DBUG_ENTER("Field_timestampf::set_max"); ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; @@ -5453,7 +5452,7 @@ bool Field_timestampf::set_max() mi_int4store(ptr, TIMESTAMP_MAX_VALUE); memset(ptr + 4, 0x0, value_length() - 4); - DBUG_RETURN(FALSE); + DBUG_VOID_RETURN; } bool Field_timestampf::is_max() diff --git a/sql/field.h b/sql/field.h index a6b1cace962..e955323b917 100644 --- a/sql/field.h +++ b/sql/field.h @@ -677,8 +677,8 @@ public: /** Used by System Versioning. */ - virtual bool set_max() - { DBUG_ASSERT(0); return false; } + virtual void set_max() + { DBUG_ASSERT(0); } virtual bool is_max() { DBUG_ASSERT(0); return false; } @@ -2129,7 +2129,7 @@ public: return unpack_int64(to, from, from_end); } - bool set_max(); + void set_max(); bool is_max(); }; @@ -2540,7 +2540,7 @@ public: { return memcmp(a_ptr, b_ptr, pack_length()); } - bool set_max(); + void set_max(); bool is_max(); void store_TIME(my_time_t timestamp, ulong sec_part); my_time_t get_timestamp(const uchar *pos= NULL, ulong *sec_part= NULL) const; diff --git a/sql/table.cc b/sql/table.cc index ef8b3104165..4b7d5ef0d6f 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -7602,8 +7602,7 @@ void TABLE::vers_update_fields() if (vers_start_field()->set_time()) DBUG_ASSERT(0); - if (vers_end_field()->set_max()) - DBUG_ASSERT(0); + vers_end_field()->set_max(); DBUG_VOID_RETURN; } -- cgit v1.2.1 From ef10ef98ab6e44737474a93daca91c22ae25fefa Mon Sep 17 00:00:00 2001 From: kevg Date: Fri, 16 Dec 2016 14:11:23 +0300 Subject: SQL: UPDATE on row-based replication [closes: #94] --- sql/ha_sequence.cc | 6 ++++-- sql/handler.cc | 32 +++++++++++++++----------------- 2 files changed, 19 insertions(+), 19 deletions(-) (limited to 'sql') diff --git a/sql/ha_sequence.cc b/sql/ha_sequence.cc index f3c4d8961a8..04df348ca6f 100644 --- a/sql/ha_sequence.cc +++ b/sql/ha_sequence.cc @@ -220,7 +220,8 @@ int ha_sequence::write_row(uchar *buf) sequence->copy(&tmp_seq); rows_changed++; /* We have to do the logging while we hold the sequence mutex */ - error= binlog_log_row(table, 0, buf, log_func); + if (table->file->check_table_binlog_row_based(1)) + error= binlog_log_row(table, 0, buf, log_func); row_already_logged= 1; } @@ -254,7 +255,8 @@ int ha_sequence::update_row(const uchar *old_data, uchar *new_data) sequence->copy(&tmp_seq); rows_changed++; /* We have to do the logging while we hold the sequence mutex */ - error= binlog_log_row(table, old_data, new_data, + if (table->file->check_table_binlog_row_based(1)) + error= binlog_log_row(table, old_data, new_data, Update_rows_log_event::binlog_row_logging_function); row_already_logged= 1; } diff --git a/sql/handler.cc b/sql/handler.cc index 62c628a01dc..b90ee5f6425 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -5795,10 +5795,10 @@ static int write_locked_table_maps(THD *thd) static int check_wsrep_max_ws_rows(); -static int binlog_log_row_internal(TABLE* table, - const uchar *before_record, - const uchar *after_record, - Log_func *log_func) +int binlog_log_row(TABLE* table, + const uchar *before_record, + const uchar *after_record, + Log_func *log_func) { bool error= 0; THD *const thd= table->in_use; @@ -5833,16 +5833,6 @@ static int binlog_log_row_internal(TABLE* table, return error ? HA_ERR_RBR_LOGGING_FAILED : 0; } -int binlog_log_row(TABLE* table, - const uchar *before_record, - const uchar *after_record, - Log_func *log_func) -{ - if (!table->file->check_table_binlog_row_based(1)) - return 0; - return binlog_log_row_internal(table, before_record, after_record, log_func); -} - int handler::ha_external_lock(THD *thd, int lock_type) { @@ -5991,7 +5981,8 @@ int handler::ha_write_row(uchar *buf) if (likely(!error) && !row_already_logged) { rows_changed++; - error= binlog_log_row(table, 0, buf, log_func); + if (table->file->check_table_binlog_row_based(1)) + error= binlog_log_row(table, 0, buf, log_func); } DEBUG_SYNC_C("ha_write_row_end"); DBUG_RETURN(error); @@ -6012,6 +6003,9 @@ int handler::ha_update_row(const uchar *old_data, uchar *new_data) DBUG_ASSERT(new_data == table->record[0]); DBUG_ASSERT(old_data == table->record[1]); + // InnoDB changes sys_trx_end to curr_trx_id and we need to restore MAX_TRX + if (table->file->check_table_binlog_row_based(1)) + memcpy(table->record[2], table->record[1], table->s->reclength); MYSQL_UPDATE_ROW_START(table_share->db.str, table_share->table_name.str); mark_trx_read_write(); increment_statistics(&SSV::ha_update_count); @@ -6023,7 +6017,10 @@ int handler::ha_update_row(const uchar *old_data, uchar *new_data) if (likely(!error) && !row_already_logged) { rows_changed++; - error= binlog_log_row(table, old_data, new_data, log_func); + if (table->file->check_table_binlog_row_based(1)) { + memcpy(table->record[1], table->record[2], table->s->reclength); + error= binlog_log_row(table, old_data, new_data, log_func); + } } return error; } @@ -6072,7 +6069,8 @@ int handler::ha_delete_row(const uchar *buf) if (likely(!error)) { rows_changed++; - error= binlog_log_row(table, buf, 0, log_func); + if (table->file->check_table_binlog_row_based(1)) + error= binlog_log_row(table, buf, 0, log_func); } return error; } -- cgit v1.2.1 From e45b85eb3e5bb905ee6f45dfb037fba1aa47266d Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Sun, 18 Dec 2016 17:06:43 +0000 Subject: SQL: replication from unversioned to versioned [fixes #94] --- sql/handler.cc | 10 ++++++---- sql/log_event.cc | 29 +++++++++++++++++++++++++---- 2 files changed, 31 insertions(+), 8 deletions(-) (limited to 'sql') diff --git a/sql/handler.cc b/sql/handler.cc index b90ee5f6425..0712d3e5798 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -6003,8 +6003,8 @@ int handler::ha_update_row(const uchar *old_data, uchar *new_data) DBUG_ASSERT(new_data == table->record[0]); DBUG_ASSERT(old_data == table->record[1]); - // InnoDB changes sys_trx_end to curr_trx_id and we need to restore MAX_TRX - if (table->file->check_table_binlog_row_based(1)) + // it is important to keep 'old_data' intact for versioning to work correctly on slave side + if (table->file->check_table_binlog_row_based(1) && table->versioned()) memcpy(table->record[2], table->record[1], table->s->reclength); MYSQL_UPDATE_ROW_START(table_share->db.str, table_share->table_name.str); mark_trx_read_write(); @@ -6017,8 +6017,10 @@ int handler::ha_update_row(const uchar *old_data, uchar *new_data) if (likely(!error) && !row_already_logged) { rows_changed++; - if (table->file->check_table_binlog_row_based(1)) { - memcpy(table->record[1], table->record[2], table->s->reclength); + if (table->file->check_table_binlog_row_based(1)) + { + if (table->versioned()) + memcpy(table->record[1], table->record[2], table->s->reclength); error= binlog_log_row(table, old_data, new_data, log_func); } } diff --git a/sql/log_event.cc b/sql/log_event.cc index c7cbff9c615..0e48791fd5c 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -12766,7 +12766,7 @@ uint8 Write_rows_log_event::get_trg_event_map() Returns TRUE if different. */ -static bool record_compare(TABLE *table) +static bool record_compare(TABLE *table, bool skip_sys_start) { bool result= FALSE; /** @@ -12799,7 +12799,10 @@ static bool record_compare(TABLE *table) /* Compare fields */ for (Field **ptr=table->field ; *ptr ; ptr++) { - + if (skip_sys_start && *ptr == table->vers_start_field()) + { + continue; + } /** We only compare field contents that are not null. NULL fields (i.e., their null bits) were compared @@ -12994,6 +12997,24 @@ int Rows_log_event::find_row(rpl_group_info *rgi) prepare_record(table, m_width, FALSE); error= unpack_current_row(rgi); + bool skip_sys_start= false; + + if (table->versioned()) + { + Field *sys_trx_end= table->vers_end_field(); + DBUG_ASSERT(table->read_set); + bitmap_set_bit(table->read_set, sys_trx_end->field_index); + // master table is unversioned + if (sys_trx_end->val_int() == 0) + { + DBUG_ASSERT(table->write_set); + bitmap_set_bit(table->write_set, sys_trx_end->field_index); + sys_trx_end->set_max(); + table->vers_start_field()->set_notnull(); + skip_sys_start= true; + } + } + DBUG_PRINT("info",("looking for the following record")); DBUG_DUMP("record[0]", table->record[0], table->s->reclength); @@ -13169,7 +13190,7 @@ int Rows_log_event::find_row(rpl_group_info *rgi) /* We use this to test that the correct key is used in test cases. */ DBUG_EXECUTE_IF("slave_crash_if_index_scan", abort();); - while (record_compare(table)) + while (record_compare(table, skip_sys_start)) { while ((error= table->file->ha_index_next(table->record[0]))) { @@ -13233,7 +13254,7 @@ int Rows_log_event::find_row(rpl_group_info *rgi) goto end; } } - while (record_compare(table)); + while (record_compare(table, skip_sys_start)); /* Note: above record_compare will take into accout all record fields -- cgit v1.2.1 From 412dd1e1f3a890a771b64283b7f34bc65a8a791f Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 20 Dec 2016 18:52:45 +0000 Subject: SQL: FOR SYSTEM_TIME support in VIEW expression [fixes #99] --- sql/sql_select.cc | 43 ++++++------------------------------------- sql/table.h | 6 ------ 2 files changed, 6 insertions(+), 43 deletions(-) (limited to 'sql') diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 1aef44397b2..f72456a9414 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -899,8 +899,6 @@ vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, SELECT_LEX *s *dst_cond= cond1; else thd->change_item_tree(dst_cond, cond1); - - table->vers_moved_to_where= true; } } // if (... table->table->versioned()) } // for (table= tables; ...) @@ -25036,39 +25034,6 @@ bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result) DBUG_RETURN(res || thd->is_error()); } -void TABLE_LIST::print_system_versioning(THD *thd, table_map eliminated_tables, - String *str, enum_query_type query_type) -{ - if (vers_moved_to_where) - return; - - DBUG_ASSERT(select_lex); - // system versioning - if (select_lex->vers_conditions.type != FOR_SYSTEM_TIME_UNSPECIFIED) - { - switch (select_lex->vers_conditions.type) - { - case FOR_SYSTEM_TIME_AS_OF: - str->append(STRING_WITH_LEN(" for system_time as of ")); - select_lex->vers_conditions.start->print(str, query_type); - break; - case FOR_SYSTEM_TIME_FROM_TO: - str->append(STRING_WITH_LEN(" for system_time from timestamp ")); - select_lex->vers_conditions.start->print(str, query_type); - str->append(STRING_WITH_LEN(" to ")); - select_lex->vers_conditions.end->print(str, query_type); - break; - case FOR_SYSTEM_TIME_BETWEEN: - str->append(STRING_WITH_LEN(" for system_time between timestamp ")); - select_lex->vers_conditions.start->print(str, query_type); - str->append(STRING_WITH_LEN(" and ")); - select_lex->vers_conditions.end->print(str, query_type); - break; - default: - DBUG_ASSERT(0); - } - } -} static void print_table_array(THD *thd, table_map eliminated_tables, @@ -25404,8 +25369,6 @@ void TABLE_LIST::print(THD *thd, table_map eliminated_tables, String *str, append_identifier(thd, str, t_alias, strlen(t_alias)); } - print_system_versioning(thd, eliminated_tables, str, query_type); - if (index_hints) { List_iterator it(*index_hints); @@ -25565,6 +25528,12 @@ void st_select_lex::print(THD *thd, String *str, enum_query_type query_type) str->append(having_value != Item::COND_FALSE ? "1" : "0"); } + if (vers_conditions.type != FOR_SYSTEM_TIME_UNSPECIFIED) + { + // versioning conditions must be already unwrapped to WHERE clause + str->append(STRING_WITH_LEN(" for system_time all ")); + } + if (order_list.elements) { str->append(STRING_WITH_LEN(" order by ")); diff --git a/sql/table.h b/sql/table.h index fc7e639f162..dbcbee290f8 100644 --- a/sql/table.h +++ b/sql/table.h @@ -2334,9 +2334,6 @@ struct TABLE_LIST TABLE_LIST *first_leaf_for_name_resolution(); TABLE_LIST *last_leaf_for_name_resolution(); - /* System Versioning */ - bool vers_moved_to_where; - /** @brief Find the bottom in the chain of embedded table VIEWs. @@ -2535,9 +2532,6 @@ struct TABLE_LIST void check_pushable_cond_for_table(Item *cond); Item *build_pushable_cond_for_table(THD *thd, Item *cond); - void print_system_versioning(THD *thd, table_map eliminated_tables, - String *str, enum_query_type query_type); - private: bool prep_check_option(THD *thd, uint8 check_opt_type); bool prep_where(THD *thd, Item **conds, bool no_where_clause); -- cgit v1.2.1 From 27d9e762a9dfe24c9afe167d3f3acfc6d8e43fd2 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Wed, 21 Dec 2016 08:17:44 +0000 Subject: SQL: prohibit write-locking of historic rows [fixes #102] --- sql/sql_select.cc | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) (limited to 'sql') diff --git a/sql/sql_select.cc b/sql/sql_select.cc index f72456a9414..b36ccc4e8d6 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -690,9 +690,11 @@ vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, SELECT_LEX *s versioned_tables++; } + vers_range_type_t query_type= slex->vers_conditions.type; + if (versioned_tables == 0) { - if (slex->vers_conditions.type != FOR_SYSTEM_TIME_UNSPECIFIED) + if (query_type != FOR_SYSTEM_TIME_UNSPECIFIED) { my_error(ER_VERSIONING_REQUIRED, MYF(0), "`FOR SYSTEM_TIME` query"); DBUG_RETURN(-1); @@ -700,9 +702,27 @@ vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, SELECT_LEX *s DBUG_RETURN(0); } - if (slex->vers_conditions.type == FOR_SYSTEM_TIME_ALL) + if (query_type != FOR_SYSTEM_TIME_UNSPECIFIED) { - DBUG_RETURN(0); + switch (slex->lock_type) + { + case TL_WRITE_ALLOW_WRITE: + case TL_WRITE_CONCURRENT_INSERT: + case TL_WRITE_DELAYED: + case TL_WRITE_DEFAULT: + case TL_WRITE_LOW_PRIORITY: + case TL_WRITE: + case TL_WRITE_ONLY: + my_error(ER_VERS_WRONG_PARAMS, MYF(0), "FOR SYSTEM_TIME query", "write-locking of historic rows"); + DBUG_RETURN(-1); + default: + break; + } + + if (query_type == FOR_SYSTEM_TIME_ALL) + { + DBUG_RETURN(0); + } } /* For prepared statements we create items on statement arena, @@ -784,7 +804,7 @@ vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, SELECT_LEX *s } } else if (vers_simple_select && slex->vers_conditions.unit == UNIT_TIMESTAMP - && slex->vers_conditions.type != FOR_SYSTEM_TIME_UNSPECIFIED) + && query_type != FOR_SYSTEM_TIME_UNSPECIFIED) { DBUG_ASSERT(table->table->s && table->table->s->db_plugin); handlerton *hton= plugin_hton(table->table->s->db_plugin); @@ -805,7 +825,7 @@ vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, SELECT_LEX *s Item *cond1= 0, *cond2= 0, *curr= 0; if (table->table->versioned_by_sql() || vers_simple_select) { - switch (slex->vers_conditions.type) + switch (query_type) { case FOR_SYSTEM_TIME_UNSPECIFIED: if (table->table->versioned_by_sql()) @@ -851,7 +871,7 @@ vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, SELECT_LEX *s Item *trx_id0, *trx_id1; - switch (slex->vers_conditions.type) + switch (query_type) { case FOR_SYSTEM_TIME_UNSPECIFIED: curr= newx Item_int(thd, ULONGLONG_MAX); @@ -877,7 +897,7 @@ vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, SELECT_LEX *s trx_id1= slex->vers_conditions.end; } - cond1= slex->vers_conditions.type == FOR_SYSTEM_TIME_FROM_TO ? + cond1= query_type == FOR_SYSTEM_TIME_FROM_TO ? newx Item_func_vtq_trx_sees(thd, trx_id1, row_start) : newx Item_func_vtq_trx_sees_eq(thd, trx_id1, row_start); cond2= newx Item_func_vtq_trx_sees_eq(thd, row_end, trx_id0); -- cgit v1.2.1 From ea60760e476ae3e5d1dc44d1976fba68e86f679f Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Thu, 22 Dec 2016 07:34:33 +0000 Subject: SQL: missed FOR SYSTEM_TIME ALL for FOR_SYSTEM_TIME_UNSPECIFIED [fixes #105] --- sql/sql_select.cc | 7 +++++-- sql/table.h | 4 ++++ 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'sql') diff --git a/sql/sql_select.cc b/sql/sql_select.cc index b36ccc4e8d6..4dc67267b74 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -721,6 +721,7 @@ vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, SELECT_LEX *s if (query_type == FOR_SYSTEM_TIME_ALL) { + slex->vers_conditions.unwrapped= true; DBUG_RETURN(0); } } @@ -928,6 +929,8 @@ vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, SELECT_LEX *s thd->restore_active_arena(arena, &backup); } + slex->vers_conditions.unwrapped= true; + DBUG_RETURN(0); #undef newx } @@ -25548,9 +25551,9 @@ void st_select_lex::print(THD *thd, String *str, enum_query_type query_type) str->append(having_value != Item::COND_FALSE ? "1" : "0"); } - if (vers_conditions.type != FOR_SYSTEM_TIME_UNSPECIFIED) + if (vers_conditions.unwrapped) { - // versioning conditions must be already unwrapped to WHERE clause + // versioning conditions are already unwrapped to WHERE clause str->append(STRING_WITH_LEN(" for system_time all ")); } diff --git a/sql/table.h b/sql/table.h index dbcbee290f8..552e51284a6 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1857,11 +1857,14 @@ struct vers_select_conds_t vers_range_unit_t unit; Item *start, *end; + bool unwrapped; + void empty() { type= FOR_SYSTEM_TIME_UNSPECIFIED; unit= UNIT_TIMESTAMP; start= end= NULL; + unwrapped= false; } void init( @@ -1874,6 +1877,7 @@ struct vers_select_conds_t unit= u; start= s; end= e; + unwrapped= false; } }; -- cgit v1.2.1 From abb2f9488d3e1ca4e4dacc8a315ae6ed6f13fd68 Mon Sep 17 00:00:00 2001 From: kevg Date: Mon, 26 Dec 2016 18:46:02 +0300 Subject: IB: skip sys_trx_start when comparing master and slave rows [closes #107] --- sql/log_event.cc | 10 ++++------ sql/table.h | 6 ++++++ 2 files changed, 10 insertions(+), 6 deletions(-) (limited to 'sql') diff --git a/sql/log_event.cc b/sql/log_event.cc index 0e48791fd5c..4a48ebce077 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -12766,7 +12766,7 @@ uint8 Write_rows_log_event::get_trg_event_map() Returns TRUE if different. */ -static bool record_compare(TABLE *table, bool skip_sys_start) +static bool record_compare(TABLE *table) { bool result= FALSE; /** @@ -12799,7 +12799,7 @@ static bool record_compare(TABLE *table, bool skip_sys_start) /* Compare fields */ for (Field **ptr=table->field ; *ptr ; ptr++) { - if (skip_sys_start && *ptr == table->vers_start_field()) + if (table->versioned_by_engine() && *ptr == table->vers_start_field()) { continue; } @@ -12997,7 +12997,6 @@ int Rows_log_event::find_row(rpl_group_info *rgi) prepare_record(table, m_width, FALSE); error= unpack_current_row(rgi); - bool skip_sys_start= false; if (table->versioned()) { @@ -13011,7 +13010,6 @@ int Rows_log_event::find_row(rpl_group_info *rgi) bitmap_set_bit(table->write_set, sys_trx_end->field_index); sys_trx_end->set_max(); table->vers_start_field()->set_notnull(); - skip_sys_start= true; } } @@ -13190,7 +13188,7 @@ int Rows_log_event::find_row(rpl_group_info *rgi) /* We use this to test that the correct key is used in test cases. */ DBUG_EXECUTE_IF("slave_crash_if_index_scan", abort();); - while (record_compare(table, skip_sys_start)) + while (record_compare(table)) { while ((error= table->file->ha_index_next(table->record[0]))) { @@ -13254,7 +13252,7 @@ int Rows_log_event::find_row(rpl_group_info *rgi) goto end; } } - while (record_compare(table, skip_sys_start)); + while (record_compare(table)); /* Note: above record_compare will take into accout all record fields diff --git a/sql/table.h b/sql/table.h index 552e51284a6..da91162523a 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1522,6 +1522,12 @@ public: return s->versioned && !file->versioned(); } + bool versioned_by_engine() const + { + DBUG_ASSERT(file); + return s->versioned && file->versioned(); + } + Field *vers_start_field() const { DBUG_ASSERT(s->versioned); -- cgit v1.2.1 From c9e4ac4b7221ab58097ce16e91eb1b885f2a3485 Mon Sep 17 00:00:00 2001 From: kevg Date: Fri, 23 Dec 2016 17:05:57 +0300 Subject: 0.6: truncate history feature [closes #96] --- sql/handler.cc | 2 +- sql/handler.h | 2 +- sql/share/errmsg-utf8.txt | 3 +++ sql/sql_delete.cc | 58 ++++++++++++++++++++++++++++++++++------------- sql/sql_select.cc | 4 ++-- sql/sql_select.h | 3 +++ sql/sql_truncate.cc | 9 ++++++-- sql/sql_yacc.yy | 3 ++- 8 files changed, 61 insertions(+), 23 deletions(-) (limited to 'sql') diff --git a/sql/handler.cc b/sql/handler.cc index 0712d3e5798..98f558f50cc 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -5659,7 +5659,7 @@ bool ha_show_status(THD *thd, handlerton *db_type, enum ha_stat_type stat) 1 Row needs to be logged */ -inline bool handler::check_table_binlog_row_based(bool binlog_row) +bool handler::check_table_binlog_row_based(bool binlog_row) { if (unlikely((table->in_use->variables.sql_log_bin_off))) return 0; /* Called by partitioning engine */ diff --git a/sql/handler.h b/sql/handler.h index 5bcacf1f50b..56ee1addd6d 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -4079,7 +4079,7 @@ protected: virtual int delete_table(const char *name); public: - inline bool check_table_binlog_row_based(bool binlog_row); + bool check_table_binlog_row_based(bool binlog_row); private: /* Cache result to avoid extra calls */ inline void mark_trx_read_write() diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index c25b8b655cd..daca9b4ce52 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7522,3 +7522,6 @@ WARN_VERS_PARAMETERS WARN_VERS_PART_ROTATION eng "Switching from partition %`s to %`s" + +ER_VERS_NOT_ALLOWED + eng "%`s is not allowed for versioned table" diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 775d0c0d023..5d62b54b297 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -215,19 +215,13 @@ void Update_plan::save_explain_data_intern(MEM_ROOT *mem_root, inline int TABLE::delete_row() { - int error; - if (!versioned_by_sql()) - error= file->ha_delete_row(record[0]); - else - { - store_record(this, record[1]); - Field *sys_trx_end= vers_end_field(); - sys_trx_end->set_time(); - error= file->ha_update_row(record[1], record[0]); - } - return error; -} + if (!versioned_by_sql() || !vers_end_field()->is_max()) + return file->ha_delete_row(record[0]); + store_record(this, record[1]); + vers_end_field()->set_time(); + return file->ha_update_row(record[1], record[0]); +} /** Implement DELETE SQL word. @@ -269,6 +263,34 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (open_and_lock_tables(thd, table_list, TRUE, 0)) DBUG_RETURN(TRUE); + bool truncate_history= + select_lex->vers_conditions.type != FOR_SYSTEM_TIME_UNSPECIFIED; + if (truncate_history) + { + TABLE *table= table_list->table; + DBUG_ASSERT(table); + + if (table->versioned_by_engine() && + table->file->check_table_binlog_row_based(1)) + { + my_error(ER_VERS_NOT_ALLOWED, MYF(0), + "TRUNCATE FOR SYSTEM_TIME with row-based replication"); + DBUG_RETURN(TRUE); + } + + DBUG_ASSERT(!conds); + if (vers_setup_select(thd, table_list, &conds, select_lex)) + DBUG_RETURN(TRUE); + + // trx_sees() in InnoDB reads sys_trx_start + if (!table->versioned_by_sql() && + (select_lex->vers_conditions.type == FOR_SYSTEM_TIME_BETWEEN || + select_lex->vers_conditions.type == FOR_SYSTEM_TIME_FROM_TO)) + { + bitmap_set_bit(table->read_set, table->vers_start_field()->field_index); + } + } + if (mysql_handle_list_of_derived(thd->lex, table_list, DT_MERGE_FOR_INSERT)) DBUG_RETURN(TRUE); if (mysql_handle_list_of_derived(thd->lex, table_list, DT_PREPARE)) @@ -577,9 +599,13 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, while (!(error=info.read_record(&info)) && !thd->killed && ! thd->is_error()) { - if (table->versioned() && !table->vers_end_field()->is_max()) + if (table->versioned()) { - continue; + bool row_is_alive= table->vers_end_field()->is_max(); + if (truncate_history && row_is_alive) + continue; + if (!truncate_history && !row_is_alive) + continue; } explain->tracker.on_record_read(); @@ -589,7 +615,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (!select || select->skip_record(thd) > 0) { explain->tracker.on_record_after_where(); - if (table->triggers && + if (!truncate_history && table->triggers && table->triggers->process_triggers(thd, TRG_EVENT_DELETE, TRG_ACTION_BEFORE, FALSE)) { @@ -607,7 +633,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (!error) { deleted++; - if (table->triggers && + if (!truncate_history && table->triggers && table->triggers->process_triggers(thd, TRG_EVENT_DELETE, TRG_ACTION_AFTER, FALSE)) { diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 4dc67267b74..6655f71e10a 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -667,8 +667,8 @@ setup_without_group(THD *thd, Ref_ptr_array ref_pointer_array, DBUG_RETURN(res); } -static int -vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, SELECT_LEX *slex) +int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, + SELECT_LEX *slex) { DBUG_ENTER("vers_setup_select"); #define newx new (thd->mem_root) diff --git a/sql/sql_select.h b/sql/sql_select.h index 77cf73d785f..aa238c9b741 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -2301,4 +2301,7 @@ int create_sort_index(THD *thd, JOIN *join, JOIN_TAB *tab, Filesort *fsort); JOIN_TAB *first_explain_order_tab(JOIN* join); JOIN_TAB *next_explain_order_tab(JOIN* join, JOIN_TAB* tab); +int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, + SELECT_LEX *slex); + #endif /* SQL_SELECT_INCLUDED */ diff --git a/sql/sql_truncate.cc b/sql/sql_truncate.cc index daa295d768e..4faad5b4711 100644 --- a/sql/sql_truncate.cc +++ b/sql/sql_truncate.cc @@ -26,7 +26,8 @@ #include "sql_truncate.h" #include "wsrep_mysqld.h" #include "sql_show.h" //append_identifier() - +#include "sql_select.h" +#include "sql_delete.h" /** Append a list of field names to a string. @@ -480,7 +481,6 @@ bool Sql_cmd_truncate_table::truncate_table(THD *thd, TABLE_LIST *table_ref) DBUG_RETURN(error); } - /** Execute a TRUNCATE statement at runtime. @@ -495,6 +495,11 @@ bool Sql_cmd_truncate_table::execute(THD *thd) TABLE_LIST *first_table= thd->lex->select_lex.table_list.first; DBUG_ENTER("Sql_cmd_truncate_table::execute"); + bool truncate_history= thd->lex->current_select->vers_conditions.type != + FOR_SYSTEM_TIME_UNSPECIFIED; + if (truncate_history) + DBUG_RETURN(mysql_delete(thd, first_table, NULL, NULL, -1, 0, NULL)); + if (check_one_table_access(thd, DROP_ACL, first_table)) DBUG_RETURN(res); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index f591566d91a..4c781a6afd1 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -12998,8 +12998,9 @@ truncate: lex->select_lex.init_order(); YYPS->m_lock_type= TL_WRITE; YYPS->m_mdl_type= MDL_EXCLUSIVE; + Select->vers_conditions.empty(); } - table_name opt_lock_wait_timeout + table_name opt_for_system_time_clause opt_lock_wait_timeout { LEX* lex= thd->lex; DBUG_ASSERT(!lex->m_sql_cmd); -- cgit v1.2.1 From 57692d71176693cb9ee8e8946336b4e2c1feb2ef Mon Sep 17 00:00:00 2001 From: kevg Date: Tue, 10 Jan 2017 15:15:39 +0300 Subject: SQL, IB: ALTER ADD AUTO_INCREMENT for versioned tables [closes #112] --- sql/handler.cc | 31 ++++++++++++++++++++++++++++++- sql/handler.h | 2 ++ sql/sql_table.cc | 4 ++++ 3 files changed, 36 insertions(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/handler.cc b/sql/handler.cc index 98f558f50cc..c19d04236d7 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -3023,6 +3023,35 @@ int handler::update_auto_increment() enum enum_check_fields save_count_cuted_fields; DBUG_ENTER("handler::update_auto_increment"); + if (table->versioned_by_sql()) + { + Field *end= table->vers_end_field(); + DBUG_ASSERT(end); + bitmap_set_bit(table->read_set, end->field_index); + if (!end->is_max()) + { + uchar *ptr= table->next_number_field->ptr; + switch (table->next_number_field->pack_length()) + { + case 8: + int8store(ptr, vers_auto_decrement--); + break; + case 4: + int4store(ptr, vers_auto_decrement--); + break; + case 2: + int2store(ptr, vers_auto_decrement--); + break; + case 1: + *ptr= vers_auto_decrement--; + break; + default: + DBUG_ASSERT(false); + } + DBUG_RETURN(0); + } + } + /* next_insert_id is a "cursor" into the reserved interval, it may go greater than the interval, but not smaller. @@ -3145,7 +3174,7 @@ int handler::update_auto_increment() /* Store field without warning (Warning will be printed by insert) */ save_count_cuted_fields= thd->count_cuted_fields; thd->count_cuted_fields= CHECK_FIELD_IGNORE; - tmp= table->next_number_field->store((longlong) nr, TRUE); + tmp= table->next_number_field->store((longlong)nr, TRUE); thd->count_cuted_fields= save_count_cuted_fields; if (unlikely(tmp)) // Out of range value in store diff --git a/sql/handler.h b/sql/handler.h index 56ee1addd6d..e20f95df1f3 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -2835,6 +2835,8 @@ public: */ uint auto_inc_intervals_count; + ulonglong vers_auto_decrement; + /** Instrumented table associated with this handler. This member should be set to NULL when no instrumentation is in place, diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 9ad37134c86..b5cf35ed17c 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -9777,6 +9777,10 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, { from_sys_trx_end= from->field[from->s->row_end_field]; } + else if (from->versioned() && to->versioned()) + { + to->file->vers_auto_decrement= 0xffffffffffffffff; + } THD_STAGE_INFO(thd, stage_copy_to_tmp_table); /* Tell handler that we have values for all columns in the to table */ -- cgit v1.2.1 From 3a64d55aede6db2b3d520e7fbc17e70cfeb8916e Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Fri, 13 Jan 2017 13:56:01 +0000 Subject: Parser, SQL: table-specific FOR SYSTEM_TIME [closes #116] * Syntax sugar: query-global QUERY FOR SYSTEM_TIME --- sql/item.cc | 6 ++-- sql/sql_lex.cc | 15 ++++++++ sql/sql_lex.h | 3 ++ sql/sql_select.cc | 105 +++++++++++++++++++++++++++++------------------------- sql/sql_yacc.yy | 97 ++++++++++++++++++++++++++----------------------- sql/table.h | 5 ++- 6 files changed, 134 insertions(+), 97 deletions(-) (limited to 'sql') diff --git a/sql/item.cc b/sql/item.cc index 4bcdb477a51..bb1efca232b 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -2762,8 +2762,10 @@ void Item_field::set_field(Field *field_par) field->force_null= false; if (field->flags & VERS_OPTIMIZED_UPDATE_FLAG && context && - context->select_lex && - context->select_lex->vers_conditions.type != FOR_SYSTEM_TIME_UNSPECIFIED) + ((field->table->pos_in_table_list && + field->table->pos_in_table_list->vers_conditions.type != FOR_SYSTEM_TIME_UNSPECIFIED) || + (context->select_lex && + context->select_lex->vers_conditions.type != FOR_SYSTEM_TIME_UNSPECIFIED))) { field->force_null= true; push_warning_printf( diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 3375324b027..75a4c3bbc0c 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -766,6 +766,8 @@ void LEX::start(THD *thd_arg) frame_bottom_bound= NULL; win_spec= NULL; + vers_conditions.empty(); + is_lex_started= TRUE; DBUG_VOID_RETURN; } @@ -1366,6 +1368,19 @@ int MYSQLlex(YYSTYPE *yylval, THD *thd) return FOR_SYM; } break; + case QUERY_SYM: + token= lex_one_token(yylval, thd); + lip->add_digest_token(token, yylval); + switch(token) { + case FOR_SYM: + return QUERY_FOR_SYM; + default: + lip->lookahead_yylval= lip->yylval; + lip->yylval= NULL; + lip->lookahead_token= token; + return QUERY_SYM; + } + break; default: break; } diff --git a/sql/sql_lex.h b/sql/sql_lex.h index c0e28ab8fd2..06aa248ee02 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -2920,6 +2920,9 @@ public: Window_frame_bound *frame_bottom_bound; Window_spec *win_spec; + /* System Versioning */ + vers_select_conds_t vers_conditions; + inline void free_set_stmt_mem_root() { DBUG_ASSERT(!is_arena_for_set_stmt()); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 6655f71e10a..a00304760de 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -688,42 +688,21 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, { if (table->table && table->table->versioned()) versioned_tables++; - } - - vers_range_type_t query_type= slex->vers_conditions.type; - - if (versioned_tables == 0) - { - if (query_type != FOR_SYSTEM_TIME_UNSPECIFIED) + else if (table->vers_conditions.type != FOR_SYSTEM_TIME_UNSPECIFIED) { my_error(ER_VERSIONING_REQUIRED, MYF(0), "`FOR SYSTEM_TIME` query"); DBUG_RETURN(-1); } - DBUG_RETURN(0); } - if (query_type != FOR_SYSTEM_TIME_UNSPECIFIED) + if (versioned_tables == 0) { - switch (slex->lock_type) + if (slex->vers_conditions.type != FOR_SYSTEM_TIME_UNSPECIFIED) { - case TL_WRITE_ALLOW_WRITE: - case TL_WRITE_CONCURRENT_INSERT: - case TL_WRITE_DELAYED: - case TL_WRITE_DEFAULT: - case TL_WRITE_LOW_PRIORITY: - case TL_WRITE: - case TL_WRITE_ONLY: - my_error(ER_VERS_WRONG_PARAMS, MYF(0), "FOR SYSTEM_TIME query", "write-locking of historic rows"); + my_error(ER_VERSIONING_REQUIRED, MYF(0), "`FOR SYSTEM_TIME` query"); DBUG_RETURN(-1); - default: - break; - } - - if (query_type == FOR_SYSTEM_TIME_ALL) - { - slex->vers_conditions.unwrapped= true; - DBUG_RETURN(0); } + DBUG_RETURN(0); } /* For prepared statements we create items on statement arena, @@ -779,6 +758,36 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, { if (table->table && table->table->versioned()) { + vers_select_conds_t &vers_conditions= + table->vers_conditions.type == FOR_SYSTEM_TIME_UNSPECIFIED ? + slex->vers_conditions : table->vers_conditions; + + if (vers_conditions.type != FOR_SYSTEM_TIME_UNSPECIFIED) + { + switch (slex->lock_type) + { + case TL_WRITE_ALLOW_WRITE: + case TL_WRITE_CONCURRENT_INSERT: + case TL_WRITE_DELAYED: + case TL_WRITE_DEFAULT: + case TL_WRITE_LOW_PRIORITY: + case TL_WRITE: + case TL_WRITE_ONLY: + my_error(ER_VERS_WRONG_PARAMS, MYF(0), "FOR SYSTEM_TIME query", "write-locking of historic rows"); + if (arena) + thd->restore_active_arena(arena, &backup); + DBUG_RETURN(-1); + default: + break; + } + + if (vers_conditions.type == FOR_SYSTEM_TIME_ALL) + { + vers_conditions.unwrapped= true; + continue; + } + } + COND** dst_cond= where_expr; if (table->on_expr) { @@ -798,14 +807,14 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, if (table->table->versioned_by_sql()) { - if (slex->vers_conditions.unit == UNIT_TRX_ID) + if (vers_conditions.unit == UNIT_TRX_ID) { my_error(ER_VERS_ENGINE_UNSUPPORTED, MYF(0), table->table_name); DBUG_RETURN(-1); } } - else if (vers_simple_select && slex->vers_conditions.unit == UNIT_TIMESTAMP - && query_type != FOR_SYSTEM_TIME_UNSPECIFIED) + else if (vers_simple_select && vers_conditions.unit == UNIT_TIMESTAMP + && vers_conditions.type != FOR_SYSTEM_TIME_UNSPECIFIED) { DBUG_ASSERT(table->table->s && table->table->s->db_plugin); handlerton *hton= plugin_hton(table->table->s->db_plugin); @@ -826,7 +835,7 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, Item *cond1= 0, *cond2= 0, *curr= 0; if (table->table->versioned_by_sql() || vers_simple_select) { - switch (query_type) + switch (vers_conditions.type) { case FOR_SYSTEM_TIME_UNSPECIFIED: if (table->table->versioned_by_sql()) @@ -844,21 +853,21 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, break; case FOR_SYSTEM_TIME_AS_OF: cond1= newx Item_func_le(thd, row_start, - slex->vers_conditions.start); + vers_conditions.start); cond2= newx Item_func_gt(thd, row_end, - slex->vers_conditions.start); + vers_conditions.start); break; case FOR_SYSTEM_TIME_FROM_TO: cond1= newx Item_func_lt(thd, row_start, - slex->vers_conditions.end); + vers_conditions.end); cond2= newx Item_func_ge(thd, row_end, - slex->vers_conditions.start); + vers_conditions.start); break; case FOR_SYSTEM_TIME_BETWEEN: cond1= newx Item_func_le(thd, row_start, - slex->vers_conditions.end); + vers_conditions.end); cond2= newx Item_func_ge(thd, row_end, - slex->vers_conditions.start); + vers_conditions.start); break; default: DBUG_ASSERT(0); @@ -872,33 +881,33 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, Item *trx_id0, *trx_id1; - switch (query_type) + switch (vers_conditions.type) { case FOR_SYSTEM_TIME_UNSPECIFIED: curr= newx Item_int(thd, ULONGLONG_MAX); cond1= newx Item_func_eq(thd, row_end2, curr); break; case FOR_SYSTEM_TIME_AS_OF: - trx_id0= slex->vers_conditions.unit == UNIT_TIMESTAMP ? - newx Item_func_vtq_id(thd, slex->vers_conditions.start, VTQ_TRX_ID) : - slex->vers_conditions.start; + trx_id0= vers_conditions.unit == UNIT_TIMESTAMP ? + newx Item_func_vtq_id(thd, vers_conditions.start, VTQ_TRX_ID) : + vers_conditions.start; cond1= newx Item_func_vtq_trx_sees_eq(thd, trx_id0, row_start); cond2= newx Item_func_vtq_trx_sees(thd, row_end, trx_id0); break; case FOR_SYSTEM_TIME_FROM_TO: case FOR_SYSTEM_TIME_BETWEEN: - if (slex->vers_conditions.unit == UNIT_TIMESTAMP) + if (vers_conditions.unit == UNIT_TIMESTAMP) { - trx_id0= newx Item_func_vtq_id(thd, slex->vers_conditions.start, VTQ_TRX_ID, true); - trx_id1= newx Item_func_vtq_id(thd, slex->vers_conditions.end, VTQ_TRX_ID, false); + trx_id0= newx Item_func_vtq_id(thd, vers_conditions.start, VTQ_TRX_ID, true); + trx_id1= newx Item_func_vtq_id(thd, vers_conditions.end, VTQ_TRX_ID, false); } else { - trx_id0= slex->vers_conditions.start; - trx_id1= slex->vers_conditions.end; + trx_id0= vers_conditions.start; + trx_id1= vers_conditions.end; } - cond1= query_type == FOR_SYSTEM_TIME_FROM_TO ? + cond1= vers_conditions.type == FOR_SYSTEM_TIME_FROM_TO ? newx Item_func_vtq_trx_sees(thd, trx_id1, row_start) : newx Item_func_vtq_trx_sees_eq(thd, trx_id1, row_start); cond2= newx Item_func_vtq_trx_sees_eq(thd, row_end, trx_id0); @@ -925,9 +934,7 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, } // for (table= tables; ...) if (arena) - { thd->restore_active_arena(arena, &backup); - } slex->vers_conditions.unwrapped= true; @@ -25554,7 +25561,7 @@ void st_select_lex::print(THD *thd, String *str, enum_query_type query_type) if (vers_conditions.unwrapped) { // versioning conditions are already unwrapped to WHERE clause - str->append(STRING_WITH_LEN(" for system_time all ")); + str->append(STRING_WITH_LEN(" query for system_time all ")); } if (order_list.elements) diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 4c781a6afd1..11f4b53eb8b 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -751,6 +751,7 @@ Virtual_column_info *add_virtual_expression(THD *thd, Item *expr) %} %union { + bool BOOL; int num; ulong ulong_num; ulonglong ulonglong_number; @@ -1352,6 +1353,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token PURGE %token QUARTER_SYM %token QUERY_SYM +%token QUERY_FOR_SYM /* INTERNAL */ %token QUICK %token RAISE_SYM /* Oracle-PLSQL-R */ %token RANGE_SYM /* SQL-2003-R */ @@ -1970,6 +1972,7 @@ END_OF_INPUT %type opt_with_column_list %type trans_or_timestamp +%type opt_for_system_time_clause %% @@ -8762,7 +8765,7 @@ table_expression: opt_group_clause opt_having_clause opt_window_clause - opt_for_system_time_clause + opt_query_for_system_time_clause ; opt_table_expression: @@ -8808,75 +8811,74 @@ trans_or_timestamp: } ; -opt_for_system_time_clause: +opt_query_for_system_time_clause: /* empty */ {} - | FOR_SYSTEM_TIME_SYM - AS OF_SYM - trans_or_timestamp simple_expr + | QUERY_FOR_SYM SYSTEM_TIME_SYM for_system_time_expr + { + DBUG_ASSERT(Select); + Select->vers_conditions= Lex->vers_conditions; + } + ; + +opt_for_system_time_clause: + /* empty */ + { + $$= false; + } + | FOR_SYSTEM_TIME_SYM for_system_time_expr + { + $$= true; + } + ; + +for_system_time_expr: + AS OF_SYM trans_or_timestamp simple_expr { - Lex->current_select->vers_conditions.init(FOR_SYSTEM_TIME_AS_OF, $4, $5); + Lex->vers_conditions.init(FOR_SYSTEM_TIME_AS_OF, $3, $4); } - | FOR_SYSTEM_TIME_SYM - AS OF_SYM - NOW_SYM + | AS OF_SYM NOW_SYM { Item *item= new (thd->mem_root) Item_func_now_local(thd, 6); if (item == NULL) MYSQL_YYABORT; - Lex->current_select->vers_conditions.init(FOR_SYSTEM_TIME_AS_OF, UNIT_TIMESTAMP, item); + Lex->vers_conditions.init(FOR_SYSTEM_TIME_AS_OF, UNIT_TIMESTAMP, item); } - | FOR_SYSTEM_TIME_SYM ALL + | ALL { - Lex->current_select->vers_conditions.init(FOR_SYSTEM_TIME_ALL, UNIT_TIMESTAMP); + Lex->vers_conditions.init(FOR_SYSTEM_TIME_ALL, UNIT_TIMESTAMP); } - | FOR_SYSTEM_TIME_SYM - FROM - trans_or_timestamp - simple_expr - TO_SYM - trans_or_timestamp - simple_expr + | FROM trans_or_timestamp simple_expr + TO_SYM trans_or_timestamp simple_expr { - if ($3 != $6) + if ($2 != $5) { Lex->parse_error(ER_VERS_RANGE_UNITS_MISMATCH); MYSQL_YYABORT; } - Lex->current_select->vers_conditions.init(FOR_SYSTEM_TIME_FROM_TO, $3, $4, $7); + Lex->vers_conditions.init(FOR_SYSTEM_TIME_FROM_TO, $2, $3, $6); } - | FOR_SYSTEM_TIME_SYM - trans_or_timestamp - FROM - simple_expr - TO_SYM - simple_expr + | trans_or_timestamp + FROM simple_expr + TO_SYM simple_expr { - Lex->current_select->vers_conditions.init(FOR_SYSTEM_TIME_FROM_TO, $2, $4, $6); + Lex->vers_conditions.init(FOR_SYSTEM_TIME_FROM_TO, $1, $3, $5); } - | FOR_SYSTEM_TIME_SYM - BETWEEN_SYM - trans_or_timestamp - simple_expr - AND_SYM - trans_or_timestamp - simple_expr + | BETWEEN_SYM trans_or_timestamp simple_expr + AND_SYM trans_or_timestamp simple_expr { - if ($3 != $6) + if ($2 != $5) { Lex->parse_error(ER_VERS_RANGE_UNITS_MISMATCH); MYSQL_YYABORT; } - Lex->current_select->vers_conditions.init(FOR_SYSTEM_TIME_BETWEEN, $3, $4, $7); + Lex->vers_conditions.init(FOR_SYSTEM_TIME_BETWEEN, $2, $3, $6); } - | FOR_SYSTEM_TIME_SYM - trans_or_timestamp - BETWEEN_SYM - simple_expr - AND_SYM - simple_expr + | trans_or_timestamp + BETWEEN_SYM simple_expr + AND_SYM simple_expr { - Lex->current_select->vers_conditions.init(FOR_SYSTEM_TIME_BETWEEN, $2, $4, $6); + Lex->vers_conditions.init(FOR_SYSTEM_TIME_BETWEEN, $1, $3, $5); } ; @@ -11284,10 +11286,11 @@ table_factor: table_primary_ident: { + DBUG_ASSERT(Select); SELECT_LEX *sel= Select; sel->table_join_options= 0; } - table_ident opt_use_partition opt_table_alias opt_key_definition + table_ident opt_use_partition opt_table_alias opt_key_definition opt_for_system_time_clause { if (!($$= Select->add_table_to_list(thd, $2, $4, Select->get_table_join_options(), @@ -11297,6 +11300,8 @@ table_primary_ident: $3))) MYSQL_YYABORT; Select->add_joined_table($$); + if ($6) + $$->vers_conditions= Lex->vers_conditions; } ; @@ -13007,6 +13012,8 @@ truncate: lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_truncate_table(); if (lex->m_sql_cmd == NULL) MYSQL_YYABORT; + if ($5) + Select->vers_conditions= Lex->vers_conditions; } ; diff --git a/sql/table.h b/sql/table.h index da91162523a..a13de8d7128 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1856,7 +1856,7 @@ enum vers_range_unit_t UNIT_TRX_ID }; -/** System versioning support. */ +/** last_leaf_for_name_resolutioning support. */ struct vers_select_conds_t { vers_range_type_t type; @@ -2344,6 +2344,9 @@ struct TABLE_LIST TABLE_LIST *first_leaf_for_name_resolution(); TABLE_LIST *last_leaf_for_name_resolution(); + /* System Versioning */ + vers_select_conds_t vers_conditions; + /** @brief Find the bottom in the chain of embedded table VIEWs. -- cgit v1.2.1 From e069de7d9da69dd0aed3147a493402cbdeca2c12 Mon Sep 17 00:00:00 2001 From: kevg Date: Thu, 12 Jan 2017 13:51:12 +0300 Subject: SQL: TRUNCATE FOR SYSTEM_TIME BEFORE [closes #111] --- sql/share/errmsg-utf8.txt | 3 +++ sql/sql_delete.cc | 15 ++++++++++----- sql/sql_select.cc | 18 ++++++++++++++++++ sql/sql_yacc.yy | 6 ++++++ sql/table.h | 3 ++- 5 files changed, 39 insertions(+), 6 deletions(-) (limited to 'sql') diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index daca9b4ce52..b30240f64c3 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7525,3 +7525,6 @@ WARN_VERS_PART_ROTATION ER_VERS_NOT_ALLOWED eng "%`s is not allowed for versioned table" + +ER_VERS_WRONG_QUERY_TYPE + eng "%`s works only with %`s query type" diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 5d62b54b297..7d59d5164e1 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -283,11 +283,16 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, DBUG_RETURN(TRUE); // trx_sees() in InnoDB reads sys_trx_start - if (!table->versioned_by_sql() && - (select_lex->vers_conditions.type == FOR_SYSTEM_TIME_BETWEEN || - select_lex->vers_conditions.type == FOR_SYSTEM_TIME_FROM_TO)) - { - bitmap_set_bit(table->read_set, table->vers_start_field()->field_index); + if (!table->versioned_by_sql()) { + if (select_lex->vers_conditions.type == FOR_SYSTEM_TIME_BETWEEN || + select_lex->vers_conditions.type == FOR_SYSTEM_TIME_FROM_TO) + { + bitmap_set_bit(table->read_set, table->vers_start_field()->field_index); + } + else if (select_lex->vers_conditions.type == FOR_SYSTEM_TIME_BEFORE) + { + bitmap_set_bit(table->read_set, table->vers_end_field()->field_index); + } } } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index a00304760de..2d4d603c078 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -762,6 +762,14 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, table->vers_conditions.type == FOR_SYSTEM_TIME_UNSPECIFIED ? slex->vers_conditions : table->vers_conditions; + if (vers_conditions.type == FOR_SYSTEM_TIME_BEFORE && + thd->lex->sql_command != SQLCOM_TRUNCATE) + { + my_error(ER_VERS_WRONG_QUERY_TYPE, MYF(0), "FOR SYSTEM_TIME BEFORE", + "TRUNCATE"); + DBUG_RETURN(-1); + } + if (vers_conditions.type != FOR_SYSTEM_TIME_UNSPECIFIED) { switch (slex->lock_type) @@ -869,6 +877,10 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, cond2= newx Item_func_ge(thd, row_end, vers_conditions.start); break; + case FOR_SYSTEM_TIME_BEFORE: + cond1= newx Item_func_lt(thd, row_end, + vers_conditions.start); + break; default: DBUG_ASSERT(0); } @@ -912,6 +924,12 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, newx Item_func_vtq_trx_sees_eq(thd, trx_id1, row_start); cond2= newx Item_func_vtq_trx_sees_eq(thd, row_end, trx_id0); break; + case FOR_SYSTEM_TIME_BEFORE: + trx_id0= vers_conditions.unit == UNIT_TIMESTAMP ? + newx Item_func_vtq_id(thd, vers_conditions.start, VTQ_TRX_ID) : + vers_conditions.start; + cond1= newx Item_func_lt(thd, row_end, trx_id0); + break; default: DBUG_ASSERT(0); } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 11f4b53eb8b..2f1346eeb40 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -8880,6 +8880,12 @@ for_system_time_expr: { Lex->vers_conditions.init(FOR_SYSTEM_TIME_BETWEEN, $1, $3, $5); } + | BEFORE_SYM + trans_or_timestamp + simple_expr + { + Lex->vers_conditions.init(FOR_SYSTEM_TIME_BEFORE, $2, $3); + } ; select_option_list: diff --git a/sql/table.h b/sql/table.h index a13de8d7128..cb24d211261 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1847,7 +1847,8 @@ enum vers_range_type_t FOR_SYSTEM_TIME_AS_OF, FOR_SYSTEM_TIME_FROM_TO, FOR_SYSTEM_TIME_BETWEEN, - FOR_SYSTEM_TIME_ALL + FOR_SYSTEM_TIME_ALL, + FOR_SYSTEM_TIME_BEFORE }; enum vers_range_unit_t -- cgit v1.2.1 From 26a3ff0a22e931e44edf97b37c41db395eee6553 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Sat, 31 Dec 2016 15:33:26 +0000 Subject: SQL: (0.6) Pruning for VERSIONING partitions [closes #97] * based on RANGE pruning by COLUMNS (sys_trx_end) condition * removed DEFAULT; AS OF NOW is always last; current VERSIONING as last non-empty (or first empty) * Min/Max stats in TABLE_SHARE * ALTER TABLE ADD PARTITION adds before AS OF NOW partition --- sql/ha_partition.cc | 9 ++ sql/ha_partition.h | 4 +- sql/item.cc | 20 ++++ sql/item.h | 6 ++ sql/mysqld.cc | 7 +- sql/mysqld.h | 3 +- sql/opt_range.cc | 7 +- sql/partition_element.h | 65 +++++++++--- sql/partition_info.cc | 273 ++++++++++++++++++++++++++++++++++++++---------- sql/partition_info.h | 89 ++++++++++++++-- sql/sql_partition.cc | 51 ++++++--- sql/sql_yacc.yy | 35 +++---- sql/table.cc | 32 ++++-- sql/table.h | 20 ++-- 14 files changed, 483 insertions(+), 138 deletions(-) (limited to 'sql') diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 1ba3210d62b..0fa461e1807 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -4289,6 +4289,15 @@ int ha_partition::update_row(const uchar *old_data, uchar *new_data) if (error) goto exit; + if (m_part_info->part_type == VERSIONING_PARTITION) + { + uint sub_factor= m_part_info->num_subparts ? m_part_info->num_subparts : 1; + DBUG_ASSERT(m_tot_parts == m_part_info->num_parts * sub_factor); + uint lpart_id= new_part_id / sub_factor; + // lpart_id is VERSIONING partition because new_part_id != old_part_id + m_part_info->vers_update_stats(thd, lpart_id); + } + tmp_disable_binlog(thd); /* Do not replicate the low-level changes. */ error= m_file[old_part_id]->ha_delete_row(old_data); reenable_binlog(thd); diff --git a/sql/ha_partition.h b/sql/ha_partition.h index 1ca320eef1a..2c7f4a0861f 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -1295,9 +1295,7 @@ public: { handler *file= m_file[part_id]; DBUG_ASSERT(bitmap_is_set(&(m_part_info->read_partitions), part_id)); - file->info(HA_STATUS_TIME | HA_STATUS_VARIABLE | - HA_STATUS_VARIABLE_EXTRA | HA_STATUS_NO_LOCK); - + file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); part_recs+= file->stats.records; } return part_recs; diff --git a/sql/item.cc b/sql/item.cc index bb1efca232b..410fa32f234 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -6989,6 +6989,26 @@ bool Item_temporal_literal::eq(const Item *item, bool binary_cmp) const &((Item_temporal_literal *) item)->cached_time); } +bool Item_temporal_literal::set_lower(MYSQL_TIME * ltime) +{ + if (my_time_compare(ltime, &cached_time) < 0) + { + cached_time= *ltime; + return true; + } + return false; +} + +bool Item_temporal_literal::set_higher(MYSQL_TIME * ltime) +{ + if (my_time_compare(ltime, &cached_time) > 0) + { + cached_time= *ltime; + return true; + } + return false; +} + void Item_date_literal::print(String *str, enum_query_type query_type) { diff --git a/sql/item.h b/sql/item.h index b7381bde260..357eabd72e1 100644 --- a/sql/item.h +++ b/sql/item.h @@ -3865,6 +3865,12 @@ public: { return val_decimal_from_date(decimal_value); } int save_in_field(Field *field, bool no_conversions) { return save_date_in_field(field, no_conversions); } + void set_time(MYSQL_TIME *ltime) + { + cached_time= *ltime; + } + bool set_lower(MYSQL_TIME *ltime); + bool set_higher(MYSQL_TIME *ltime); }; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index acda4b51627..5196ec41c11 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -1017,7 +1017,8 @@ static PSI_mutex_info all_server_mutexes[]= PSI_rwlock_key key_rwlock_LOCK_grant, key_rwlock_LOCK_logger, key_rwlock_LOCK_sys_init_connect, key_rwlock_LOCK_sys_init_slave, - key_rwlock_LOCK_system_variables_hash, key_rwlock_query_cache_query_lock; + key_rwlock_LOCK_system_variables_hash, key_rwlock_query_cache_query_lock, + key_rwlock_LOCK_vers_stats, key_rwlock_LOCK_stat_serial; static PSI_rwlock_info all_server_rwlocks[]= { @@ -1029,7 +1030,9 @@ static PSI_rwlock_info all_server_rwlocks[]= { &key_rwlock_LOCK_sys_init_connect, "LOCK_sys_init_connect", PSI_FLAG_GLOBAL}, { &key_rwlock_LOCK_sys_init_slave, "LOCK_sys_init_slave", PSI_FLAG_GLOBAL}, { &key_rwlock_LOCK_system_variables_hash, "LOCK_system_variables_hash", PSI_FLAG_GLOBAL}, - { &key_rwlock_query_cache_query_lock, "Query_cache_query::lock", 0} + { &key_rwlock_query_cache_query_lock, "Query_cache_query::lock", 0}, + { &key_rwlock_LOCK_vers_stats, "Vers_field_stats::lock", 0}, + { &key_rwlock_LOCK_stat_serial, "TABLE_SHARE::LOCK_stat_serial", 0} }; #ifdef HAVE_MMAP diff --git a/sql/mysqld.h b/sql/mysqld.h index 8ac0625b755..46382396a5a 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -314,7 +314,8 @@ extern PSI_mutex_key key_LOCK_gtid_waiting; extern PSI_rwlock_key key_rwlock_LOCK_grant, key_rwlock_LOCK_logger, key_rwlock_LOCK_sys_init_connect, key_rwlock_LOCK_sys_init_slave, - key_rwlock_LOCK_system_variables_hash, key_rwlock_query_cache_query_lock; + key_rwlock_LOCK_system_variables_hash, key_rwlock_query_cache_query_lock, + key_rwlock_LOCK_vers_stats, key_rwlock_LOCK_stat_serial; #ifdef HAVE_MMAP extern PSI_cond_key key_PAGE_cond, key_COND_active, key_COND_pool; diff --git a/sql/opt_range.cc b/sql/opt_range.cc index fbdbf76ffd9..fcf038a212b 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -3454,6 +3454,11 @@ bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond) free_root(&alloc,MYF(0)); // Return memory & allocator DBUG_RETURN(FALSE); } + + if (part_info->part_type == VERSIONING_PARTITION) + { + part_info->vers_update_range_constants(thd); + } dbug_tmp_use_all_columns(table, old_sets, table->read_set, table->write_set); @@ -3980,7 +3985,7 @@ int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree) simply set res= -1 as if the mapper had returned that. TODO: What to do here is defined in WL#4065. */ - if (ppar->arg_stack[0]->part == 0) + if (ppar->arg_stack[0]->part == 0 || ppar->part_info->part_type == VERSIONING_PARTITION) { uint32 i; uint32 store_length_array[MAX_KEY]; diff --git a/sql/partition_element.h b/sql/partition_element.h index e76558bf7ae..18276ef713e 100644 --- a/sql/partition_element.h +++ b/sql/partition_element.h @@ -90,38 +90,64 @@ typedef struct p_elem_val struct st_ddl_log_memory_entry; -class Stat_timestampf : public Sql_alloc +class Vers_field_stats : public Sql_alloc { static const uint buf_size= 4 + (TIME_SECOND_PART_DIGITS + 1) / 2; uchar min_buf[buf_size]; uchar max_buf[buf_size]; Field_timestampf min_value; Field_timestampf max_value; + mysql_rwlock_t lock; public: - Stat_timestampf(const char *field_name, TABLE_SHARE *share) : + Vers_field_stats(const char *field_name, TABLE_SHARE *share) : min_value(min_buf, NULL, 0, Field::NONE, field_name, share, 6), max_value(max_buf, NULL, 0, Field::NONE, field_name, share, 6) { min_value.set_max(); memset(max_buf, 0, buf_size); + mysql_rwlock_init(key_rwlock_LOCK_vers_stats, &lock); } - void update(Field *from) + ~Vers_field_stats() { - from->update_min(&min_value, false); - from->update_max(&max_value, false); + mysql_rwlock_destroy(&lock); + } + bool update_unguarded(Field *from) + { + return + from->update_min(&min_value, false) + + from->update_max(&max_value, false); + } + bool update(Field *from) + { + mysql_rwlock_wrlock(&lock); + bool res= update_unguarded(from); + mysql_rwlock_unlock(&lock); + return res; } my_time_t min_time() { - return min_value.get_timestamp(); + mysql_rwlock_rdlock(&lock); + my_time_t res= min_value.get_timestamp(); + mysql_rwlock_unlock(&lock); + return res; } my_time_t max_time() { - return max_value.get_timestamp(); + mysql_rwlock_rdlock(&lock); + my_time_t res= max_value.get_timestamp(); + mysql_rwlock_unlock(&lock); + return res; } }; -class partition_element :public Sql_alloc { +enum stat_trx_field +{ + STAT_TRX_END= 0 +}; + +class partition_element :public Sql_alloc +{ public: List subpartitions; List list_val_list; @@ -142,6 +168,7 @@ public: bool signed_flag; // Range value signed bool max_value; // MAXVALUE range uint32 id; + bool empty; enum elem_type { @@ -151,7 +178,6 @@ public: }; elem_type type; - Stat_timestampf *stat_trx_end; partition_element() : part_max_rows(0), part_min_rows(0), range_value(0), @@ -162,10 +188,9 @@ public: nodegroup_id(UNDEF_NODEGROUP), has_null_value(FALSE), signed_flag(FALSE), max_value(FALSE), id(UINT32_MAX), - type(CONVENTIONAL), - stat_trx_end(NULL) - { - } + empty(true), + type(CONVENTIONAL) + {} partition_element(partition_element *part_elem) : part_max_rows(part_elem->part_max_rows), part_min_rows(part_elem->part_min_rows), @@ -180,11 +205,19 @@ public: nodegroup_id(part_elem->nodegroup_id), has_null_value(FALSE), id(part_elem->id), - type(part_elem->type), - stat_trx_end(NULL) + empty(part_elem->empty), + type(part_elem->type) + {} + ~partition_element() {} + + part_column_list_val& get_col_val(uint idx) { + DBUG_ASSERT(type != CONVENTIONAL); + DBUG_ASSERT(list_val_list.elements == 1); + part_elem_value *ev= static_cast(list_val_list.first_node()->info); + DBUG_ASSERT(ev && ev->col_val_array); + return ev->col_val_array[idx]; } - ~partition_element() {} }; #endif /* PARTITION_ELEMENT_INCLUDED */ diff --git a/sql/partition_info.cc b/sql/partition_info.cc index e83fced4495..f45b45548b0 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -21,6 +21,7 @@ #endif #include +#include #include "sql_priv.h" // Required to get server definitions for mysql/plugin.h right #include "sql_plugin.h" @@ -796,6 +797,7 @@ bool partition_info::vers_init_info(THD * thd) part_type= VERSIONING_PARTITION; list_of_part_fields= TRUE; column_list= TRUE; + num_columns= 1; vers_info= new (thd->mem_root) Vers_part_info; if (!vers_info) { @@ -854,8 +856,11 @@ partition_element* partition_info::vers_part_rotate(THD * thd) { DBUG_ASSERT(table && table->s); - if (table->s->free_parts.is_empty()) + DBUG_ASSERT(vers_info && vers_info->initialized()); + + if (table->s->hist_part_id >= vers_info->now_part->id - 1) { + DBUG_ASSERT(table->s->hist_part_id == vers_info->now_part->id - 1); push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, WARN_VERS_PART_FULL, @@ -864,8 +869,7 @@ partition_info::vers_part_rotate(THD * thd) return vers_info->hist_part; } - table->s->vers_part_rotate(); - DBUG_ASSERT(table->s->hist_part_id < num_parts); + table->s->hist_part_id++; const char* old_part_name= vers_info->hist_part->partition_name; vers_hist_part(); @@ -879,18 +883,100 @@ partition_info::vers_part_rotate(THD * thd) return vers_info->hist_part; } -bool partition_info::vers_setup_1(THD * thd) +bool partition_info::vers_setup_1(THD * thd, uint32 added) { DBUG_ASSERT(part_type == VERSIONING_PARTITION); + if (!table->versioned()) { my_error(ER_VERSIONING_REQUIRED, MYF(0), "`BY SYSTEM_TIME` partitioning"); return true; } - Field *sys_trx_end= table->vers_end_field(); - part_field_list.empty(); - part_field_list.push_back(const_cast(sys_trx_end->field_name), thd->mem_root); - sys_trx_end->flags|= GET_FIXED_FIELDS_FLAG; // needed in handle_list_of_fields() + + if (added) + { + DBUG_ASSERT(partitions.elements > added + 1); + Vers_field_stats** old_array= table->s->stat_trx; + table->s->stat_trx= static_cast( + alloc_root(&table->s->mem_root, sizeof(void *) * partitions.elements * num_columns)); + memcpy(table->s->stat_trx, old_array, sizeof(void *) * (partitions.elements - added) * num_columns); + } + else + { + Field *sys_trx_end= table->vers_end_field(); + part_field_list.empty(); + part_field_list.push_back(const_cast(sys_trx_end->field_name), thd->mem_root); + DBUG_ASSERT(part_field_list.elements == num_columns); + // needed in handle_list_of_fields() + sys_trx_end->flags|= GET_FIXED_FIELDS_FLAG; + } + + List_iterator it(partitions); + partition_element *el; + MYSQL_TIME t; + memset(&t, 0, sizeof(t)); + my_time_t ts= TIMESTAMP_MAX_VALUE - partitions.elements; + uint32 id= 0; + while ((el= it++)) + { + DBUG_ASSERT(el->type != partition_element::CONVENTIONAL); + ++ts; + if (added) + { + if (el->type == partition_element::VERSIONING && !el->empty) + { + ++id; + continue; + } + if (el->id == UINT32_MAX || el->type == partition_element::AS_OF_NOW) + { + DBUG_ASSERT(table && table->s); + Vers_field_stats *stat_trx_end= new (&table->s->mem_root) + Vers_field_stats(table->s->vers_end_field()->field_name, table->s); + table->s->stat_trx[id * num_columns + STAT_TRX_END]= stat_trx_end; + el->id= id++; + if (el->type == partition_element::AS_OF_NOW) + break; + goto create_col_val; + } + thd->variables.time_zone->gmt_sec_to_TIME(&t, ts); + for (uint i= 0; i < num_columns; ++i) + { + part_column_list_val &col_val= el->get_col_val(i); + static_cast(col_val.item_expression)->set_time(&t); + col_val.fixed= 0; + } + ++id; + continue; + } + + create_col_val: + curr_part_elem= el; + init_column_part(thd); + el->list_val_list.empty(); + el->list_val_list.push_back(curr_list_val, thd->mem_root); + thd->variables.time_zone->gmt_sec_to_TIME(&t, ts); + for (uint i= 0; i < num_columns; ++i) + { + part_column_list_val *col_val= add_column_value(thd); + if (el->type == partition_element::AS_OF_NOW) + { + col_val->max_value= true; + col_val->item_expression= NULL; + col_val->column_value= NULL; + col_val->part_info= this; + col_val->fixed= 1; + continue; + } + Item *item_expression= new (thd->mem_root) Item_datetime_literal(thd, &t); + /* We initialize col_val with bogus max value to make fix_partition_func() and check_range_constants() happy. + Later in vers_setup_2() it is initialized with real stat value if there will be any. */ + /* FIXME: TIME_RESULT in col_val is expensive. It should be INT_RESULT + (got to be fixed when InnoDB is supported). */ + init_col_val(col_val, item_expression); + DBUG_ASSERT(item_expression == el->get_col_val(i).item_expression); + } + } return false; } @@ -902,6 +988,8 @@ bool partition_info::vers_scan_min_max(THD *thd, partition_element *part) uint32 sub_factor= num_subparts ? num_subparts : 1; uint32 part_id= part->id * sub_factor; uint32 part_id_end= part_id + sub_factor; + DBUG_ASSERT(part->empty); + DBUG_ASSERT(table->s->stat_trx); for (; part_id < part_id_end; ++part_id) { handler *file= table->file->part_handler(part_id); @@ -913,6 +1001,8 @@ bool partition_info::vers_scan_min_max(THD *thd, partition_element *part) { while ((rc= file->ha_rnd_next(table->record[0])) != HA_ERR_END_OF_FILE) { + if (part->empty) + part->empty= false; if (thd->killed) { file->ha_rnd_end(); @@ -924,7 +1014,7 @@ bool partition_info::vers_scan_min_max(THD *thd, partition_element *part) continue; break; } - part->stat_trx_end->update(table->vers_end_field()); + vers_stat_trx(STAT_TRX_END, part).update_unguarded(table->vers_end_field()); } file->ha_rnd_end(); } @@ -939,12 +1029,49 @@ bool partition_info::vers_scan_min_max(THD *thd, partition_element *part) return false; } +void partition_info::vers_update_col_vals(THD *thd, partition_element *el0, partition_element *el1) +{ + MYSQL_TIME t; + memset(&t, 0, sizeof(t)); + DBUG_ASSERT(table && table->s && table->s->stat_trx); + DBUG_ASSERT(!el0 || el1->id == el0->id + 1); + const uint idx= el1->id * num_columns; + my_time_t ts; + part_column_list_val *col_val; + Item_datetime_literal *val_item; + Vers_field_stats *stat_trx_x; + for (uint i= 0; i < num_columns; ++i) + { + stat_trx_x= table->s->stat_trx[idx + i]; + if (el0) + { + ts= stat_trx_x->min_time(); + thd->variables.time_zone->gmt_sec_to_TIME(&t, ts); + col_val= &el0->get_col_val(i); + val_item= static_cast(col_val->item_expression); + DBUG_ASSERT(val_item); + if (val_item->set_lower(&t)) + col_val->fixed= 0; + } + col_val= &el1->get_col_val(i); + if (!col_val->max_value) + { + ts= stat_trx_x->max_time() + 1; + thd->variables.time_zone->gmt_sec_to_TIME(&t, ts); + val_item= static_cast(col_val->item_expression); + DBUG_ASSERT(val_item); + if (val_item->set_higher(&t)) + col_val->fixed= 0; + } + } +} + // setup at open stage (TABLE_SHARE is initialized) bool partition_info::vers_setup_2(THD * thd, bool is_create_table_ind) { DBUG_ASSERT(part_type == VERSIONING_PARTITION); - DBUG_ASSERT(vers_info && vers_info->initialized(is_create_table_ind) && vers_info->hist_default != UINT32_MAX); + DBUG_ASSERT(vers_info && vers_info->initialized(false)); DBUG_ASSERT(table && table->s); if (!table->versioned_by_sql()) { @@ -961,38 +1088,78 @@ bool partition_info::vers_setup_2(THD * thd, bool is_create_table_ind) { table->s->busy_rotation= true; mysql_mutex_unlock(&table->s->LOCK_rotation); + + DBUG_ASSERT(part_field_list.elements == num_columns); + + bool dont_stat= true; + bool col_val_updated= false; + if (!table->s->stat_trx) + { + DBUG_ASSERT(partitions.elements > 1); + table->s->stat_trx= static_cast( + alloc_root(&table->s->mem_root, sizeof(void *) * partitions.elements * num_columns)); + dont_stat= false; + } + // build freelist, scan min/max, assign hist_part List_iterator it(partitions); - partition_element *el; - while ((el= it++)) + partition_element *el= NULL, *prev; + while ((prev= el, el= it++)) { - DBUG_ASSERT(el->type != partition_element::CONVENTIONAL); - if (el->type == partition_element::VERSIONING) + if (el->type == partition_element::VERSIONING && dont_stat) + { + if (el->id == table->s->hist_part_id) + { + vers_info->hist_part= el; + break; + } + continue; + } + + { + Vers_field_stats *stat_trx_end= new (&table->s->mem_root) + Vers_field_stats(table->s->vers_end_field()->field_name, table->s); + table->s->stat_trx[el->id * num_columns + STAT_TRX_END]= stat_trx_end; + } + + if (!is_create_table_ind) { - DBUG_ASSERT(!el->stat_trx_end); - el->stat_trx_end= new (&table->mem_root) - Stat_timestampf(table->s->vers_end_field()->field_name, table->s); - if (!is_create_table_ind && vers_scan_min_max(thd, el)) + if (vers_scan_min_max(thd, el)) return true; + if (!el->empty) + { + vers_update_col_vals(thd, prev, el); + col_val_updated= true; + } } - if (el == vers_info->now_part || el == vers_info->hist_part) - continue; - if (!vers_info->hist_part && el->id == vers_info->hist_default) + + if (el->type == partition_element::AS_OF_NOW) + break; + + DBUG_ASSERT(el->type == partition_element::VERSIONING); + + if (vers_info->hist_part) { - vers_info->hist_part= el; + if (!el->empty) + goto set_hist_part; } - if (is_create_table_ind || ( - table->s->free_parts_init && - !vers_limit_exceed(el) && - !vers_interval_exceed(el))) + else { - table->s->free_parts.push_back((void *) el->id, &table->s->mem_root); + set_hist_part: + vers_info->hist_part= el; + continue; } + } // while + + if (!dont_stat) + { + if (col_val_updated) + table->s->stat_serial++; + + table->s->hist_part_id= vers_info->hist_part->id; + if (!is_create_table_ind && (vers_limit_exceed() || vers_interval_exceed())) + vers_part_rotate(thd); } - table->s->hist_part_id= vers_info->hist_part->id; - if (!is_create_table_ind && (vers_limit_exceed() || vers_interval_exceed())) - vers_part_rotate(thd); - table->s->free_parts_init= false; mysql_mutex_lock(&table->s->LOCK_rotation); mysql_cond_broadcast(&table->s->COND_rotation); table->s->busy_rotation= false; @@ -1184,7 +1351,7 @@ error: called for RANGE PARTITIONed tables. */ -bool partition_info::check_range_constants(THD *thd) +bool partition_info::check_range_constants(THD *thd, bool init) { partition_element* part_def; bool first= TRUE; @@ -1201,12 +1368,15 @@ bool partition_info::check_range_constants(THD *thd) part_column_list_val *UNINIT_VAR(current_largest_col_val); uint num_column_values= part_field_list.elements; uint size_entries= sizeof(part_column_list_val) * num_column_values; - range_col_array= (part_column_list_val*) thd->calloc(num_parts * - size_entries); - if (unlikely(range_col_array == NULL)) + if (init) { - mem_alloc_error(num_parts * size_entries); - goto end; + range_col_array= (part_column_list_val*) thd->calloc(num_parts * + size_entries); + if (unlikely(range_col_array == NULL)) + { + mem_alloc_error(num_parts * size_entries); + goto end; + } } loc_range_col_array= range_col_array; i= 0; @@ -1239,11 +1409,14 @@ bool partition_info::check_range_constants(THD *thd) longlong part_range_value; bool signed_flag= !part_expr->unsigned_flag; - range_int_array= (longlong*) thd->alloc(num_parts * sizeof(longlong)); - if (unlikely(range_int_array == NULL)) + if (init) { - mem_alloc_error(num_parts * sizeof(longlong)); - goto end; + range_int_array= (longlong*) thd->alloc(num_parts * sizeof(longlong)); + if (unlikely(range_int_array == NULL)) + { + mem_alloc_error(num_parts * sizeof(longlong)); + goto end; + } } i= 0; do @@ -1609,7 +1782,6 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type, char *same_name; uint32 hist_parts= 0; uint32 now_parts= 0; - const char* hist_default= NULL; DBUG_ENTER("partition_info::check_partition_info"); DBUG_ASSERT(default_engine_type != partition_hton); @@ -1816,13 +1988,6 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type, if (part_elem->type == partition_element::VERSIONING) { hist_parts++; - if (vers_info->hist_default == UINT32_MAX) - { - vers_info->hist_default= part_elem->id; - hist_default= part_elem->partition_name; - } - if (vers_info->hist_default == part_elem->id) - vers_info->hist_part= part_elem; } else { @@ -1861,7 +2026,7 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type, if (add_or_reorg_part) { - if (unlikely((part_type == RANGE_PARTITION && + if (unlikely(((part_type == RANGE_PARTITION || part_type == VERSIONING_PARTITION) && check_range_constants(thd)) || (part_type == LIST_PARTITION && check_list_constants(thd)))) @@ -1878,14 +2043,6 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type, ER_THD(thd, WARN_VERS_PARAMETERS), "no rotation condition for multiple `VERSIONING` partitions."); } - if (hist_default) - { - push_warning_printf(thd, - Sql_condition::WARN_LEVEL_WARN, - WARN_VERS_PARAMETERS, - "No `DEFAULT` for `VERSIONING` partitions. Setting `%s` as default.", - hist_default); - } } if (now_parts > 1) { diff --git a/sql/partition_info.h b/sql/partition_info.h index 844956594ff..5a671bfc50f 100644 --- a/sql/partition_info.h +++ b/sql/partition_info.h @@ -41,7 +41,7 @@ struct Vers_part_info : public Sql_alloc limit(0), now_part(NULL), hist_part(NULL), - hist_default(UINT32_MAX) + stat_serial(0) { } Vers_part_info(Vers_part_info &src) : @@ -49,7 +49,7 @@ struct Vers_part_info : public Sql_alloc limit(src.limit), now_part(NULL), hist_part(NULL), - hist_default(src.hist_default) + stat_serial(src.stat_serial) { } bool initialized(bool fully= true) @@ -71,7 +71,7 @@ struct Vers_part_info : public Sql_alloc ulonglong limit; partition_element *now_part; partition_element *hist_part; - uint32 hist_default; + ulonglong stat_serial; }; class partition_info : public Sql_alloc @@ -182,8 +182,9 @@ public: LIST_PART_ENTRY *list_array; part_column_list_val *range_col_array; part_column_list_val *list_col_array; - Vers_part_info *vers_info; }; + + Vers_part_info *vers_info; /******************************************** * INTERVAL ANALYSIS @@ -350,7 +351,7 @@ public: char *find_duplicate_field(); char *find_duplicate_name(); bool check_engine_mix(handlerton *engine_type, bool default_engine); - bool check_range_constants(THD *thd); + bool check_range_constants(THD *thd, bool init= true); bool check_list_constants(THD *thd); bool check_partition_info(THD *thd, handlerton **eng_type, handler *file, HA_CREATE_INFO *info, @@ -400,9 +401,10 @@ public: bool vers_set_interval(const INTERVAL &i); bool vers_set_limit(ulonglong limit); partition_element* vers_part_rotate(THD *thd); - bool vers_setup_1(THD *thd); + bool vers_setup_1(THD *thd, uint32 added= 0); bool vers_setup_2(THD *thd, bool is_create_table_ind); bool vers_scan_min_max(THD *thd, partition_element *part); + void vers_update_col_vals(THD *thd, partition_element *el0, partition_element *el1); partition_element *vers_hist_part() { @@ -427,6 +429,17 @@ public: DBUG_ASSERT(0); return NULL; } + partition_element *get_partition(uint part_id) + { + List_iterator it(partitions); + partition_element *el; + while ((el= it++)) + { + if (el->id == part_id) + return el; + } + return NULL; + } bool vers_limit_exceed(partition_element *part= NULL) { DBUG_ASSERT(vers_info); @@ -440,6 +453,18 @@ public: // TODO: cache thread-shared part_recs and increment on INSERT return table->file->part_recs_slow(part) >= vers_info->limit; } + Vers_field_stats& vers_stat_trx(stat_trx_field fld, uint32 part_element_id) + { + DBUG_ASSERT(table && table->s && table->s->stat_trx); + Vers_field_stats* res= table->s->stat_trx[part_element_id * num_columns + fld]; + DBUG_ASSERT(res); + return *res; + } + Vers_field_stats& vers_stat_trx(stat_trx_field fld, partition_element *part) + { + DBUG_ASSERT(part); + return vers_stat_trx(fld, part->id); + } bool vers_interval_exceed(my_time_t max_time, partition_element *part= NULL) { DBUG_ASSERT(vers_info); @@ -450,19 +475,63 @@ public: DBUG_ASSERT(vers_info->initialized()); part= vers_hist_part(); } - DBUG_ASSERT(part->stat_trx_end); - max_time-= part->stat_trx_end->min_time(); + max_time-= vers_stat_trx(STAT_TRX_END, part).min_time(); return max_time > vers_info->interval; } bool vers_interval_exceed(partition_element *part) { - DBUG_ASSERT(part->stat_trx_end); - return vers_interval_exceed(part->stat_trx_end->max_time(), part); + return vers_interval_exceed(vers_stat_trx(STAT_TRX_END, part).max_time(), part); } bool vers_interval_exceed() { return vers_interval_exceed(vers_hist_part()); } + void vers_update_stats(THD *thd, partition_element *el) + { + DBUG_ASSERT(vers_info && vers_info->initialized()); + DBUG_ASSERT(table && table->s); + DBUG_ASSERT(el && el->type == partition_element::VERSIONING); + mysql_rwlock_wrlock(&table->s->LOCK_stat_serial); + el->empty= false; + bool updated= + vers_stat_trx(STAT_TRX_END, el->id).update(table->vers_end_field()); + if (updated) + table->s->stat_serial++; + mysql_rwlock_unlock(&table->s->LOCK_stat_serial); + if (updated) + { + vers_update_col_vals(thd, + el->id > 0 ? get_partition(el->id - 1) : NULL, + el); + } + } + void vers_update_stats(THD *thd, uint part_id) + { + DBUG_ASSERT(vers_info && vers_info->initialized()); + if (part_id < vers_info->now_part->id) + vers_update_stats(thd, get_partition(part_id)); + } + void vers_update_range_constants(THD *thd) + { + DBUG_ASSERT(vers_info && vers_info->initialized()); + DBUG_ASSERT(table && table->s); + + mysql_rwlock_rdlock(&table->s->LOCK_stat_serial); + if (vers_info->stat_serial == table->s->stat_serial) + { + mysql_rwlock_unlock(&table->s->LOCK_stat_serial); + return; + } + + for (uint i= 0; i < num_columns; ++i) + { + Field *f= part_field_array[i]; + bitmap_set_bit(f->table->write_set, f->field_index); + } + check_range_constants(thd, false); + vers_info->stat_serial= table->s->stat_serial; + mysql_rwlock_unlock(&table->s->LOCK_stat_serial); + } }; uint32 get_next_partition_id_range(struct st_partition_iter* part_iter); diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 816cd374e67..b358fe3386e 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -1716,6 +1716,8 @@ bool fix_partition_func(THD *thd, TABLE *table, else if (part_info->part_type == VERSIONING_PARTITION) { error_str= partition_keywords[PKW_SYSTEM_TIME].str; + if (unlikely(part_info->check_range_constants(thd))) + goto end; } else { @@ -2396,9 +2398,6 @@ static int add_partition_values(File fptr, partition_info *part_info, break; case partition_element::VERSIONING: err+= add_string(fptr, " VERSIONING"); - DBUG_ASSERT(part_info->vers_info); - if (part_info->vers_info->hist_default == p_elem->id) - err+= add_string(fptr, " DEFAULT"); break; default: DBUG_ASSERT(0 && "wrong p_elem->type"); @@ -3423,7 +3422,8 @@ int vers_get_partition_id(partition_info *part_info, longlong *func_value) { DBUG_ENTER("vers_get_partition_id"); - Field *sys_trx_end= part_info->part_field_array[0]; + DBUG_ASSERT(part_info); + Field *sys_trx_end= part_info->part_field_array[STAT_TRX_END]; DBUG_ASSERT(sys_trx_end); DBUG_ASSERT(part_info->table); Vers_part_info *vers_info= part_info->vers_info; @@ -3438,7 +3438,6 @@ int vers_get_partition_id(partition_info *part_info, } else // row is historical { - partition_element *part= vers_info->hist_part; THD *thd= current_thd; TABLE *table= part_info->table; @@ -3461,18 +3460,13 @@ int vers_get_partition_id(partition_info *part_info, mysql_mutex_unlock(&table->s->LOCK_rotation); if (part_info->vers_limit_exceed() || part_info->vers_interval_exceed(sys_trx_end->get_timestamp())) { - part= part_info->vers_part_rotate(thd); + part_info->vers_part_rotate(thd); } mysql_mutex_lock(&table->s->LOCK_rotation); mysql_cond_broadcast(&table->s->COND_rotation); table->s->busy_rotation= false; } mysql_mutex_unlock(&table->s->LOCK_rotation); - if (vers_info->interval) - { - DBUG_ASSERT(part->stat_trx_end); - part->stat_trx_end->update(sys_trx_end); - } break; default: ; @@ -5295,6 +5289,21 @@ that are reorganised. partition configuration is made. */ { + partition_element *now_part= NULL; + if (tab_part_info->part_type == VERSIONING_PARTITION) + { + List_iterator it(tab_part_info->partitions); + partition_element *el; + while ((el= it++)) + { + if (el->type == partition_element::AS_OF_NOW) + { + DBUG_ASSERT(tab_part_info->vers_info && el == tab_part_info->vers_info->now_part); + it.remove(); + now_part= el; + } + } + } List_iterator alt_it(alt_part_info->partitions); uint part_count= 0; do @@ -5309,6 +5318,15 @@ that are reorganised. } } while (++part_count < num_new_partitions); tab_part_info->num_parts+= num_new_partitions; + if (tab_part_info->part_type == VERSIONING_PARTITION) + { + DBUG_ASSERT(now_part); + if (tab_part_info->partitions.push_back(now_part, thd->mem_root)) + { + mem_alloc_error(1); + goto err; + } + } } /* If we specify partitions explicitly we don't use defaults anymore. @@ -5697,6 +5715,12 @@ the generated partition syntax in a correct manner. tab_part_info->use_default_subpartitions= FALSE; tab_part_info->use_default_num_subpartitions= FALSE; } + + if (alter_info->flags & Alter_info::ALTER_ADD_PARTITION && + tab_part_info->part_type == VERSIONING_PARTITION && + tab_part_info->vers_setup_1(thd, alt_part_info->partitions.elements)) + goto err; + if (tab_part_info->check_partition_info(thd, (handlerton**)NULL, table->file, 0, TRUE)) { @@ -7548,6 +7572,7 @@ static void set_up_range_analysis_info(partition_info *part_info) switch (part_info->part_type) { case RANGE_PARTITION: case LIST_PARTITION: + case VERSIONING_PARTITION: if (!part_info->column_list) { if (part_info->part_expr->get_monotonicity_info() != NON_MONOTONIC) @@ -7848,7 +7873,7 @@ int get_part_iter_for_interval_cols_via_map(partition_info *part_info, uint full_length= 0; DBUG_ENTER("get_part_iter_for_interval_cols_via_map"); - if (part_info->part_type == RANGE_PARTITION) + if (part_info->part_type == RANGE_PARTITION || part_info->part_type == VERSIONING_PARTITION) { get_col_endpoint= get_partition_id_cols_range_for_endpoint; part_iter->get_next= get_next_partition_id_range; @@ -7894,7 +7919,7 @@ int get_part_iter_for_interval_cols_via_map(partition_info *part_info, } if (flags & NO_MAX_RANGE) { - if (part_info->part_type == RANGE_PARTITION) + if (part_info->part_type == RANGE_PARTITION || part_info->part_type == VERSIONING_PARTITION) part_iter->part_nums.end= part_info->num_parts; else /* LIST_PARTITION */ { diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 2f1346eeb40..f66c79a52ca 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -5197,6 +5197,7 @@ opt_part_values: { LEX *lex= Lex; partition_info *part_info= lex->part_info; + partition_element *elem= part_info->curr_part_elem; if (! lex->is_partition_management()) { if (part_info->part_type != VERSIONING_PARTITION) @@ -5205,17 +5206,22 @@ opt_part_values: } else { - part_info->vers_init_info(thd); + // FIXME: other ALTER commands? + my_yyabort_error((ER_VERS_WRONG_PARAMS, MYF(0), "BY SYSTEM_TIME", "AS OF NOW partition can not be added")); } - partition_element *elem= part_info->curr_part_elem; elem->type= partition_element::AS_OF_NOW; DBUG_ASSERT(part_info->vers_info); part_info->vers_info->now_part= elem; + if (part_info->init_column_part(thd)) + { + MYSQL_YYABORT; + } } | VERSIONING_SYM { LEX *lex= Lex; partition_info *part_info= lex->part_info; + partition_element *elem= part_info->curr_part_elem; if (! lex->is_partition_management()) { if (part_info->part_type != VERSIONING_PARTITION) @@ -5225,10 +5231,17 @@ opt_part_values: else { part_info->vers_init_info(thd); + elem->id= UINT32_MAX; + } + DBUG_ASSERT(part_info->vers_info); + if (part_info->vers_info->now_part) + my_yyabort_error((ER_VERS_WRONG_PARAMS, MYF(0), "BY SYSTEM_TIME", "AS OF NOW partition is not last")); + elem->type= partition_element::VERSIONING; + if (part_info->init_column_part(thd)) + { + MYSQL_YYABORT; } - part_info->curr_part_elem->type= partition_element::VERSIONING; } - opt_default_hist_part | DEFAULT { LEX *lex= Lex; @@ -5556,20 +5569,6 @@ opt_versioning_limit: } ; -opt_default_hist_part: - /* empty */ {} - | DEFAULT - { - partition_info *part_info= Lex->part_info; - DBUG_ASSERT(part_info && part_info->vers_info && part_info->curr_part_elem); - if (part_info->vers_info->hist_part) - my_yyabort_error((ER_VERS_WRONG_PARAMS, MYF(0), - "BY SYSTEM_TIME", "multiple `DEFAULT` partitions")); - part_info->vers_info->hist_part= part_info->curr_part_elem; - part_info->vers_info->hist_default= part_info->curr_part_elem->id; - } - ; - /* End of partition parser part */ diff --git a/sql/table.cc b/sql/table.cc index 4b7d5ef0d6f..354658ba476 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -3226,6 +3226,7 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share, } #ifdef WITH_PARTITION_STORAGE_ENGINE + bool work_part_info_used; if (share->partition_info_str_len && outparam->file) { /* @@ -3246,7 +3247,6 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share, thd->set_n_backup_active_arena(&part_func_arena, &backup_arena); thd->stmt_arena= &part_func_arena; bool tmp; - bool work_part_info_used; tmp= mysql_unpack_partition(thd, share->partition_info_str, share->partition_info_str_len, @@ -3412,12 +3412,32 @@ partititon_err: #ifdef WITH_PARTITION_STORAGE_ENGINE if (outparam->part_info && - outparam->part_info->part_type == VERSIONING_PARTITION && - outparam->part_info->vers_setup_2(thd, is_create_table)) + outparam->part_info->part_type == VERSIONING_PARTITION) { - error= OPEN_FRM_OPEN_ERROR; - error_reported= true; - goto err; + Query_arena *backup_stmt_arena_ptr= thd->stmt_arena; + Query_arena backup_arena; + Query_arena part_func_arena(&outparam->mem_root, + Query_arena::STMT_INITIALIZED); + if (!work_part_info_used) + { + thd->set_n_backup_active_arena(&part_func_arena, &backup_arena); + thd->stmt_arena= &part_func_arena; + } + + bool err= outparam->part_info->vers_setup_2(thd, is_create_table); + + if (!work_part_info_used) + { + thd->stmt_arena= backup_stmt_arena_ptr; + thd->restore_active_arena(&part_func_arena, &backup_arena); + } + + if (err) + { + error= OPEN_FRM_OPEN_ERROR; + error_reported= true; + goto err; + } } #endif diff --git a/sql/table.h b/sql/table.h index cb24d211261..402b2548873 100644 --- a/sql/table.h +++ b/sql/table.h @@ -561,6 +561,8 @@ struct TABLE_STATISTICS_CB bool histograms_are_read; }; +class Vers_field_stats; + #ifndef UINT32_MAX #define UINT32_MAX (4294967295U) #endif @@ -752,26 +754,30 @@ struct TABLE_SHARE uint16 row_start_field; uint16 row_end_field; uint32 hist_part_id; - List free_parts; - bool free_parts_init; + Vers_field_stats** stat_trx; + ulonglong stat_serial; // guards check_range_constants() updates + bool busy_rotation; mysql_mutex_t LOCK_rotation; mysql_cond_t COND_rotation; + mysql_rwlock_t LOCK_stat_serial; void vers_init() { hist_part_id= UINT32_MAX; busy_rotation= false; - free_parts.empty(); - free_parts_init= true; + stat_trx= NULL; + stat_serial= 0; mysql_mutex_init(key_TABLE_SHARE_LOCK_rotation, &LOCK_rotation, MY_MUTEX_INIT_FAST); mysql_cond_init(key_TABLE_SHARE_COND_rotation, &COND_rotation, NULL); + mysql_rwlock_init(key_rwlock_LOCK_stat_serial, &LOCK_stat_serial); } void vers_destroy() { mysql_mutex_destroy(&LOCK_rotation); mysql_cond_destroy(&COND_rotation); + mysql_rwlock_destroy(&LOCK_stat_serial); } Field *vers_start_field() @@ -784,12 +790,6 @@ struct TABLE_SHARE return field[row_end_field]; } - void vers_part_rotate() - { - DBUG_ASSERT(!free_parts.is_empty()); - hist_part_id= (ulong)(void *)(free_parts.pop()); - } - void vers_wait_rotation() { while (busy_rotation) -- cgit v1.2.1 From 94f83b262d7405c2259984be819d479362b289d4 Mon Sep 17 00:00:00 2001 From: kevg Date: Sat, 21 Jan 2017 16:38:57 +0300 Subject: SQL: fix assertion failure in parser [closes #119] --- sql/sql_lex.cc | 13 ------------- sql/sql_yacc.yy | 27 ++++++++++++++++----------- 2 files changed, 16 insertions(+), 24 deletions(-) (limited to 'sql') diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 75a4c3bbc0c..5260e81ff8b 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1368,19 +1368,6 @@ int MYSQLlex(YYSTYPE *yylval, THD *thd) return FOR_SYM; } break; - case QUERY_SYM: - token= lex_one_token(yylval, thd); - lip->add_digest_token(token, yylval); - switch(token) { - case FOR_SYM: - return QUERY_FOR_SYM; - default: - lip->lookahead_yylval= lip->yylval; - lip->yylval= NULL; - lip->lookahead_token= token; - return QUERY_SYM; - } - break; default: break; } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index f66c79a52ca..7a17b90a191 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -861,10 +861,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %parse-param { THD *thd } %lex-param { THD *thd } /* - Currently there are 103 shift/reduce conflicts. + Currently there are 106 shift/reduce conflicts. We should not introduce new conflicts any more. */ -%expect 103 +%expect 106 /* Comments for TOKENS. @@ -1353,7 +1353,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token PURGE %token QUARTER_SYM %token QUERY_SYM -%token QUERY_FOR_SYM /* INTERNAL */ %token QUICK %token RAISE_SYM /* Oracle-PLSQL-R */ %token RANGE_SYM /* SQL-2003-R */ @@ -8813,7 +8812,7 @@ trans_or_timestamp: opt_query_for_system_time_clause: /* empty */ {} - | QUERY_FOR_SYM SYSTEM_TIME_SYM for_system_time_expr + | QUERY_SYM FOR_SYSTEM_TIME_SYM for_system_time_expr { DBUG_ASSERT(Select); Select->vers_conditions= Lex->vers_conditions; @@ -11690,15 +11689,21 @@ date_time_type: | TIMESTAMP {$$=MYSQL_TIMESTAMP_DATETIME;} ; -table_alias: - /* empty */ - | AS - | '=' - ; - opt_table_alias: /* empty */ { $$=0; } - | table_alias ident + | ident + { + $$= (LEX_STRING*) thd->memdup(&$1,sizeof(LEX_STRING)); + if ($$ == NULL) + MYSQL_YYABORT; + } + | AS ident + { + $$= (LEX_STRING*) thd->memdup(&$2,sizeof(LEX_STRING)); + if ($$ == NULL) + MYSQL_YYABORT; + } + | '=' ident { $$= (LEX_STRING*) thd->memdup(&$2,sizeof(LEX_STRING)); if ($$ == NULL) -- cgit v1.2.1 From 5853266fab57fa695403f260fed97257bc31f13b Mon Sep 17 00:00:00 2001 From: kevg Date: Wed, 8 Feb 2017 21:54:13 +0300 Subject: Style: condition rewrite --- sql/sql_delete.cc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'sql') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 7d59d5164e1..27e112c3285 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -607,9 +607,12 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (table->versioned()) { bool row_is_alive= table->vers_end_field()->is_max(); - if (truncate_history && row_is_alive) - continue; - if (!truncate_history && !row_is_alive) + if (truncate_history) + { + if (row_is_alive) + continue; + } + else if (!row_is_alive) continue; } -- cgit v1.2.1 From 7aa3ebdd189c1ba5b8556959839362514190dbd1 Mon Sep 17 00:00:00 2001 From: kevg Date: Tue, 31 Jan 2017 22:41:38 +0300 Subject: SQL, Tests: FOR SYSTEM_TIME for VIEWs [closes #98] --- sql/sql_base.cc | 13 +++++++++++++ sql/sql_select.cc | 48 +++++++++++++++++++++++++++++++++++++----------- sql/sql_view.cc | 45 ++++++++++++++++++++++++++++++++++++++++----- sql/table.h | 4 ---- 4 files changed, 90 insertions(+), 20 deletions(-) (limited to 'sql') diff --git a/sql/sql_base.cc b/sql/sql_base.cc index eb8bcf2148f..02adbb656c1 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -7576,6 +7576,19 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, if (f->field->flags & HIDDEN_FLAG) continue; } + if (item->type() == Item::REF_ITEM) + { + Item *i= item; + while (i->type() == Item::REF_ITEM) + i= *((Item_ref *)i)->ref; + if (i->type() == Item::FIELD_ITEM) + { + Item_field *f= (Item_field *)i; + DBUG_ASSERT(f->field); + if (f->field->flags & HIDDEN_FLAG) + continue; + } + } /* cache the table for the Item_fields inserted by expanding stars */ if (item->type() == Item::FIELD_ITEM && tables->cacheable_table) diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 2d4d603c078..c41801401e7 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -684,6 +684,11 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, DBUG_RETURN(0); } + while (tables && tables->is_view() && !thd->stmt_arena->is_stmt_prepare()) + { + tables= tables->view->select_lex.table_list.first; + } + for (table= tables; table; table= table->next_local) { if (table->table && table->table->versioned()) @@ -790,10 +795,7 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, } if (vers_conditions.type == FOR_SYSTEM_TIME_ALL) - { - vers_conditions.unwrapped= true; continue; - } } COND** dst_cond= where_expr; @@ -954,8 +956,6 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, if (arena) thd->restore_active_arena(arena, &backup); - slex->vers_conditions.unwrapped= true; - DBUG_RETURN(0); #undef newx } @@ -16728,6 +16728,8 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List &fields, List_iterator_fast li(fields); Item *item; Field **tmp_from_field=from_field; + Field *sys_trx_start= NULL; + Field *sys_trx_end= NULL; while ((item=li++)) { Item::Type type=item->type(); @@ -16843,6 +16845,20 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List &fields, goto err; // Got OOM continue; // Some kind of const item } + + if (type == Item::FIELD_ITEM) + { + Item_field *item_field= (Item_field *)item; + Field *field= item_field->field; + TABLE_SHARE *s= field->table->s; + if (s->versioned) + { + if (field->flags & VERS_SYS_START_FLAG) + sys_trx_start= new_field; + else if (field->flags & VERS_SYS_END_FLAG) + sys_trx_end= new_field; + } + } if (type == Item::SUM_FUNC_ITEM) { Item_sum *agg_item= (Item_sum *) item; @@ -16923,6 +16939,17 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List &fields, total_uneven_bit_length= 0; } } + + if (sys_trx_start && sys_trx_end) + { + sys_trx_start->flags|= VERS_SYS_START_FLAG | HIDDEN_FLAG; + sys_trx_end->flags|= VERS_SYS_END_FLAG | HIDDEN_FLAG; + share->versioned= true; + share->field= table->field; + share->row_start_field= field_count - 2; + share->row_end_field= field_count - 1; + } + DBUG_ASSERT(fieldnr == (uint) (reg_field - table->field)); DBUG_ASSERT(field_count >= (uint) (reg_field - table->field)); field_count= fieldnr; @@ -25416,6 +25443,11 @@ void TABLE_LIST::print(THD *thd, table_map eliminated_tables, String *str, append_identifier(thd, str, t_alias, strlen(t_alias)); } + if (table && table->versioned()) + { + // versioning conditions are already unwrapped to WHERE clause + str->append(" FOR SYSTEM_TIME ALL"); + } if (index_hints) { @@ -25576,12 +25608,6 @@ void st_select_lex::print(THD *thd, String *str, enum_query_type query_type) str->append(having_value != Item::COND_FALSE ? "1" : "0"); } - if (vers_conditions.unwrapped) - { - // versioning conditions are already unwrapped to WHERE clause - str->append(STRING_WITH_LEN(" query for system_time all ")); - } - if (order_list.elements) { str->append(STRING_WITH_LEN(" order by ")); diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 08f42e9ce70..8ee4a75e259 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -453,6 +453,26 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, goto err; } + /* Implicitly add versioning fields if needed */ + { + TABLE_LIST *tl = tables; + while (tl && tl->is_view()) + tl = tl->view->select_lex.table_list.first; + if (tl && tl->table) + { + TABLE_SHARE *s= tl->table->s; + if (s->versioned) + { + select_lex->item_list.push_back(new (thd->mem_root) Item_field( + thd, &select_lex->context, NULL, NULL, + s->vers_start_field()->field_name)); + select_lex->item_list.push_back(new (thd->mem_root) Item_field( + thd, &select_lex->context, NULL, NULL, + s->vers_end_field()->field_name)); + } + } + } + view= lex->unlink_first_table(&link_to_local); if (check_db_dir_existence(view->db)) @@ -605,14 +625,22 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, view->table_name, item->name) & VIEW_ANY_ACL); - if (fld && !fld->field->table->s->tmp_table) + if (!fld) + continue; + TABLE_SHARE *s= fld->field->table->s; + const char *field_name= fld->field->field_name; + if (s->tmp_table || + (s->versioned && + (!strcmp(field_name, s->vers_start_field()->field_name) || + !strcmp(field_name, s->vers_end_field()->field_name)))) { + continue; + } - final_priv&= fld->have_privileges; + final_priv&= fld->have_privileges; - if (~fld->have_privileges & priv) - report_item= item; - } + if (~fld->have_privileges & priv) + report_item= item; } } @@ -2027,7 +2055,14 @@ bool insert_view_fields(THD *thd, List *list, TABLE_LIST *view) { Item_field *fld; if ((fld= entry->item->field_for_view_update())) + { + TABLE_SHARE *s= fld->context->table_list->table->s; + if (s->versioned && + (!strcmp(fld->name, s->vers_start_field()->field_name) || + !strcmp(fld->name, s->vers_end_field()->field_name))) + continue; list->push_back(fld, thd->mem_root); + } else { my_error(ER_NON_INSERTABLE_TABLE, MYF(0), view->alias, "INSERT"); diff --git a/sql/table.h b/sql/table.h index 402b2548873..12e5830089d 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1864,14 +1864,11 @@ struct vers_select_conds_t vers_range_unit_t unit; Item *start, *end; - bool unwrapped; - void empty() { type= FOR_SYSTEM_TIME_UNSPECIFIED; unit= UNIT_TIMESTAMP; start= end= NULL; - unwrapped= false; } void init( @@ -1884,7 +1881,6 @@ struct vers_select_conds_t unit= u; start= s; end= e; - unwrapped= false; } }; -- cgit v1.2.1 From bcc8ba78bc89b075c169d4ab414f59922401ebeb Mon Sep 17 00:00:00 2001 From: kevg Date: Thu, 9 Feb 2017 12:01:05 +0300 Subject: SQL, Tests: versioning for nested queries and CTE [closes #74] --- sql/item.cc | 9 ++++++++- sql/item.h | 2 ++ sql/sql_derived.cc | 20 ++++++++++++++++++++ sql/sql_select.cc | 28 ++++++++++++++++++++++------ 4 files changed, 52 insertions(+), 7 deletions(-) (limited to 'sql') diff --git a/sql/item.cc b/sql/item.cc index 410fa32f234..80a38e4d1d2 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -10271,7 +10271,8 @@ void Item_cache_row::set_null() Item_type_holder::Item_type_holder(THD *thd, Item *item) :Item(thd, item), Type_handler_hybrid_field_type(item->real_type_handler()), - enum_set_typelib(0) + enum_set_typelib(0), + flags(0) { DBUG_ASSERT(item->fixed); maybe_null= item->maybe_null; @@ -10282,6 +10283,12 @@ Item_type_holder::Item_type_holder(THD *thd, Item *item) if (item->field_type() == MYSQL_TYPE_GEOMETRY) geometry_type= item->get_geometry_type(); #endif /* HAVE_SPATIAL */ + if (item->real_type() == Item::FIELD_ITEM) + { + Item_field *item_field= (Item_field*)item->real_item(); + flags|= + (item_field->field->flags & (VERS_SYS_START_FLAG | VERS_SYS_END_FLAG)); + } } diff --git a/sql/item.h b/sql/item.h index 357eabd72e1..f7bc1110898 100644 --- a/sql/item.h +++ b/sql/item.h @@ -5892,6 +5892,8 @@ public: Field *make_field_by_type(TABLE *table); Field::geometry_type get_geometry_type() const { return geometry_type; }; Item* get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; } + + uint flags; }; diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index 827fb21edf4..43302498d57 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -713,6 +713,26 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived) cursor= cursor->next_local) cursor->outer_join|= JOIN_TYPE_OUTER; } + if ((thd->stmt_arena->is_stmt_prepare() || + !thd->stmt_arena->is_stmt_execute()) && + !derived->is_view() && sl->table_list.elements > 0) + { + TABLE_LIST *tl= sl->table_list.first; + if (tl->table && tl->table->versioned()) + { + TABLE_SHARE *s= tl->table->s; + const char *db= tl->db; + const char *alias= tl->alias; + Query_arena backup; + Query_arena *arena= thd->activate_stmt_arena_if_needed(&backup); + sl->item_list.push_back(new (thd->mem_root) Item_field( + thd, &sl->context, db, alias, s->vers_start_field()->field_name)); + sl->item_list.push_back(new (thd->mem_root) Item_field( + thd, &sl->context, db, alias, s->vers_end_field()->field_name)); + if (arena) + thd->restore_active_arena(arena, &backup); + } + } } unit->derived= derived; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index c41801401e7..2ba2f2e7234 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -807,12 +807,20 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, Field *fstart= table->table->vers_start_field(); Field *fend= table->table->vers_end_field(); - DBUG_ASSERT(slex->parent_lex); - Name_resolution_context *context= slex->parent_lex->current_context(); - DBUG_ASSERT(context); - - Item *row_start= newx Item_field(thd, context, fstart); - Item *row_end= newx Item_field(thd, context, fend); + Item *row_start= NULL; + Item *row_end= NULL; + if (table->is_derived() && !table->is_recursive_with_table()) + { + row_start= newx Item_field(thd, &slex->context, NULL, NULL, + fstart->field_name); + row_end= + newx Item_field(thd, &slex->context, NULL, NULL, fend->field_name); + } + else + { + row_start= newx Item_field(thd, &slex->context, fstart); + row_end= newx Item_field(thd, &slex->context, fend); + } Item *row_end2= row_end; if (table->table->versioned_by_sql()) @@ -16859,6 +16867,14 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List &fields, sys_trx_end= new_field; } } + if (type == Item::TYPE_HOLDER) + { + Item_type_holder *ith= (Item_type_holder*)item; + if (ith->flags & VERS_SYS_START_FLAG) + sys_trx_start= new_field; + else if (ith->flags & VERS_SYS_END_FLAG) + sys_trx_end= new_field; + } if (type == Item::SUM_FUNC_ITEM) { Item_sum *agg_item= (Item_sum *) item; -- cgit v1.2.1 From fc7da4dd4f1e2b9b78b292f20d8fe61f1e9a1d11 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Mon, 20 Feb 2017 10:06:58 +0300 Subject: IB, SQL: InnoDB partitioning [closes #118] * native InnoDB partitioning for BY SYSTEM_TIME partitions. --- sql/CMakeLists.txt | 4 +- sql/ha_partition.cc | 23 +- sql/ha_partition.h | 117 +- sql/handler.cc | 6 + sql/handler.h | 27 +- sql/partition_info.cc | 192 +- sql/partition_info.h | 49 +- sql/partitioning/partition_handler.cc | 3746 +++++++++++++++++++++++++++++++++ sql/partitioning/partition_handler.h | 1113 ++++++++++ sql/share/errmsg-utf8.txt | 9 + sql/sql_partition.cc | 142 +- sql/sql_partition.h | 34 + sql/sql_table.cc | 5 +- sql/sql_tablespace.cc | 64 + sql/sql_tablespace.h | 35 + sql/table.cc | 14 + 16 files changed, 5533 insertions(+), 47 deletions(-) create mode 100644 sql/partitioning/partition_handler.cc create mode 100644 sql/partitioning/partition_handler.h (limited to 'sql') diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 1dfa313a70c..08a39b1975d 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -121,7 +121,7 @@ SET (SQL_SOURCE rpl_tblmap.cc sql_binlog.cc event_scheduler.cc event_data_objects.cc event_queue.cc event_db_repository.cc sql_tablespace.cc events.cc ../sql-common/my_user.c - partition_info.cc rpl_utility.cc rpl_injector.cc sql_locale.cc + partition_info.cc partitioning/partition_handler.cc rpl_utility.cc rpl_injector.cc sql_locale.cc rpl_rli.cc rpl_mi.cc sql_servers.cc sql_audit.cc sql_connect.cc scheduler.cc sql_partition_admin.cc sql_profile.cc event_parse_data.cc sql_alter.cc @@ -165,7 +165,7 @@ IF (CMAKE_SYSTEM_NAME MATCHES "Linux" OR ENDIF() -MYSQL_ADD_PLUGIN(partition ha_partition.cc STORAGE_ENGINE DEFAULT STATIC_ONLY +MYSQL_ADD_PLUGIN(partition ha_partition.cc partitioning/partition_handler.cc STORAGE_ENGINE DEFAULT STATIC_ONLY RECOMPILE_FOR_EMBEDDED) MYSQL_ADD_PLUGIN(sql_sequence ha_sequence.cc STORAGE_ENGINE MANDATORY STATIC_ONLY RECOMPILE_FOR_EMBEDDED) diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 0fa461e1807..747b9a8871f 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -160,9 +160,6 @@ static int partition_initialize(void *p) bool Partition_share::init(uint num_parts) { DBUG_ENTER("Partition_share::init"); - mysql_mutex_init(key_partition_auto_inc_mutex, - &auto_inc_mutex, - MY_MUTEX_INIT_FAST); auto_inc_initialized= false; partition_name_hash_initialized= false; next_auto_inc_val= 0; @@ -1246,12 +1243,12 @@ int ha_partition::handle_opt_part(THD *thd, HA_CHECK_OPT *check_opt, (modelled after mi_check_print_msg) TODO: move this into the handler, or rewrite mysql_admin_table. */ -static bool print_admin_msg(THD* thd, uint len, +bool print_admin_msg(THD* thd, uint len, const char* msg_type, const char* db_name, String &table_name, const char* op_name, const char *fmt, ...) ATTRIBUTE_FORMAT(printf, 7, 8); -static bool print_admin_msg(THD* thd, uint len, +bool print_admin_msg(THD* thd, uint len, const char* msg_type, const char* db_name, String &table_name, const char* op_name, const char *fmt, ...) @@ -5731,6 +5728,22 @@ int ha_partition::index_next_same(uchar *buf, const uchar *key, uint keylen) } +int ha_partition::index_read_last_map(uchar *buf, + const uchar *key, + key_part_map keypart_map) +{ + DBUG_ENTER("ha_partition::index_read_last_map"); + + m_ordered= true; // Safety measure + end_range= NULL; + m_index_scan_type= partition_index_read_last; + m_start_key.key= key; + m_start_key.keypart_map= keypart_map; + m_start_key.flag= HA_READ_PREFIX_LAST; + DBUG_RETURN(common_index_read(buf, true)); +} + + /* Read next record when performing index scan backwards diff --git a/sql/ha_partition.h b/sql/ha_partition.h index 2c7f4a0861f..861ba47b94e 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -77,43 +77,118 @@ public: }; +extern PSI_mutex_key key_partition_auto_inc_mutex; + /** Partition specific Handler_share. */ class Partition_share : public Handler_share { public: - bool auto_inc_initialized; - mysql_mutex_t auto_inc_mutex; /**< protecting auto_inc val */ - ulonglong next_auto_inc_val; /**< first non reserved value */ - /** - Hash of partition names. Initialized in the first ha_partition::open() - for the table_share. After that it is read-only, i.e. no locking required. - */ - bool partition_name_hash_initialized; - HASH partition_name_hash; - /** Storage for each partitions Handler_share */ - Parts_share_refs *partitions_share_refs; - Partition_share() {} + Partition_share() + : auto_inc_initialized(false), + next_auto_inc_val(0), + partition_name_hash_initialized(false), + partitions_share_refs(NULL), + partition_names(NULL) + { + mysql_mutex_init(key_partition_auto_inc_mutex, + &auto_inc_mutex, + MY_MUTEX_INIT_FAST); + } + ~Partition_share() { - DBUG_ENTER("Partition_share::~Partition_share"); mysql_mutex_destroy(&auto_inc_mutex); + if (partition_names) + { + my_free(partition_names); + } if (partition_name_hash_initialized) + { my_hash_free(&partition_name_hash); + } if (partitions_share_refs) delete partitions_share_refs; - DBUG_VOID_RETURN; } + bool init(uint num_parts); - void lock_auto_inc() + + /** Set if auto increment is used an initialized. */ + bool auto_inc_initialized; + /** + Mutex protecting next_auto_inc_val. + Initialized if table uses auto increment. + */ + mysql_mutex_t auto_inc_mutex; + /** First non reserved auto increment value. */ + ulonglong next_auto_inc_val; + /** + Hash of partition names. Initialized by the first handler instance of a + table_share calling populate_partition_name_hash(). + After that it is read-only, i.e. no locking required for reading. + */ + HASH partition_name_hash; + /** flag that the name hash is initialized, so it only will do it once. */ + bool partition_name_hash_initialized; + + /** Storage for each partitions Handler_share */ + Parts_share_refs *partitions_share_refs; + + /** + Release reserved auto increment values not used. + @param thd Thread. + @param table_share Table Share + @param next_insert_id Next insert id (first non used auto inc value). + @param max_reserved End of reserved auto inc range. + */ + void release_auto_inc_if_possible(THD *thd, TABLE_SHARE *table_share, + const ulonglong next_insert_id, + const ulonglong max_reserved); + + /** lock mutex protecting auto increment value next_auto_inc_val. */ + inline void lock_auto_inc() { mysql_mutex_lock(&auto_inc_mutex); } - void unlock_auto_inc() + /** unlock mutex protecting auto increment value next_auto_inc_val. */ + inline void unlock_auto_inc() { mysql_mutex_unlock(&auto_inc_mutex); } + /** + Populate partition_name_hash with partition and subpartition names + from part_info. + @param part_info Partition info containing all partitions metadata. + + @return Operation status. + @retval false Success. + @retval true Failure. + */ + bool populate_partition_name_hash(partition_info *part_info); + /** Get partition name. + + @param part_id Partition id (for subpartitioned table only subpartition + names will be returned.) + + @return partition name or NULL if error. + */ + const char *get_partition_name(size_t part_id) const; +private: + const uchar **partition_names; + /** + Insert [sub]partition name into partition_name_hash + @param name Partition name. + @param part_id Partition id. + @param is_subpart True if subpartition else partition. + + @return Operation status. + @retval false Success. + @retval true Failure. + */ + bool insert_partition_name_in_hash(const char *name, + uint part_id, + bool is_subpart); }; @@ -605,6 +680,10 @@ public: virtual int index_last(uchar * buf); virtual int index_next_same(uchar * buf, const uchar * key, uint keylen); + int index_read_last_map(uchar *buf, + const uchar *key, + key_part_map keypart_map); + /* read_first_row is virtual method but is only implemented by handler.cc, no storage engine has implemented it so neither @@ -1086,7 +1165,6 @@ private: ulonglong nr= (((Field_num*) field)->unsigned_flag || field->val_int() > 0) ? field->val_int() : 0; lock_auto_increment(); - DBUG_ASSERT(part_share->auto_inc_initialized); /* must check when the mutex is taken */ if (nr >= part_share->next_auto_inc_val) part_share->next_auto_inc_val= nr + 1; @@ -1310,4 +1388,9 @@ public: friend int cmp_key_rowid_part_id(void *ptr, uchar *ref1, uchar *ref2); }; +bool print_admin_msg(THD* thd, uint len, + const char* msg_type, + const char* db_name, String &table_name, + const char* op_name, const char *fmt, ...); + #endif /* HA_PARTITION_INCLUDED */ diff --git a/sql/handler.cc b/sql/handler.cc index c19d04236d7..ba947fd7a2d 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -2435,6 +2435,12 @@ LEX_STRING *handler::engine_name() } +void handler::ha_statistic_increment(ulong SSV::*offset) const +{ + (table->in_use->status_var.*offset)++; +} + + double handler::keyread_time(uint index, uint ranges, ha_rows rows) { /* diff --git a/sql/handler.h b/sql/handler.h index e20f95df1f3..f5e3d83d8d9 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -1393,6 +1393,7 @@ struct handlerton bool (*vers_query_trx_id)(THD* thd, void *out, ulonglong trx_id, vtq_field_t field); bool (*vers_query_commit_ts)(THD* thd, void *out, const MYSQL_TIME &commit_ts, vtq_field_t field, bool backwards); bool (*vers_trx_sees)(THD *thd, bool &result, ulonglong trx_id1, ulonglong trx_id0, ulonglong commit_id1, uchar iso_level1, ulonglong commit_id0); + handler *(*vers_upgrade_handler)(handler *hnd, MEM_ROOT *mem_root); }; @@ -3271,6 +3272,18 @@ protected: virtual int index_last(uchar * buf) { return HA_ERR_WRONG_COMMAND; } virtual int index_next_same(uchar *buf, const uchar *key, uint keylen); + /** + @brief + The following functions works like index_read, but it find the last + row with the current key value or prefix. + @returns @see index_read_map(). + */ + virtual int index_read_last_map(uchar * buf, const uchar * key, + key_part_map keypart_map) + { + uint key_len= calculate_key_len(table, active_index, key, keypart_map); + return index_read_last(buf, key, key_len); + } virtual int close(void)=0; inline void update_rows_read() { @@ -3350,7 +3363,7 @@ public: void ft_end() { ft_handler=NULL; } virtual FT_INFO *ft_init_ext(uint flags, uint inx,String *key) { return NULL; } -private: +public: virtual int ft_read(uchar *buf) { return HA_ERR_WRONG_COMMAND; } virtual int rnd_next(uchar *buf)=0; virtual int rnd_pos(uchar * buf, uchar *pos)=0; @@ -4057,6 +4070,7 @@ public: TABLE_SHARE* get_table_share() { return table_share; } protected: /* Service methods for use by storage engines. */ + void ha_statistic_increment(ulong SSV::*offset) const; void **ha_data(THD *) const; THD *ha_thd(void) const; @@ -4082,7 +4096,7 @@ protected: public: bool check_table_binlog_row_based(bool binlog_row); -private: + /* Cache result to avoid extra calls */ inline void mark_trx_read_write() { @@ -4092,6 +4106,8 @@ private: mark_trx_read_write_internal(); } } + +private: void mark_trx_read_write_internal(); bool check_table_binlog_row_based_internal(bool binlog_row); @@ -4210,6 +4226,11 @@ protected: virtual int index_read(uchar * buf, const uchar * key, uint key_len, enum ha_rkey_function find_flag) { return HA_ERR_WRONG_COMMAND; } + virtual int index_read_last(uchar * buf, const uchar * key, uint key_len) + { + my_errno= HA_ERR_WRONG_COMMAND; + return HA_ERR_WRONG_COMMAND; + } friend class ha_partition; friend class ha_sequence; public: @@ -4340,6 +4361,8 @@ public: { DBUG_ASSERT(0); return false; } virtual handler* part_handler(uint32 part_id) { DBUG_ASSERT(0); return NULL; } + virtual void update_partition(uint part_id) + {} protected: Handler_share *get_ha_share_ptr(); void set_ha_share_ptr(Handler_share *arg_ha_share); diff --git a/sql/partition_info.cc b/sql/partition_info.cc index f45b45548b0..c1a792c87e0 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -208,6 +208,48 @@ bool partition_info::set_named_partition_bitmap(const char *part_name, +/** + Prune away partitions not mentioned in the PARTITION () clause, + if used. + + @param table_list Table list pointing to table to prune. + + @return Operation status + @retval false Success + @retval true Failure +*/ +bool partition_info::set_read_partitions(List *partition_names) +{ + DBUG_ENTER("partition_info::set_read_partitions"); + if (!partition_names || !partition_names->elements) + { + DBUG_RETURN(true); + } + + uint num_names= partition_names->elements; + List_iterator partition_names_it(*partition_names); + uint i= 0; + /* + TODO: When adding support for FK in partitioned tables, the referenced + table must probably lock all partitions for read, and also write depending + of ON DELETE/UPDATE. + */ + bitmap_clear_all(&read_partitions); + + /* No check for duplicate names or overlapping partitions/subpartitions. */ + + DBUG_PRINT("info", ("Searching through partition_name_hash")); + do + { + char *part_name= partition_names_it++; + if (add_named_partition(part_name, strlen(part_name))) + DBUG_RETURN(true); + } while (++i < num_names); + DBUG_RETURN(false); +} + + + /** Prune away partitions not mentioned in the PARTITION () clause, if used. @@ -989,13 +1031,22 @@ bool partition_info::vers_scan_min_max(THD *thd, partition_element *part) uint32 part_id= part->id * sub_factor; uint32 part_id_end= part_id + sub_factor; DBUG_ASSERT(part->empty); + DBUG_ASSERT(part->type == partition_element::VERSIONING); DBUG_ASSERT(table->s->stat_trx); for (; part_id < part_id_end; ++part_id) { - handler *file= table->file->part_handler(part_id); - int rc= file->ha_external_lock(thd, F_RDLCK); + handler *file= table->file->part_handler(part_id); // requires update_partition() for ha_innopart + int rc= file->ha_external_lock(thd, F_RDLCK); // requires ha_commit_trans() for ha_innobase if (rc) - goto error; + { + file->update_partition(part_id); + goto lock_fail; + } + + table->default_column_bitmaps(); + bitmap_set_bit(table->read_set, table->vers_end_field()->field_index); + file->column_bitmaps_signal(); + rc= file->ha_rnd_init(true); if (!rc) { @@ -1006,6 +1057,8 @@ bool partition_info::vers_scan_min_max(THD *thd, partition_element *part) if (thd->killed) { file->ha_rnd_end(); + file->update_partition(part_id); + ha_commit_trans(thd, false); return true; } if (rc) @@ -1014,18 +1067,44 @@ bool partition_info::vers_scan_min_max(THD *thd, partition_element *part) continue; break; } - vers_stat_trx(STAT_TRX_END, part).update_unguarded(table->vers_end_field()); + if (table->vers_end_field()->is_max()) + { + rc= HA_ERR_INTERNAL_ERROR; + push_warning_printf(thd, + Sql_condition::WARN_LEVEL_WARN, + WARN_VERS_PART_NON_HISTORICAL, + ER_THD(thd, WARN_VERS_PART_NON_HISTORICAL), + part->partition_name); + break; + } + if (table->versioned_by_engine()) + { + uchar buf[8]; + Field_timestampf fld(buf, NULL, 0, Field::NONE, table->vers_end_field()->field_name, NULL, 6); + if (!vers_trx_id_to_ts(thd, table->vers_end_field(), fld)) + { + vers_stat_trx(STAT_TRX_END, part).update_unguarded(&fld); + } + } + else + { + vers_stat_trx(STAT_TRX_END, part).update_unguarded(table->vers_end_field()); + } } file->ha_rnd_end(); } file->ha_external_lock(thd, F_UNLCK); + file->update_partition(part_id); if (rc != HA_ERR_END_OF_FILE) { - error: - my_error(ER_INTERNAL_ERROR, MYF(0), "partition/subpartition scan failed in versioned partitions setup"); + ha_commit_trans(thd, false); + lock_fail: + // TODO: print rc code + my_error(ER_INTERNAL_ERROR, MYF(0), "min/max scan failed in versioned partitions setup (see warnings)"); return true; } } + ha_commit_trans(thd, false); return false; } @@ -1073,11 +1152,9 @@ bool partition_info::vers_setup_2(THD * thd, bool is_create_table_ind) DBUG_ASSERT(part_type == VERSIONING_PARTITION); DBUG_ASSERT(vers_info && vers_info->initialized(false)); DBUG_ASSERT(table && table->s); - if (!table->versioned_by_sql()) - { - my_error(ER_VERS_WRONG_PARAMS, MYF(0), table->s->table_name.str, "selected engine is not supported in `BY SYSTEM_TIME` partitioning"); - return true; - } + + bool error= false; + mysql_mutex_lock(&table->s->LOCK_rotation); if (table->s->busy_rotation) { @@ -1124,8 +1201,19 @@ bool partition_info::vers_setup_2(THD * thd, bool is_create_table_ind) if (!is_create_table_ind) { - if (vers_scan_min_max(thd, el)) - return true; + if (el->type == partition_element::AS_OF_NOW) + { + uchar buf[8]; + Field_timestampf fld(buf, NULL, 0, Field::NONE, table->vers_end_field()->field_name, NULL, 6); + fld.set_max(); + vers_stat_trx(STAT_TRX_END, el).update_unguarded(&fld); + el->empty= false; + } + else if (vers_scan_min_max(thd, el)) + { + error= true; + break; + } if (!el->empty) { vers_update_col_vals(thd, prev, el); @@ -1151,7 +1239,7 @@ bool partition_info::vers_setup_2(THD * thd, bool is_create_table_ind) } } // while - if (!dont_stat) + if (!error && !dont_stat) { if (col_val_updated) table->s->stat_serial++; @@ -1165,7 +1253,7 @@ bool partition_info::vers_setup_2(THD * thd, bool is_create_table_ind) table->s->busy_rotation= false; } mysql_mutex_unlock(&table->s->LOCK_rotation); - return false; + return error; } @@ -3262,6 +3350,80 @@ bool partition_info::has_same_partitioning(partition_info *new_part_info) } +static bool has_same_column_order(List *create_list, + Field** field_array) +{ + Field **f_ptr; + List_iterator_fast new_field_it; + Create_field *new_field= NULL; + new_field_it.init(*create_list); + + for (f_ptr= field_array; *f_ptr; f_ptr++) + { + while ((new_field= new_field_it++)) + { + if (new_field->field == *f_ptr) + break; + } + if (!new_field) + break; + } + + if (!new_field) + { + /* Not same order!*/ + return false; + } + return true; +} + +bool partition_info::vers_trx_id_to_ts(THD* thd, Field* in_trx_id, Field_timestamp& out_ts) +{ + handlerton *hton= plugin_hton(table->s->db_plugin); + DBUG_ASSERT(hton); + ulonglong trx_id= in_trx_id->val_int(); + MYSQL_TIME ts; + bool found= hton->vers_query_trx_id(thd, &ts, trx_id, VTQ_COMMIT_TS); + if (!found) + { + push_warning_printf(thd, + Sql_condition::WARN_LEVEL_WARN, + WARN_VERS_TRX_MISSING, + ER_THD(thd, WARN_VERS_TRX_MISSING), + trx_id); + return true; + } + out_ts.store_time_dec(&ts, 6); + return false; +} + + +/** + Check if the partitioning columns are in the same order as the given list. + + Used to see if INPLACE alter can be allowed or not. If the order is + different then the rows must be redistributed for KEY [sub]partitioning. + + @param[in] create_list Column list after ALTER TABLE. + @return true is same order as before ALTER TABLE, else false. +*/ +bool partition_info::same_key_column_order(List *create_list) +{ + /* Only need to check for KEY [sub] partitioning. */ + if (list_of_part_fields && !column_list) + { + if (!has_same_column_order(create_list, part_field_array)) + return false; + } + if (list_of_subpart_fields) + { + if (!has_same_column_order(create_list, subpart_field_array)) + return false; + } + return true; +} + + void partition_info::print_debug(const char *str, uint *value) { DBUG_ENTER("print_debug"); diff --git a/sql/partition_info.h b/sql/partition_info.h index 5a671bfc50f..ef20564837c 100644 --- a/sql/partition_info.h +++ b/sql/partition_info.h @@ -22,6 +22,7 @@ #include "sql_class.h" #include "partition_element.h" +#include "sql_partition.h" class partition_info; struct TABLE_LIST; @@ -382,6 +383,28 @@ public: uint32 *part_id); void report_part_expr_error(bool use_subpart_expr); bool has_same_partitioning(partition_info *new_part_info); + inline bool is_partition_used(uint part_id) const + { + return bitmap_is_set(&read_partitions, part_id); + } + inline bool is_partition_locked(uint part_id) const + { + return bitmap_is_set(&lock_partitions, part_id); + } + inline uint num_partitions_used() + { + return bitmap_bits_set(&read_partitions); + } + inline uint get_first_used_partition() const + { + return bitmap_get_first_set(&read_partitions); + } + inline uint get_next_used_partition(uint part_id) const + { + return bitmap_get_next_set(&read_partitions, part_id); + } + bool same_key_column_order(List *create_list); + private: static int list_part_cmp(const void* a, const void* b); bool set_up_default_partitions(THD *thd, handler *file, HA_CREATE_INFO *info, @@ -392,9 +415,11 @@ private: uint start_no); char *create_default_subpartition_name(THD *thd, uint subpart_no, const char *part_name); + // FIXME: prune_partition_bitmaps() is duplicate of set_read_partitions() bool prune_partition_bitmaps(TABLE_LIST *table_list); bool add_named_partition(const char *part_name, uint length); public: + bool set_read_partitions(List *partition_names); bool has_unique_name(partition_element *element); bool vers_init_info(THD *thd); @@ -475,8 +500,8 @@ public: DBUG_ASSERT(vers_info->initialized()); part= vers_hist_part(); } - max_time-= vers_stat_trx(STAT_TRX_END, part).min_time(); - return max_time > vers_info->interval; + my_time_t min_time= vers_stat_trx(STAT_TRX_END, part).min_time(); + return max_time - min_time > vers_info->interval; } bool vers_interval_exceed(partition_element *part) { @@ -486,15 +511,31 @@ public: { return vers_interval_exceed(vers_hist_part()); } + bool vers_trx_id_to_ts(THD *thd, Field *in_trx_id, Field_timestamp &out_ts); void vers_update_stats(THD *thd, partition_element *el) { DBUG_ASSERT(vers_info && vers_info->initialized()); DBUG_ASSERT(table && table->s); DBUG_ASSERT(el && el->type == partition_element::VERSIONING); + bool updated; mysql_rwlock_wrlock(&table->s->LOCK_stat_serial); el->empty= false; - bool updated= - vers_stat_trx(STAT_TRX_END, el->id).update(table->vers_end_field()); + if (table->versioned_by_engine()) + { + // transaction is not yet pushed to VTQ, so we use now-time + my_time_t end_ts= my_time(0); + + uchar buf[8]; + Field_timestampf fld(buf, NULL, 0, Field::NONE, table->vers_end_field()->field_name, NULL, 6); + fld.store_TIME(end_ts, 0); + updated= + vers_stat_trx(STAT_TRX_END, el->id).update(&fld); + } + else + { + updated= + vers_stat_trx(STAT_TRX_END, el->id).update(table->vers_end_field()); + } if (updated) table->s->stat_serial++; mysql_rwlock_unlock(&table->s->LOCK_stat_serial); diff --git a/sql/partitioning/partition_handler.cc b/sql/partitioning/partition_handler.cc new file mode 100644 index 00000000000..1e04439e100 --- /dev/null +++ b/sql/partitioning/partition_handler.cc @@ -0,0 +1,3746 @@ +/* + Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; version 2 of + the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "table.h" // TABLE_SHARE +#include "sql_partition.h" // LIST_PART_ENTRY, part_id_range +#include "partition_info.h" // NOT_A_PARTITION_ID +#include "partition_handler.h" +#include "log.h" // sql_print_error +#include "key.h" // key_rec_cmp +#include "sql_class.h" // THD +#include + +#define MI_MAX_MSG_BUF 1024 + +// In sql_class.cc: +extern "C" int thd_binlog_format(const MYSQL_THD thd); + +/** operation names for the enum_part_operation. */ +static const char *opt_op_name[]= {"optimize", "analyze", "check", "repair", + "assign_to_keycache", "preload_keys"}; + +// static PSI_memory_key key_memory_Partition_share; +// static PSI_memory_key key_memory_partition_sort_buffer; +// static PSI_memory_key key_memory_Partition_admin; +#ifdef HAVE_PSI_INTERFACE +extern PSI_mutex_key key_partition_auto_inc_mutex; +// static PSI_memory_info all_partitioning_memory[]= +// { { &key_memory_Partition_share, "Partition_share", 0}, +// { &key_memory_partition_sort_buffer, "partition_sort_buffer", 0}, +// { &key_memory_Partition_admin, "Partition_admin", 0} }; +static PSI_mutex_info all_partitioning_mutex[]= +{ { &key_partition_auto_inc_mutex, "Partiton_share::auto_inc_mutex", 0} }; +#endif + +void partitioning_init() +{ +#ifdef HAVE_PSI_INTERFACE + int count; +// count= array_elements(all_partitioning_memory); +// mysql_memory_register("sql", all_partitioning_memory, count); + count= array_elements(all_partitioning_mutex); + mysql_mutex_register("sql", all_partitioning_mutex, count); +#endif +} + + +/** + Release reserved auto increment values not used. + @param thd Thread. + @param table_share Table Share + @param next_insert_id Next insert id (first non used auto inc value). + @param max_reserved End of reserved auto inc range. +*/ +void +Partition_share::release_auto_inc_if_possible(THD *thd, TABLE_SHARE *table_share, + const ulonglong next_insert_id, + const ulonglong max_reserved) +{ +#ifndef DBUG_OFF + if (table_share->tmp_table == NO_TMP_TABLE) + { + mysql_mutex_assert_owner(&auto_inc_mutex); + } +#endif /* DBUG_OFF */ + + /* + If the current auto_increment values is lower than the reserved value (1) + and the reserved value was reserved by this thread (2), then we can + lower the reserved value. + However, we cannot lower the value if there are forced/non generated + values from 'SET INSERT_ID = forced_val' (3). */ + if (next_insert_id < next_auto_inc_val && // (1) + max_reserved >= next_auto_inc_val && // (2) + thd->auto_inc_intervals_forced.maximum() < next_insert_id) // (3) + { + next_auto_inc_val= next_insert_id; + } +} + + +/** + Get the partition name. + + @param part Struct containing name and length + @param[out] length Length of the name + + @return Partition name +*/ + +static uchar *get_part_name_from_def(PART_NAME_DEF *part, + size_t *length, + my_bool not_used MY_ATTRIBUTE((unused))) +{ + *length= part->length; + return part->partition_name; +} + + +/** + Populate the partition_name_hash in part_share. +*/ + +bool Partition_share::populate_partition_name_hash(partition_info *part_info) +{ + uint tot_names; + uint num_subparts= part_info->num_subparts; + DBUG_ENTER("Partition_share::populate_partition_name_hash"); + DBUG_ASSERT(!part_info->is_sub_partitioned() || num_subparts); + + if (num_subparts == 0) + { + num_subparts= 1; + } + + /* + TABLE_SHARE::LOCK_ha_data must been locked before calling this function. + This ensures only one thread/table instance will execute this. + */ + +#ifndef DBUG_OFF + if (part_info->table->s->tmp_table == NO_TMP_TABLE) + { + mysql_mutex_assert_owner(&part_info->table->s->LOCK_ha_data); + } +#endif + if (partition_name_hash_initialized) + { + DBUG_RETURN(false); + } + tot_names= part_info->num_parts; + if (part_info->is_sub_partitioned()) + { + tot_names+= part_info->num_parts * num_subparts; + } + partition_names= static_cast(my_malloc( + part_info->get_tot_partitions() * + sizeof(*partition_names), + MYF(MY_WME))); + if (!partition_names) + { + DBUG_RETURN(true); + } + if (my_hash_init(&partition_name_hash, + system_charset_info, tot_names, 0, 0, + (my_hash_get_key) get_part_name_from_def, + my_free, HASH_UNIQUE)) + { + my_free(partition_names); + partition_names= NULL; + DBUG_RETURN(true); + } + + List_iterator part_it(part_info->partitions); + uint i= 0; + do + { + partition_element *part_elem= part_it++; + DBUG_ASSERT(part_elem->part_state == PART_NORMAL); + if (part_elem->part_state == PART_NORMAL) + { + if (insert_partition_name_in_hash(part_elem->partition_name, + i * num_subparts, + false)) + goto err; + if (part_info->is_sub_partitioned()) + { + List_iterator + subpart_it(part_elem->subpartitions); + partition_element *sub_elem; + uint j= 0; + do + { + sub_elem= subpart_it++; + if (insert_partition_name_in_hash(sub_elem->partition_name, + i * num_subparts + j, true)) + goto err; + + } while (++j < num_subparts); + } + } + } while (++i < part_info->num_parts); + + for (i= 0; i < tot_names; i++) + { + PART_NAME_DEF *part_def; + part_def= reinterpret_cast( + my_hash_element(&partition_name_hash, i)); + if (part_def->is_subpart == part_info->is_sub_partitioned()) + { + partition_names[part_def->part_id]= part_def->partition_name; + } + } + partition_name_hash_initialized= true; + + DBUG_RETURN(false); +err: + my_hash_free(&partition_name_hash); + my_free(partition_names); + partition_names= NULL; + + DBUG_RETURN(true); +} + + +/** + Insert a partition name in the partition_name_hash. + + @param name Name of partition + @param part_id Partition id (number) + @param is_subpart Set if the name belongs to a subpartition + + @return Operation status + @retval true Failure + @retval false Success +*/ + +bool Partition_share::insert_partition_name_in_hash(const char *name, + uint part_id, + bool is_subpart) +{ + PART_NAME_DEF *part_def; + uchar *part_name; + uint part_name_length; + DBUG_ENTER("Partition_share::insert_partition_name_in_hash"); + /* + Calculate and store the length here, to avoid doing it when + searching the hash. + */ + part_name_length= static_cast(strlen(name)); + /* + Must use memory that lives as long as table_share. + Freed in the Partition_share destructor. + Since we use my_multi_malloc, then my_free(part_def) will also free + part_name, as a part of my_hash_free. + */ + if (!my_multi_malloc(MY_WME, + &part_def, sizeof(PART_NAME_DEF), + &part_name, part_name_length + 1, + NULL)) + { + DBUG_RETURN(true); + } + memcpy(part_name, name, part_name_length + 1); + part_def->partition_name= part_name; + part_def->length= part_name_length; + part_def->part_id= part_id; + part_def->is_subpart= is_subpart; + if (my_hash_insert(&partition_name_hash, (uchar *) part_def)) + { + my_free(part_def); + DBUG_RETURN(true); + } + DBUG_RETURN(false); +} + + +const char *Partition_share::get_partition_name(size_t part_id) const +{ + if (partition_names == NULL) + { + return NULL; + } + return reinterpret_cast(partition_names[part_id]); +} +/* + Implementation of Partition_helper class. +*/ +Partition_helper::Partition_helper(handler *main_handler) + : + m_handler(main_handler), + m_part_info(), + m_tot_parts(), + m_last_part(), + m_err_rec(), + m_ordered(), + m_ordered_scan_ongoing(), + m_ordered_rec_buffer(), + m_queue() +{} + + +Partition_helper::~Partition_helper() +{ + DBUG_ASSERT(m_ordered_rec_buffer == NULL); + DBUG_ASSERT(m_key_not_found_partitions.bitmap == NULL); +} + + +/** + Set partition info. + + To be called from Partition_handler. + + @param part_info Partition info to use. + @param early True if called when part_info only created and parsed, + but not setup, checked or fixed. + */ +void Partition_helper::set_part_info_low(partition_info *part_info, + bool early) +{ + /* + ha_partition will set m_tot_parts from the .par file during creating + the new handler. + And this call can be earlier than the partition_default_handling(), + so get_tot_partitions() may return zero. + */ + if (m_tot_parts == 0 && + (m_part_info == NULL || !early)) + { + m_tot_parts= part_info->get_tot_partitions(); + } + m_part_info= part_info; + m_is_sub_partitioned= m_part_info->is_sub_partitioned(); +} + +/** + Initialize the partitioning helper for use after the table is opened. + + @param part_share Partitioning share (used for auto increment). + + @return Operation status. + @retval false for success otherwise true. +*/ + +bool Partition_helper::open_partitioning(Partition_share *part_share) +{ + m_table= get_table(); + DBUG_ASSERT(m_part_info == m_table->part_info); + m_part_share= part_share; + m_tot_parts= m_part_info->get_tot_partitions(); + if (bitmap_init(&m_key_not_found_partitions, NULL, m_tot_parts, false)) + { + return true; + } + bitmap_clear_all(&m_key_not_found_partitions); + m_key_not_found= false; + m_is_sub_partitioned= m_part_info->is_sub_partitioned(); + m_auto_increment_lock= false; + m_auto_increment_safe_stmt_log_lock= false; + m_pkey_is_clustered= m_handler->primary_key_is_clustered(); + m_part_spec.start_part= NOT_A_PARTITION_ID; + m_part_spec.end_part= NOT_A_PARTITION_ID; + m_index_scan_type= PARTITION_NO_INDEX_SCAN; + m_start_key.key= NULL; + m_start_key.length= 0; + m_scan_value= 3; + m_reverse_order= false; + m_curr_key_info[0]= NULL; + m_curr_key_info[1]= NULL; + m_curr_key_info[2]= NULL; + m_top_entry= NO_CURRENT_PART_ID; + m_ref_usage= REF_NOT_USED; + m_rec_length= m_table->s->reclength; + return false; +} + + +void Partition_helper::close_partitioning() +{ + bitmap_free(&m_key_not_found_partitions); + DBUG_ASSERT(!m_ordered_rec_buffer); + destroy_record_priority_queue(); +} + +/**************************************************************************** + MODULE change record +****************************************************************************/ + +/** + Insert a row to the partitioned table. + + @param buf The row in MySQL Row Format. + + @return Operation status. + @retval 0 Success + @retval != 0 Error code +*/ + +int Partition_helper::ph_write_row(uchar *buf) +{ + uint32 part_id; + int error; + longlong func_value; + bool have_auto_increment= m_table->next_number_field && + buf == m_table->record[0]; + THD *thd= get_thd(); + sql_mode_t saved_sql_mode= thd->variables.sql_mode; + bool saved_auto_inc_field_not_null= m_table->auto_increment_field_not_null; +#ifndef DBUG_OFF + my_bitmap_map *old_map; +#endif /* DBUG_OFF */ + DBUG_ENTER("Partition_helper::ph_write_row"); + DBUG_ASSERT(buf == m_table->record[0]); + + /* + If we have an auto_increment column and we are writing a changed row + or a new row, then update the auto_increment value in the record. + */ + if (have_auto_increment) + { + error= m_handler->update_auto_increment(); + + /* + If we have failed to set the auto-increment value for this row, + it is highly likely that we will not be able to insert it into + the correct partition. We must check and fail if neccessary. + */ + if (error) + DBUG_RETURN(error); + + /* + Don't allow generation of auto_increment value the partitions handler. + If a partitions handler would change the value, then it might not + match the partition any longer. + This can occur if 'SET INSERT_ID = 0; INSERT (NULL)', + So allow this by adding 'MODE_NO_AUTO_VALUE_ON_ZERO' to sql_mode. + The partitions handler::next_insert_id must always be 0. Otherwise + we need to forward release_auto_increment, or reset it for all + partitions. + */ + if (m_table->next_number_field->val_int() == 0) + { + m_table->auto_increment_field_not_null= TRUE; + thd->variables.sql_mode|= MODE_NO_AUTO_VALUE_ON_ZERO; + } + } + +#ifndef DBUG_OFF + /* Temporary mark the partitioning fields as readable. */ + old_map= dbug_tmp_use_all_columns(m_table, m_table->read_set); +#endif /* DBUG_OFF */ + + error= m_part_info->get_partition_id(m_part_info, &part_id, &func_value); + +#ifndef DBUG_OFF + dbug_tmp_restore_column_map(m_table->read_set, old_map); +#endif /* DBUG_OFF */ + + if (unlikely(error)) + { + m_part_info->err_value= func_value; + goto exit; + } + if (!m_part_info->is_partition_locked(part_id)) + { + DBUG_PRINT("info", ("Write to non-locked partition %u (func_value: %ld)", + part_id, (long) func_value)); + error= HA_ERR_NOT_IN_LOCK_PARTITIONS; + goto exit; + } + m_last_part= part_id; + DBUG_PRINT("info", ("Insert in partition %d", part_id)); + + error= write_row_in_part(part_id, buf); + + if (have_auto_increment && !m_table->s->next_number_keypart) + { + set_auto_increment_if_higher(); + } +exit: + thd->variables.sql_mode= saved_sql_mode; + m_table->auto_increment_field_not_null= saved_auto_inc_field_not_null; + DBUG_RETURN(error); +} + + +/** + Update an existing row in the partitioned table. + + Yes, update_row() does what you expect, it updates a row. old_data will + have the previous row record in it, while new_data will have the newest + data in it. + Keep in mind that the server can do updates based on ordering if an + ORDER BY clause was used. Consecutive ordering is not guaranteed. + + If the new record belongs to a different partition than the old record + then it will be inserted into the new partition and deleted from the old. + + new_data is always record[0] + old_data is always record[1] + + @param old_data The old record in MySQL Row Format. + @param new_data The new record in MySQL Row Format. + + @return Operation status. + @retval 0 Success + @retval != 0 Error code +*/ + +// FIXME: duplicate of ha_partition::update_row() +int Partition_helper::ph_update_row(const uchar *old_data, uchar *new_data) +{ + THD *thd= get_thd(); + uint32 new_part_id, old_part_id; + int error= 0; + longlong func_value; + DBUG_ENTER("Partition_helper::ph_update_row"); + m_err_rec= NULL; + + // Need to read partition-related columns, to locate the row's partition: + DBUG_ASSERT(bitmap_is_subset(&m_part_info->full_part_field_set, + m_table->read_set)); + if ((error= get_parts_for_update(old_data, new_data, m_table->record[0], + m_part_info, &old_part_id, &new_part_id, + &func_value))) + { + m_part_info->err_value= func_value; + goto exit; + } + DBUG_ASSERT(bitmap_is_set(&(m_part_info->read_partitions), old_part_id)); + if (!bitmap_is_set(&(m_part_info->lock_partitions), new_part_id)) + { + error= HA_ERR_NOT_IN_LOCK_PARTITIONS; + goto exit; + } + + /* + The protocol for updating a row is: + 1) position the handler (cursor) on the row to be updated, + either through the last read row (rnd or index) or by rnd_pos. + 2) call update_row with both old and new full records as arguments. + + This means that m_last_part should already be set to actual partition + where the row was read from. And if that is not the same as the + calculated part_id we found a misplaced row, we return an error to + notify the user that something is broken in the row distribution + between partitions! Since we don't check all rows on read, we return an + error instead of correcting m_last_part, to make the user aware of the + problem! + + Notice that HA_READ_BEFORE_WRITE_REMOVAL does not require this protocol, + so this is not supported for this engine. + */ + if (old_part_id != m_last_part) + { + m_err_rec= old_data; + DBUG_RETURN(HA_ERR_ROW_IN_WRONG_PARTITION); + } + + m_last_part= new_part_id; + if (new_part_id == old_part_id) + { + DBUG_PRINT("info", ("Update in partition %d", new_part_id)); + tmp_disable_binlog(thd); /* Do not replicate the low-level changes. */ + error= update_row_in_part(new_part_id, old_data, new_data); + reenable_binlog(thd); + goto exit; + } + else + { + Field *saved_next_number_field= m_table->next_number_field; + /* + Don't allow generation of auto_increment value for update. + table->next_number_field is never set on UPDATE. + But is set for INSERT ... ON DUPLICATE KEY UPDATE, + and since update_row() does not generate or update an auto_inc value, + we cannot have next_number_field set when moving a row + to another partition with write_row(), since that could + generate/update the auto_inc value. + This gives the same behavior for partitioned vs non partitioned tables. + */ + m_table->next_number_field= NULL; + DBUG_PRINT("info", ("Update from partition %d to partition %d", + old_part_id, new_part_id)); + tmp_disable_binlog(thd); /* Do not replicate the low-level changes. */ + error= write_row_in_part(new_part_id, new_data); + reenable_binlog(thd); + m_table->next_number_field= saved_next_number_field; + if (error) + goto exit; + + if (m_part_info->part_type == VERSIONING_PARTITION) + { + uint sub_factor= m_part_info->num_subparts ? m_part_info->num_subparts : 1; + DBUG_ASSERT(m_tot_parts == m_part_info->num_parts * sub_factor); + uint lpart_id= new_part_id / sub_factor; + // lpart_id is VERSIONING partition because new_part_id != old_part_id + m_part_info->vers_update_stats(thd, lpart_id); + } + + tmp_disable_binlog(thd); /* Do not replicate the low-level changes. */ + error= delete_row_in_part(old_part_id, old_data); + reenable_binlog(thd); + if (error) + { + goto exit; + } + } + +exit: + /* + if updating an auto_increment column, update + m_part_share->next_auto_inc_val if needed. + (not to be used if auto_increment on secondary field in a multi-column + index) + mysql_update does not set table->next_number_field, so we use + table->found_next_number_field instead. + Also checking that the field is marked in the write set. + */ + if (m_table->found_next_number_field && + new_data == m_table->record[0] && + !m_table->s->next_number_keypart && + bitmap_is_set(m_table->write_set, + m_table->found_next_number_field->field_index)) + { + set_auto_increment_if_higher(); + } + DBUG_RETURN(error); +} + + +/** + Delete an existing row in the partitioned table. + + This will delete a row. buf will contain a copy of the row to be deleted. + The server will call this right after the current row has been read + (from either a previous rnd_xxx() or index_xxx() call). + If you keep a pointer to the last row or can access a primary key it will + make doing the deletion quite a bit easier. + Keep in mind that the server does no guarentee consecutive deletions. + ORDER BY clauses can be used. + + buf is either record[0] or record[1] + + @param buf The record in MySQL Row Format. + + @return Operation status. + @retval 0 Success + @retval != 0 Error code +*/ + +int Partition_helper::ph_delete_row(const uchar *buf) +{ + int error; + uint part_id; + DBUG_ENTER("Partition_helper::ph_delete_row"); + m_err_rec= NULL; + + DBUG_ASSERT(bitmap_is_subset(&m_part_info->full_part_field_set, + m_table->read_set)); + if ((error= get_part_for_delete(buf, + m_table->record[0], + m_part_info, + &part_id))) + { + DBUG_RETURN(error); + } + if (!m_part_info->is_partition_locked(part_id)) + { + DBUG_RETURN(HA_ERR_NOT_IN_LOCK_PARTITIONS); + } + + /* + The protocol for deleting a row is: + 1) position the handler (cursor) on the row to be deleted, + either through the last read row (rnd or index) or by rnd_pos. + 2) call delete_row with the full record as argument. + + This means that m_last_part should already be set to actual partition + where the row was read from. And if that is not the same as the + calculated part_id we found a misplaced row, we return an error to + notify the user that something is broken in the row distribution + between partitions! Since we don't check all rows on read, we return an + error instead of forwarding the delete to the correct (m_last_part) + partition! + + Notice that HA_READ_BEFORE_WRITE_REMOVAL does not require this protocol, + so this is not supported for this engine. + + TODO: change the assert in InnoDB into an error instead and make this one + an assert instead and remove the get_part_for_delete()! + */ + if (part_id != m_last_part) + { + m_err_rec= buf; + DBUG_RETURN(HA_ERR_ROW_IN_WRONG_PARTITION); + } + /* Should never call delete_row on a partition which is not read */ + DBUG_ASSERT(m_part_info->is_partition_used(part_id)); + + m_last_part= part_id; + error= delete_row_in_part(part_id, buf); + DBUG_RETURN(error); +} + + +/** + Get a range of auto increment values. + + Can only be used if the auto increment field is the first field in an index. + + This method is called by update_auto_increment which in turn is called + by the individual handlers as part of write_row. We use the + part_share->next_auto_inc_val, or search all + partitions for the highest auto_increment_value if not initialized or + if auto_increment field is a secondary part of a key, we must search + every partition when holding a mutex to be sure of correctness. + + @param[in] increment Increment value. + @param[in] nb_desired_values Number of desired values. + @param[out] first_value First auto inc value reserved + or MAX if failure. + @param[out] nb_reserved_values Number of values reserved. +*/ + +void Partition_helper +::get_auto_increment_first_field(ulonglong increment, + ulonglong nb_desired_values, + ulonglong *first_value, + ulonglong *nb_reserved_values) +{ + THD *thd= get_thd(); + DBUG_ENTER("Partition_helper::get_auto_increment_first_field"); + DBUG_PRINT("info", ("inc: %lu desired_values: %lu first_value: %lu", + (ulong) increment, + (ulong) nb_desired_values, + (ulong) *first_value)); + DBUG_ASSERT(increment && nb_desired_values); + /* + next_number_keypart is != 0 if the auto_increment column is a secondary + column in the index (it is allowed in MyISAM) + */ + DBUG_ASSERT(m_table->s->next_number_keypart == 0); + *first_value= 0; + + /* + Get a lock for handling the auto_increment in part_share + for avoiding two concurrent statements getting the same number. + */ + lock_auto_increment(); + + /* Initialize if not already done. */ + if (!m_part_share->auto_inc_initialized) + { + initialize_auto_increment(false); + } + + /* + In a multi-row insert statement like INSERT SELECT and LOAD DATA + where the number of candidate rows to insert is not known in advance + we must hold a lock/mutex for the whole statement if we have statement + based replication. Because the statement-based binary log contains + only the first generated value used by the statement, and slaves assumes + all other generated values used by this statement were consecutive to + this first one, we must exclusively lock the generator until the statement + is done. + */ + int binlog_format= thd_binlog_format(thd); + if (!m_auto_increment_safe_stmt_log_lock && + thd->lex->sql_command != SQLCOM_INSERT && + binlog_format != BINLOG_FORMAT_UNSPEC && + binlog_format != BINLOG_FORMAT_ROW) + { + DBUG_PRINT("info", ("locking auto_increment_safe_stmt_log_lock")); + m_auto_increment_safe_stmt_log_lock= true; + } + + /* this gets corrected (for offset/increment) in update_auto_increment */ + *first_value= m_part_share->next_auto_inc_val; + m_part_share->next_auto_inc_val+= nb_desired_values * increment; + if (m_part_share->next_auto_inc_val < *first_value) + { + /* Overflow, set to max. */ + m_part_share->next_auto_inc_val= ULLONG_MAX; + } + + unlock_auto_increment(); + DBUG_PRINT("info", ("*first_value: %lu", (ulong) *first_value)); + *nb_reserved_values= nb_desired_values; + DBUG_VOID_RETURN; +} + + +inline void Partition_helper::set_auto_increment_if_higher() +{ + Field_num *field= static_cast(m_table->found_next_number_field); + ulonglong nr= (field->unsigned_flag || field->val_int() > 0) + ? field->val_int() : 0; + lock_auto_increment(); + if (!m_part_share->auto_inc_initialized) + { + initialize_auto_increment(false); + } + /* must hold the mutex when looking/changing m_part_share. */ + if (nr >= m_part_share->next_auto_inc_val) + { + m_part_share->next_auto_inc_val= nr + 1; + } + unlock_auto_increment(); + save_auto_increment(nr); +} + + +void Partition_helper::ph_release_auto_increment() +{ + DBUG_ENTER("Partition_helper::ph_release_auto_increment"); + + if (m_table->s->next_number_keypart) + { + release_auto_increment_all_parts(); + } + else if (m_handler->next_insert_id) + { + ulonglong max_reserved= m_handler->auto_inc_interval_for_cur_row.maximum(); + lock_auto_increment(); + m_part_share->release_auto_inc_if_possible(get_thd(), m_table->s, + m_handler->next_insert_id, + max_reserved); + DBUG_PRINT("info", ("part_share->next_auto_inc_val: %lu", + (ulong) m_part_share->next_auto_inc_val)); + + /* Unlock the multi row statement lock taken in get_auto_increment */ + if (m_auto_increment_safe_stmt_log_lock) + { + m_auto_increment_safe_stmt_log_lock= FALSE; + DBUG_PRINT("info", ("unlocking auto_increment_safe_stmt_log_lock")); + } + + unlock_auto_increment(); + } + DBUG_VOID_RETURN; +} + + +/** + Calculate key hash value from an null terminated array of fields. + Support function for KEY partitioning. + + @param field_array An array of the fields in KEY partitioning + + @return hash_value calculated + + @note Uses the hash function on the character set of the field. + Integer and floating point fields use the binary character set by default. +*/ + +uint32 Partition_helper::ph_calculate_key_hash_value(Field **field_array) +{ + ulong nr1= 1; + ulong nr2= 4; + bool use_51_hash; + use_51_hash= MY_TEST((*field_array)->table->part_info->key_algorithm == + partition_info::KEY_ALGORITHM_51); + + do + { + Field *field= *field_array; + if (use_51_hash) + { + switch (field->real_type()) { + case MYSQL_TYPE_TINY: + case MYSQL_TYPE_SHORT: + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_FLOAT: + case MYSQL_TYPE_DOUBLE: + case MYSQL_TYPE_NEWDECIMAL: + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_LONGLONG: + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_YEAR: + case MYSQL_TYPE_NEWDATE: + { + if (field->is_null()) + { + nr1^= (nr1 << 1) | 1; + continue; + } + /* Force this to my_hash_sort_bin, which was used in 5.1! */ + uint len= field->pack_length(); + my_charset_bin.coll->hash_sort(&my_charset_bin, field->ptr, len, + &nr1, &nr2); + /* Done with this field, continue with next one. */ + continue; + } + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_BIT: + /* Not affected, same in 5.1 and 5.5 */ + break; + /* + ENUM/SET uses my_hash_sort_simple in 5.1 (i.e. my_charset_latin1) + and my_hash_sort_bin in 5.5! + */ + case MYSQL_TYPE_ENUM: + case MYSQL_TYPE_SET: + { + if (field->is_null()) + { + nr1^= (nr1 << 1) | 1; + continue; + } + /* Force this to my_hash_sort_bin, which was used in 5.1! */ + uint len= field->pack_length(); + my_charset_latin1.coll->hash_sort(&my_charset_latin1, field->ptr, + len, &nr1, &nr2); + continue; + } + /* New types in mysql-5.6. */ + case MYSQL_TYPE_DATETIME2: + case MYSQL_TYPE_TIME2: + case MYSQL_TYPE_TIMESTAMP2: + /* Not affected, 5.6+ only! */ + break; + + /* These types should not be allowed for partitioning! */ + case MYSQL_TYPE_NULL: + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_GEOMETRY: + /* fall through. */ + default: + DBUG_ASSERT(0); // New type? + /* Fall through for default hashing (5.5). */ + } + /* fall through, use collation based hashing. */ + } + field->hash(&nr1, &nr2); + } while (*(++field_array)); + return (uint32) nr1; +} + + +bool Partition_helper::print_partition_error(int error, myf errflag) +{ + THD *thd= get_thd(); + DBUG_ENTER("Partition_helper::print_partition_error"); + + /* Should probably look for my own errors first */ + DBUG_PRINT("enter", ("error: %d", error)); + + if ((error == HA_ERR_NO_PARTITION_FOUND) && + ! (thd->lex->alter_info.flags & Alter_info::ALTER_TRUNCATE_PARTITION)) + { + m_part_info->print_no_partition_found(m_table, errflag); + // print_no_partition_found() reports an error, so we can just return here. + DBUG_RETURN(false); + } + else if (error == HA_ERR_ROW_IN_WRONG_PARTITION) + { + /* + Should only happen on DELETE or UPDATE! + Or in ALTER TABLE REBUILD/REORGANIZE where there are a misplaced + row that needed to move to an old partition (not in the given set). + */ + DBUG_ASSERT(thd_sql_command(thd) == SQLCOM_DELETE || + thd_sql_command(thd) == SQLCOM_DELETE_MULTI || + thd_sql_command(thd) == SQLCOM_UPDATE || + thd_sql_command(thd) == SQLCOM_UPDATE_MULTI || + thd_sql_command(thd) == SQLCOM_ALTER_TABLE); + DBUG_ASSERT(m_err_rec); + if (m_err_rec) + { + size_t max_length; + char buf[MAX_KEY_LENGTH]; + String str(buf,sizeof(buf),system_charset_info); + uint32 part_id; + DBUG_ASSERT(m_last_part < m_tot_parts); + str.length(0); + if (thd_sql_command(thd) == SQLCOM_ALTER_TABLE) + { + str.append("from REBUILD/REORGANIZED partition: "); + str.append_ulonglong(m_last_part); + str.append(" to non included partition (new definition): "); + } + else + { + str.append_ulonglong(m_last_part); + str.append(". Correct is "); + } + if (get_part_for_delete(m_err_rec, + m_table->record[0], + m_part_info, + &part_id)) + { + str.append("?"); + } + else + { + str.append_ulonglong(part_id); + } + append_row_to_str(str, m_err_rec, m_table); + + /* Log this error, so the DBA can notice it and fix it! */ + sql_print_error("Table '%-192s' corrupted: row in wrong partition: %s\n" + "Please REPAIR the table!", + m_table->s->table_name.str, + str.c_ptr_safe()); + + max_length= (MYSQL_ERRMSG_SIZE - strlen(ER(ER_ROW_IN_WRONG_PARTITION))); + if (str.length() >= max_length) + { + str.length(max_length-4); + str.append(STRING_WITH_LEN("...")); + } + my_error(ER_ROW_IN_WRONG_PARTITION, MYF(0), str.c_ptr_safe()); + m_err_rec= NULL; + DBUG_RETURN(false); + } + } + + DBUG_RETURN(true); +} + + +/** + Implement the partition changes defined by ALTER TABLE of partitions. + + Add and copy if needed a number of partitions, during this operation + only read operation is ongoing in the server. This is used by + ADD PARTITION all types as well as by REORGANIZE PARTITION. For + one-phased implementations it is used also by DROP and COALESCE + PARTITIONs. + One-phased implementation needs the new frm file, other handlers will + get zero length and a NULL reference here. + + @param[in] create_info HA_CREATE_INFO object describing all + fields and indexes in table + @param[in] path Complete path of db and table name + @param[out] copied Output parameter where number of copied + records are added + @param[out] deleted Output parameter where number of deleted + records are added + + @return Operation status + @retval 0 Success + @retval != 0 Failure +*/ + +// FIXME: duplicate of ha_partition::change_partitions +int Partition_helper::change_partitions(HA_CREATE_INFO *create_info, + const char *path, + ulonglong * const copied, + ulonglong * const deleted) +{ + List_iterator part_it(m_part_info->partitions); + List_iterator t_it(m_part_info->temp_partitions); + char part_name_buff[FN_REFLEN]; + const char *table_level_data_file_name= create_info->data_file_name; + const char *table_level_index_file_name= create_info->index_file_name; + const char *table_level_tablespace_name= create_info->tablespace; + uint num_parts= m_part_info->partitions.elements; + uint num_subparts= m_part_info->num_subparts; + uint i= 0; + uint num_remain_partitions; + uint num_reorged_parts; + int error= 1; + bool first; + uint temp_partitions= m_part_info->temp_partitions.elements; + THD *thd= get_thd(); + DBUG_ENTER("Partition_helper::change_partitions"); + + /* + Use the read_partitions bitmap for reorganized partitions, + i.e. what to copy. + */ + bitmap_clear_all(&m_part_info->read_partitions); + + /* + Assert that it works without HA_FILE_BASED and lower_case_table_name = 2. + */ + DBUG_ASSERT(!strcmp(path, get_canonical_filename(m_handler, path, + part_name_buff))); + num_reorged_parts= 0; + if (!m_part_info->is_sub_partitioned()) + num_subparts= 1; + + /* + Step 1: + Calculate number of reorganized partitions. + */ + if (temp_partitions) + { + num_reorged_parts= temp_partitions * num_subparts; + } + else + { + do + { + partition_element *part_elem= part_it++; + if (part_elem->part_state == PART_CHANGED || + part_elem->part_state == PART_REORGED_DROPPED) + { + num_reorged_parts+= num_subparts; + } + } while (++i < num_parts); + } + + /* + Step 2: + Calculate number of partitions after change. + */ + num_remain_partitions= 0; + if (temp_partitions) + { + num_remain_partitions= num_parts * num_subparts; + } + else + { + part_it.rewind(); + i= 0; + do + { + partition_element *part_elem= part_it++; + if (part_elem->part_state == PART_NORMAL || + part_elem->part_state == PART_TO_BE_ADDED || + part_elem->part_state == PART_CHANGED) + { + num_remain_partitions+= num_subparts; + } + } while (++i < num_parts); + } + + /* + Step 3: + Set the read_partition bit for all partitions to be copied. + */ + if (num_reorged_parts) + { + i= 0; + first= true; + part_it.rewind(); + do + { + partition_element *part_elem= part_it++; + if (part_elem->part_state == PART_CHANGED || + part_elem->part_state == PART_REORGED_DROPPED) + { + for (uint sp = 0; sp < num_subparts; sp++) + { + bitmap_set_bit(&m_part_info->read_partitions, i * num_subparts + sp); + } + DBUG_ASSERT(first); + } + else if (first && temp_partitions && + part_elem->part_state == PART_TO_BE_ADDED) + { + /* + When doing an ALTER TABLE REORGANIZE PARTITION a number of + partitions is to be reorganized into a set of new partitions. + The reorganized partitions are in this case in the temp_partitions + list. We mark all of them in one batch and thus we only do this + until we find the first partition with state PART_TO_BE_ADDED + since this is where the new partitions go in and where the old + ones used to be. + */ + first= false; + DBUG_ASSERT(((i*num_subparts) + num_reorged_parts) <= m_tot_parts); + for (uint sp = 0; sp < num_reorged_parts; sp++) + { + bitmap_set_bit(&m_part_info->read_partitions, i * num_subparts + sp); + } + } + } while (++i < num_parts); + } + + /* + Step 4: + Create the new partitions and also open, lock and call + external_lock on them (if needed) to prepare them for copy phase + and also for later close calls. + No need to create PART_NORMAL partitions since they must not + be written to! + Only PART_CHANGED and PART_TO_BE_ADDED should be written to! + */ + + error= prepare_for_new_partitions(num_remain_partitions, + num_reorged_parts == 0); + + i= 0; + part_it.rewind(); + do + { + partition_element *part_elem= part_it++; + DBUG_ASSERT(part_elem->part_state >= PART_NORMAL && + part_elem->part_state <= PART_CHANGED); + if (part_elem->part_state == PART_TO_BE_ADDED || + part_elem->part_state == PART_CHANGED) + { + /* + A new partition needs to be created PART_TO_BE_ADDED means an + entirely new partition and PART_CHANGED means a changed partition + that will still exist with either more or less data in it. + */ + uint name_variant= NORMAL_PART_NAME; + if (part_elem->part_state == PART_CHANGED || + (part_elem->part_state == PART_TO_BE_ADDED && temp_partitions)) + name_variant= TEMP_PART_NAME; + if (m_part_info->is_sub_partitioned()) + { + List_iterator sub_it(part_elem->subpartitions); + uint j= 0, part; + do + { + partition_element *sub_elem= sub_it++; + create_subpartition_name(part_name_buff, path, + part_elem->partition_name, + sub_elem->partition_name, + name_variant); + part= i * num_subparts + j; + DBUG_PRINT("info", ("Add subpartition %s", part_name_buff)); + /* + update_create_info was called previously in + mysql_prepare_alter_table. Which may have set data/index_file_name + for the partitions to the full partition name, including + '#P#[#SP#] suffix. Remove that suffix + if it exists. + */ + truncate_partition_filename(sub_elem->data_file_name); + truncate_partition_filename(sub_elem->index_file_name); + /* Notice that sub_elem is already based on part_elem's defaults. */ + error= set_up_table_before_create(thd, + m_table->s, + part_name_buff, + create_info, + sub_elem); + if (error) + { + goto err; + } + if ((error= create_new_partition(m_table, + create_info, + part_name_buff, + part, + sub_elem))) + { + goto err; + } + /* Reset create_info to table level values. */ + create_info->data_file_name= table_level_data_file_name; + create_info->index_file_name= table_level_index_file_name; + create_info->tablespace= table_level_tablespace_name; + } while (++j < num_subparts); + } + else + { + create_partition_name(part_name_buff, path, + part_elem->partition_name, name_variant, + true); + DBUG_PRINT("info", ("Add partition %s", part_name_buff)); + /* See comment in subpartition branch above! */ + truncate_partition_filename(part_elem->data_file_name); + truncate_partition_filename(part_elem->index_file_name); + error= set_up_table_before_create(thd, + m_table->s, + part_name_buff, + create_info, + part_elem); + if (error) + { + goto err; + } + if ((error= create_new_partition(m_table, + create_info, + (const char *)part_name_buff, + i, + part_elem))) + { + goto err; + } + /* Reset create_info to table level values. */ + create_info->data_file_name= table_level_data_file_name; + create_info->index_file_name= table_level_index_file_name; + create_info->tablespace= table_level_tablespace_name; + } + } + } while (++i < num_parts); + + /* + Step 5: + State update to prepare for next write of the frm file. + */ + i= 0; + part_it.rewind(); + do + { + partition_element *part_elem= part_it++; + if (part_elem->part_state == PART_TO_BE_ADDED) + part_elem->part_state= PART_IS_ADDED; + else if (part_elem->part_state == PART_CHANGED) + part_elem->part_state= PART_IS_CHANGED; + else if (part_elem->part_state == PART_REORGED_DROPPED) + part_elem->part_state= PART_TO_BE_DROPPED; + } while (++i < num_parts); + for (i= 0; i < temp_partitions; i++) + { + partition_element *part_elem= t_it++; + DBUG_ASSERT(part_elem->part_state == PART_TO_BE_REORGED); + part_elem->part_state= PART_TO_BE_DROPPED; + } + error= copy_partitions(copied, deleted); +err: + if (error) + { + m_handler->print_error(error, + MYF(error != ER_OUTOFMEMORY ? 0 : ME_FATALERROR)); + } + /* + Close and unlock the new temporary partitions. + They will later be deleted or renamed through the ddl-log. + */ + close_new_partitions(); + DBUG_RETURN(error); +} + + +/** + Copy partitions as part of ALTER TABLE of partitions. + + change_partitions has done all the preparations, now it is time to + actually copy the data from the reorganized partitions to the new + partitions. + + @param[out] copied Number of records copied. + @param[out] deleted Number of records deleted. + + @return Operation status + @retval 0 Success + @retval >0 Error code +*/ + +int Partition_helper::copy_partitions(ulonglong * const copied, + ulonglong * const deleted) +{ + uint new_part= 0; + int result= 0; + longlong func_value; + DBUG_ENTER("Partition_helper::copy_partitions"); + + if (m_part_info->linear_hash_ind) + { + if (m_part_info->part_type == HASH_PARTITION) + set_linear_hash_mask(m_part_info, m_part_info->num_parts); + else + set_linear_hash_mask(m_part_info, m_part_info->num_subparts); + } + + /* + m_part_info->read_partitions bitmap is setup for all the reorganized + partitions to be copied. So we can use the normal handler rnd interface + for reading. + */ + if ((result= m_handler->ha_rnd_init(1))) + { + DBUG_RETURN(result); + } + while (true) + { + if ((result= m_handler->ha_rnd_next(m_table->record[0]))) + { + if (result == HA_ERR_RECORD_DELETED) + continue; //Probably MyISAM + if (result != HA_ERR_END_OF_FILE) + goto error; + /* + End-of-file reached, break out to end the copy process. + */ + break; + } + /* Found record to insert into new handler */ + if (m_part_info->get_partition_id(m_part_info, &new_part, + &func_value)) + { + /* + This record is in the original table but will not be in the new + table since it doesn't fit into any partition any longer due to + changed partitioning ranges or list values. + */ + (*deleted)++; + } + else + { + if ((result= write_row_in_new_part(new_part))) + { + goto error; + } + } + } + m_handler->ha_rnd_end(); + DBUG_RETURN(false); +error: + m_handler->ha_rnd_end(); + DBUG_RETURN(result); +} + + +/** + Check/fix misplaced rows. + + @param part_id Partition to check/fix. + @param repair If true, move misplaced rows to correct partition. + + @return Operation status. + @retval 0 Success + @retval != 0 Error +*/ + +int Partition_helper::check_misplaced_rows(uint read_part_id, bool repair) +{ + int result= 0; + THD *thd= get_thd(); + bool ignore= thd->lex->ignore; + uint32 correct_part_id; + longlong func_value; + ha_rows num_misplaced_rows= 0; + ha_rows num_deleted_rows= 0; + + DBUG_ENTER("Partition_helper::check_misplaced_rows"); + + if (repair) + { + /* We must read the full row, if we need to move it! */ + bitmap_set_all(m_table->read_set); + bitmap_set_all(m_table->write_set); + } + else + { + /* Only need to read the partitioning fields. */ + bitmap_union(m_table->read_set, &m_part_info->full_part_field_set); +#if 0 + /* Fill the base columns of virtual generated columns if necessary */ + for (Field **ptr= m_part_info->full_part_field_array; *ptr; ptr++) + { + if ((*ptr)->is_virtual_gcol()) + m_table->mark_gcol_in_maps(*ptr); + } +#endif + } + + if ((result= rnd_init_in_part(read_part_id, true))) + DBUG_RETURN(result); + + while (true) + { + if ((result= ph_rnd_next_in_part(read_part_id, m_table->record[0]))) + { + if (result == HA_ERR_RECORD_DELETED) + continue; + if (result != HA_ERR_END_OF_FILE) + break; + + if (num_misplaced_rows > 0) + { + if (repair) + { + if (num_deleted_rows > 0) + { + print_admin_msg(thd, MI_MAX_MSG_BUF, "warning", + m_table->s->db.str, m_table->alias, + opt_op_name[REPAIR_PARTS], + "Moved %lld misplaced rows, deleted %lld rows", + num_misplaced_rows - num_deleted_rows, + num_deleted_rows); + } + else + { + print_admin_msg(thd, MI_MAX_MSG_BUF, "warning", + m_table->s->db.str, m_table->alias, + opt_op_name[REPAIR_PARTS], + "Moved %lld misplaced rows", + num_misplaced_rows); + } + } + else + { + print_admin_msg(thd, MI_MAX_MSG_BUF, "error", + m_table->s->db.str, m_table->alias, + opt_op_name[CHECK_PARTS], + "Found %lld misplaced rows in partition %u", + num_misplaced_rows, + read_part_id); + } + } + /* End-of-file reached, all rows are now OK, reset result and break. */ + result= 0; + break; + } + + result= m_part_info->get_partition_id(m_part_info, &correct_part_id, + &func_value); + // TODO: Add code to delete rows not matching any partition. + if (result) + break; + + if (correct_part_id != read_part_id) + { + num_misplaced_rows++; + m_err_rec= NULL; + if (!repair) + { + /* Check. */ + result= HA_ADMIN_NEEDS_UPGRADE; + char buf[MAX_KEY_LENGTH]; + String str(buf,sizeof(buf),system_charset_info); + str.length(0); + append_row_to_str(str, m_err_rec, m_table); + print_admin_msg(thd, MI_MAX_MSG_BUF, "error", + m_table->s->db.str, m_table->alias, + opt_op_name[CHECK_PARTS], + "Found a misplaced row" + " in part %d should be in part %d:\n%s", + read_part_id, + correct_part_id, + str.c_ptr_safe()); + /* Break on first misplaced row, unless ignore is given! */ + if (!ignore) + break; + } + else + { + DBUG_PRINT("info", ("Moving row from partition %d to %d", + read_part_id, correct_part_id)); + + /* + Insert row into correct partition. Notice that there are no commit + for every N row, so the repair will be one large transaction! + */ + if ((result= write_row_in_part(correct_part_id, m_table->record[0]))) + { + /* + We have failed to insert a row, it might have been a duplicate! + */ + char buf[MAX_KEY_LENGTH]; + String str(buf,sizeof(buf),system_charset_info); + str.length(0); + if (result == HA_ERR_FOUND_DUPP_KEY) + { + if (ignore) + { + str.append("Duplicate key found, deleting the record:\n"); + num_deleted_rows++; + } + else + { + str.append("Duplicate key found, " + "please update or delete the record:\n"); + result= HA_ADMIN_CORRUPT; + } + } + append_row_to_str(str, m_err_rec, m_table); + + /* + If the engine supports transactions, the failure will be + rollbacked. + */ + if (!m_handler->has_transactions() || + ignore || result == HA_ADMIN_CORRUPT) + { + /* Log this error, so the DBA can notice it and fix it! */ + sql_print_error("Table '%-192s' failed to move/insert a row" + " from part %d into part %d:\n%s", + m_table->s->table_name.str, + read_part_id, + correct_part_id, + str.c_ptr_safe()); + } + print_admin_msg(thd, MI_MAX_MSG_BUF, "error", + m_table->s->db.str, m_table->alias, + opt_op_name[REPAIR_PARTS], + "Failed to move/insert a row" + " from part %d into part %d:\n%s", + read_part_id, + correct_part_id, + str.c_ptr_safe()); + if (!ignore || result != HA_ERR_FOUND_DUPP_KEY) + break; + } + + /* Delete row from wrong partition. */ + if ((result= delete_row_in_part(read_part_id, m_table->record[0]))) + { + result= HA_ADMIN_CORRUPT; + if (m_handler->has_transactions()) + break; + /* + We have introduced a duplicate, since we failed to remove it + from the wrong partition. + */ + char buf[MAX_KEY_LENGTH]; + String str(buf,sizeof(buf),system_charset_info); + str.length(0); + append_row_to_str(str, m_err_rec, m_table); + + /* Log this error, so the DBA can notice it and fix it! */ + sql_print_error("Table '%-192s': Delete from part %d failed with" + " error %d. But it was already inserted into" + " part %d, when moving the misplaced row!" + "\nPlease manually fix the duplicate row:\n%s", + m_table->s->table_name.str, + read_part_id, + result, + correct_part_id, + str.c_ptr_safe()); + break; + } + } + } + } + + int tmp_result= rnd_end_in_part(read_part_id, true); + DBUG_RETURN(result ? result : tmp_result); +} + +/** + Read next row during full partition scan (scan in random row order). + + This function can evaluate the virtual generated columns. If virtual + generated columns are involved, you should not call rnd_next_in_part + directly but this one. + + @param part_id Partition to read from. + @param[in,out] buf buffer that should be filled with data. + + @return Operation status. + @retval 0 Success + @retval != 0 Error code +*/ + +int Partition_helper::ph_rnd_next_in_part(uint part_id, uchar *buf) +{ + int result= rnd_next_in_part(part_id, buf); + +#if 0 + if (!result && m_table->has_gcol()) + result= update_generated_read_fields(buf, m_table); +#endif + + return result; +} + + +/** Set used partitions bitmap from Alter_info. + + @return false if success else true. +*/ + +bool Partition_helper::set_altered_partitions() +{ + Alter_info *alter_info= &get_thd()->lex->alter_info; + + if ((alter_info->flags & Alter_info::ALTER_ADMIN_PARTITION) == 0 || + (alter_info->flags & Alter_info::ALTER_ALL_PARTITION)) + { + /* + Full table command, not ALTER TABLE t PARTITION . + All partitions are already set, so do nothing. + */ + return false; + } + return m_part_info->set_read_partitions(&alter_info->partition_names); +} + +#if 0 +/** + Print a message row formatted for ANALYZE/CHECK/OPTIMIZE/REPAIR TABLE. + + Modeled after mi_check_print_msg. + + @param thd Thread context. + @param len Needed length for message buffer. + @param msg_type Message type. + @param db_name Database name. + @param table_name Table name. + @param op_name Operation name. + @param fmt Message (in printf format with additional arguments). + + @return Operation status. + @retval false for success else true. +*/ + +bool Partition_helper::print_admin_msg(THD* thd, + uint len, + const char *msg_type, + const char *db_name, + const char *table_name, + const char *op_name, + const char *fmt, + ...) +{ + va_list args; + Protocol *protocol= thd->protocol; + uint length; + size_t msg_length; + char name[NAME_LEN*2+2]; + char *msgbuf; + bool error= true; + + if (!(msgbuf= (char*) my_malloc(len, MYF(0)))) + return true; + va_start(args, fmt); + msg_length= my_vsnprintf(msgbuf, len, fmt, args); + va_end(args); + if (msg_length >= (len - 1)) + goto err; + msgbuf[len - 1] = 0; // healthy paranoia + + if (!thd->protocol->connection_alive()) + { + sql_print_error("%s", msgbuf); + goto err; + } + + length=(uint) (strxmov(name, db_name, ".", table_name,NullS) - name); + /* + TODO: switch from protocol to push_warning here. The main reason we didn't + it yet is parallel repair. Due to following trace: + mi_check_print_msg/push_warning/sql_alloc/my_pthread_getspecific_ptr. + + Also we likely need to lock mutex here (in both cases with protocol and + push_warning). + */ + DBUG_PRINT("info",("print_admin_msg: %s, %s, %s, %s", name, op_name, + msg_type, msgbuf)); + protocol->start_row(); + protocol->store(name, length, system_charset_info); + protocol->store(op_name, system_charset_info); + protocol->store(msg_type, system_charset_info); + protocol->store(msgbuf, msg_length, system_charset_info); + if (protocol->end_row()) + { + sql_print_error("Failed on my_net_write, writing to stderr instead: %s\n", + msgbuf); + goto err; + } + error= false; +err: + my_free(msgbuf); + return error; +} +#endif + + +/** + Set table->read_set taking partitioning expressions into account. + + @param[in] rnd_init True if called from rnd_init (else index_init). +*/ + +inline +void Partition_helper::set_partition_read_set() +{ + /* + For operations that may need to change data, we may need to extend + read_set. + */ + if (m_handler->get_lock_type() == F_WRLCK) + { + /* + If write_set contains any of the fields used in partition and + subpartition expression, we need to set all bits in read_set because + the row may need to be inserted in a different [sub]partition. In + other words update_row() can be converted into write_row(), which + requires a complete record. + */ + if (bitmap_is_overlapping(&m_part_info->full_part_field_set, + m_table->write_set)) + { + bitmap_set_all(m_table->read_set); + } + else + { + /* + Some handlers only read fields as specified by the bitmap for the + read set. For partitioned handlers we always require that the + fields of the partition functions are read such that we can + calculate the partition id to place updated and deleted records. + */ + bitmap_union(m_table->read_set, &m_part_info->full_part_field_set); + } + // Mark virtual generated columns writable + for (Field **vf= m_table->vfield; vf && *vf; vf++) + { + if (bitmap_is_set(m_table->read_set, (*vf)->field_index)) + bitmap_set_bit(m_table->write_set, (*vf)->field_index); + } + } +} + + +/**************************************************************************** + MODULE full table scan +****************************************************************************/ + +/** + Initialize engine for random reads. + + rnd_init() is called when the server wants the storage engine to do a + table scan or when the server wants to access data through rnd_pos. + + When scan is used we will scan one handler partition at a time. + When preparing for rnd_pos we will initialize all handler partitions. + No extra cache handling is needed when scanning is not performed. + + Before initializing we will call rnd_end to ensure that we clean up from + any previous incarnation of a table scan. + + @param scan false for initialize for random reads through rnd_pos() + true for initialize for random scan through rnd_next(). + + @return Operation status. + @retval 0 Success + @retval != 0 Error code +*/ + +int Partition_helper::ph_rnd_init(bool scan) +{ + int error; + uint i= 0; + uint part_id; + DBUG_ENTER("Partition_helper::ph_rnd_init"); + + set_partition_read_set(); + + /* Now we see what the index of our first important partition is */ + DBUG_PRINT("info", ("m_part_info->read_partitions: 0x%lx", + (long) m_part_info->read_partitions.bitmap)); + part_id= m_part_info->get_first_used_partition(); + DBUG_PRINT("info", ("m_part_spec.start_part %d", part_id)); + + if (MY_BIT_NONE == part_id) + { + error= 0; + goto err1; + } + + DBUG_PRINT("info", ("rnd_init on partition %d", part_id)); + if (scan) + { + /* A scan can be restarted without rnd_end() in between! */ + if (m_scan_value == 1 && m_part_spec.start_part != NOT_A_PARTITION_ID) + { + /* End previous scan on partition before restart. */ + if ((error= rnd_end_in_part(m_part_spec.start_part, scan))) + { + DBUG_RETURN(error); + } + } + m_scan_value= 1; + if ((error= rnd_init_in_part(part_id, scan))) + goto err; + } + else + { + m_scan_value= 0; + for (i= part_id; + i < MY_BIT_NONE; + i= m_part_info->get_next_used_partition(i)) + { + if ((error= rnd_init_in_part(i, scan))) + goto err; + } + } + m_part_spec.start_part= part_id; + m_part_spec.end_part= m_tot_parts - 1; + DBUG_PRINT("info", ("m_scan_value=%d", m_scan_value)); + DBUG_RETURN(0); + +err: + /* Call rnd_end for all previously initialized partitions. */ + for (; + part_id < i; + part_id= m_part_info->get_next_used_partition(part_id)) + { + rnd_end_in_part(part_id, scan); + } +err1: + m_scan_value= 2; + m_part_spec.start_part= NO_CURRENT_PART_ID; + DBUG_RETURN(error); +} + + +/** + End of a table scan. + + @return Operation status. + @retval 0 Success + @retval != 0 Error code +*/ + +int Partition_helper::ph_rnd_end() +{ + int error= 0; + DBUG_ENTER("Partition_helper::ph_rnd_end"); + switch (m_scan_value) { + case 3: // Error + DBUG_ASSERT(0); + /* fall through. */ + case 2: // Error + break; + case 1: + if (NO_CURRENT_PART_ID != m_part_spec.start_part) // Table scan + { + error= rnd_end_in_part(m_part_spec.start_part, true); + } + break; + case 0: + uint i; + for (i= m_part_info->get_first_used_partition(); + i < MY_BIT_NONE; + i= m_part_info->get_next_used_partition(i)) + { + int part_error; + part_error= rnd_end_in_part(i, false); + if (part_error && !error) { + error= part_error; + } + } + break; + } + m_scan_value= 3; + m_part_spec.start_part= NO_CURRENT_PART_ID; + DBUG_RETURN(error); +} + + +/** + Read next row during full table scan (scan in random row order). + + This is called for each row of the table scan. When you run out of records + you should return HA_ERR_END_OF_FILE. + The Field structure for the table is the key to getting data into buf + in a manner that will allow the server to understand it. + + @param[out] buf buffer that should be filled with data. + + @return Operation status. + @retval 0 Success + @retval != 0 Error code +*/ + +int Partition_helper::ph_rnd_next(uchar *buf) +{ + int result= HA_ERR_END_OF_FILE; + uint part_id= m_part_spec.start_part; + DBUG_ENTER("Partition_helper::ph_rnd_next"); + + if (NO_CURRENT_PART_ID == part_id) + { + /* + The original set of partitions to scan was empty and thus we report + the result here. + */ + goto end; + } + + DBUG_ASSERT(m_scan_value == 1); + + while (TRUE) + { + result= rnd_next_in_part(part_id, buf); + if (!result) + { + m_last_part= part_id; + m_part_spec.start_part= part_id; + m_table->status= 0; + DBUG_RETURN(0); + } + + /* + if we get here, then the current partition ha_rnd_next returned failure + */ + if (result == HA_ERR_RECORD_DELETED) + continue; // Probably MyISAM + + if (result != HA_ERR_END_OF_FILE) + goto end_dont_reset_start_part; // Return error + + /* End current partition */ + DBUG_PRINT("info", ("rnd_end on partition %d", part_id)); + if ((result= rnd_end_in_part(part_id, true))) + break; + + /* Shift to next partition */ + part_id= m_part_info->get_next_used_partition(part_id); + if (part_id >= m_tot_parts) + { + result= HA_ERR_END_OF_FILE; + break; + } + m_last_part= part_id; + m_part_spec.start_part= part_id; + DBUG_PRINT("info", ("rnd_init on partition %d", part_id)); + if ((result= rnd_init_in_part(part_id, true))) + break; + } + +end: + m_part_spec.start_part= NO_CURRENT_PART_ID; +end_dont_reset_start_part: + m_table->status= STATUS_NOT_FOUND; + DBUG_RETURN(result); +} + + +/** + Save position of current row. + + position() is called after each call to rnd_next() if the data needs + to be ordered or accessed later. + + The server uses ref to store data. ref_length in the above case is + the size needed to store current_position. ref is just a byte array + that the server will maintain. If you are using offsets to mark rows, then + current_position should be the offset. If it is a primary key like in + InnoDB, then it needs to be a primary key. + + @param record Current record in MySQL Row Format. +*/ + +void Partition_helper::ph_position(const uchar *record) +{ + DBUG_ASSERT(m_part_info->is_partition_used(m_last_part)); + DBUG_ENTER("Partition_helper::ph_position"); + DBUG_PRINT("info", ("record: %p", record)); + DBUG_DUMP("record", record, m_rec_length); + + /* + If m_ref_usage is set, then the ref is already stored in the + priority queue (m_queue) when doing ordered scans. + */ + if (m_ref_usage != REF_NOT_USED && m_ordered_scan_ongoing) + { + DBUG_ASSERT(!m_queue->empty()); + DBUG_ASSERT(m_ordered_rec_buffer); + DBUG_ASSERT(!m_curr_key_info[1]); + DBUG_ASSERT(uint2korr(m_queue->top()) == m_last_part); + /* We already have the ref and part id. */ + memcpy(m_handler->ref, m_queue->top(), m_handler->ref_length); + } + else + { + DBUG_PRINT("info", ("m_last_part: %u", m_last_part)); + int2store(m_handler->ref, m_last_part); + position_in_last_part(m_handler->ref + PARTITION_BYTES_IN_POS, record); + } + DBUG_DUMP("ref_out", m_handler->ref, m_handler->ref_length); + + DBUG_VOID_RETURN; +} + + +/** + Read row using position. + + This is like rnd_next, but you are given a position to use to determine + the row. The position will be pointing to data of length handler::ref_length + that handler::ref was set by position(record). Tables clustered on primary + key usually use the full primary key as reference (like InnoDB). Heap based + tables usually returns offset in heap file (like MyISAM). + + @param[out] buf buffer that should be filled with record in MySQL format. + @param[in] pos position given as handler::ref when position() was called. + + @return Operation status. + @retval 0 Success + @retval != 0 Error code +*/ + +int Partition_helper::ph_rnd_pos(uchar *buf, uchar *pos) +{ + uint part_id; + DBUG_ENTER("Partition_helper::ph_rnd_pos"); + + part_id= uint2korr(pos); + DBUG_ASSERT(part_id < m_tot_parts); + DBUG_ASSERT(m_part_info->is_partition_used(part_id)); + m_last_part= part_id; + DBUG_RETURN(rnd_pos_in_part(part_id, buf, (pos + PARTITION_BYTES_IN_POS))); +} + + +/** + Read row using position using given record to find. + + This works as position()+rnd_pos() functions, but does some extra work, + calculating m_last_part - the partition to where the 'record' should go. + + Only useful when position is based on primary key + (HA_PRIMARY_KEY_REQUIRED_FOR_POSITION). + + @param record Current record in MySQL Row Format. + + @return Operation status. + @retval 0 Success + @retval != 0 Error code +*/ + +int Partition_helper::ph_rnd_pos_by_record(uchar *record) +{ + DBUG_ENTER("Partition_helper::ph_rnd_pos_by_record"); + + DBUG_ASSERT(m_handler->ha_table_flags() & + HA_PRIMARY_KEY_REQUIRED_FOR_POSITION); + /* TODO: Support HA_READ_BEFORE_WRITE_REMOVAL */ + /* Set m_last_part correctly. */ + if (unlikely(get_part_for_delete(record, + m_table->record[0], + m_part_info, + &m_last_part))) + DBUG_RETURN(HA_ERR_INTERNAL_ERROR); + + DBUG_RETURN(rnd_pos_by_record_in_last_part(record)); +} + + +/**************************************************************************** + MODULE index scan +****************************************************************************/ +/* + Positions an index cursor to the index specified in the handle. Fetches the + row if available. If the key value is null, begin at the first key of the + index. + + There are loads of optimizations possible here for the partition handler. + The same optimizations can also be checked for full table scan although + only through conditions and not from index ranges. + Phase one optimizations: + Check if the fields of the partition function are bound. If so only use + the single partition it becomes bound to. + Phase two optimizations: + If it can be deducted through range or list partitioning that only a + subset of the partitions are used, then only use those partitions. +*/ + +/** + Setup the ordered record buffer and the priority queue. + + Call destroy_record_priority_queue() to deallocate or clean-up + from failure. + + @return false on success, else true. +*/ + +int Partition_helper::init_record_priority_queue() +{ + uint used_parts= m_part_info->num_partitions_used(); + DBUG_ENTER("Partition_helper::init_record_priority_queue"); + DBUG_ASSERT(!m_ordered_rec_buffer); + DBUG_ASSERT(!m_queue); + /* Initialize the priority queue. */ + // TODO: Create test to see the cost of allocating when needed vs + // allocate once and keep between statements. Also test on NUMA + // machines to see the difference (I guess that allocating when needed + // will allocate on 'correct' NUMA node and be faster.) + if (!m_queue) + { + m_queue= new (std::nothrow) Prio_queue(Key_rec_less(m_curr_key_info)); + if (!m_queue) + { + DBUG_RETURN(HA_ERR_OUT_OF_MEM); + } + } + /* Initialize the ordered record buffer. */ + if (!m_ordered_rec_buffer) + { + uint alloc_len; + /* + Allocate record buffer for each used partition. + If PK is clustered index, it is either the primary sort key or is + added as secondary sort. So we only need to allocate for part id + and a full record per partition. + Otherwise if the clustered index was generated, we might need to + do a secondary sort by rowid (handler::ref) and must allocate for + ref (includes part id) and full record per partition. We don't + know yet if we need to do secondary sort by rowid, so we must + allocate space for it. + TODO: enhance ha_index_init() for HA_EXTRA_SECONDARY_SORT_ROWID to + avoid allocating space for handler::ref when not needed. + When enhancing ha_index_init() care must be taken on ph_position(), + so InnoDB's row_id is correctly handled (taken from m_last_part). + */ + if (m_pkey_is_clustered && m_table->s->primary_key != MAX_KEY) + { + m_rec_offset= PARTITION_BYTES_IN_POS; + m_ref_usage= REF_NOT_USED; + } + else + { + m_rec_offset= m_handler->ref_length; + m_ref_usage= REF_STORED_IN_PQ; + } + alloc_len= used_parts * (m_rec_offset + m_rec_length); + /* Allocate a key for temporary use when setting up the scan. */ + alloc_len+= m_table->s->max_key_length; + + m_ordered_rec_buffer= static_cast( + my_malloc(alloc_len, + MYF(MY_WME))); + if (!m_ordered_rec_buffer) + { + DBUG_RETURN(HA_ERR_OUT_OF_MEM); + } + + /* + We set-up one record per partition and each record has 2 bytes in + front where the partition id is written. This is used by ordered + index_read. + If we need to also sort by rowid (handler::ref), then m_curr_key_info[1] + is NULL and we add the rowid before the record. + We also set-up a reference to the first record for temporary use in + setting up the scan. + */ + char *ptr= (char*) m_ordered_rec_buffer; + uint i; + for (i= m_part_info->get_first_used_partition(); + i < MY_BIT_NONE; + i= m_part_info->get_next_used_partition(i)) + { + DBUG_PRINT("info", ("init rec-buf for part %u", i)); + int2store(ptr, i); + ptr+= m_rec_offset + m_rec_length; + } + m_start_key.key= (const uchar*)ptr; + /* + Initialize priority queue, initialized to reading forward. + Start by only sort by KEY, HA_EXTRA_SECONDARY_SORT_ROWID + will be given if we should sort by handler::ref too. + */ + m_queue->m_rec_offset= m_rec_offset; + if (m_queue->reserve(used_parts)) + { + DBUG_RETURN(HA_ERR_OUT_OF_MEM); + } + } + DBUG_RETURN(init_record_priority_queue_for_parts(used_parts)); +} + + +/** + Destroy the ordered record buffer and the priority queue. +*/ + +void Partition_helper::destroy_record_priority_queue() +{ + DBUG_ENTER("Partition_helper::destroy_record_priority_queue"); + if (m_ordered_rec_buffer) + { + my_free(m_ordered_rec_buffer); + m_ordered_rec_buffer= NULL; + } + if (m_queue) + { + m_queue->clear(); + delete m_queue; + m_queue= NULL; + } + m_ref_usage= REF_NOT_USED; + m_ordered_scan_ongoing= false; + DBUG_VOID_RETURN; +} + + +/** + Common setup for index_init. + + Set up variables and initialize the record priority queue. + + @param inx Index to be used. + @param sorted True if the rows must be returned in index order. + + @return Operation status. + @retval 0 Success + @retval != 0 Error code +*/ + +int Partition_helper::ph_index_init_setup(uint inx, bool sorted) +{ + DBUG_ENTER("Partition_helper:ph_:index_init_setup"); + + DBUG_ASSERT(inx != MAX_KEY); + DBUG_PRINT("info", ("inx %u sorted %u", inx, sorted)); + m_part_spec.start_part= NO_CURRENT_PART_ID; + m_start_key.length= 0; + m_ordered= sorted; + m_ref_usage= REF_NOT_USED; + m_curr_key_info[0]= m_table->key_info+inx; + m_curr_key_info[1]= NULL; + /* + There are two cases where it is not enough to only sort on the key: + 1) For clustered indexes, the optimizer assumes that all keys + have the rest of the PK columns appended to the KEY, so it will + sort by PK as secondary sort key. + 2) Rowid-Order-Retrieval access methods, like index_merge_intersect + and index_merge_union. These methods requires the index to be sorted + on rowid (handler::ref) as secondary sort key. + */ + if (m_pkey_is_clustered && m_table->s->primary_key != MAX_KEY && + inx != m_table->s->primary_key) + { + /* + if PK is clustered, then the key cmp must use the pk to + differentiate between equal key in given index. + */ + DBUG_PRINT("info", ("Clustered pk, using pk as secondary cmp")); + m_curr_key_info[1]= m_table->key_info+m_table->s->primary_key; + } + + /* + Some handlers only read fields as specified by the bitmap for the + read set. For partitioned handlers we always require that the + fields of the partition functions are read such that we can + calculate the partition id to place updated and deleted records. + */ + if (m_handler->get_lock_type() == F_WRLCK) + bitmap_union(m_table->read_set, &m_part_info->full_part_field_set); + + DBUG_RETURN(0); +} + + +/** + Initialize handler before start of index scan. + + index_init is always called before starting index scans (except when + starting through index_read_idx and using read_range variants). + + @param inx Index number. + @param sorted Is rows to be returned in sorted order. + + @return Operation status + @retval 0 Success + @retval != 0 Error code +*/ + +int Partition_helper::ph_index_init(uint inx, bool sorted) +{ + int error; + uint part_id= m_part_info->get_first_used_partition(); + DBUG_ENTER("Partition_helper::ph_index_init"); + m_handler->active_index= inx; + + if (part_id == MY_BIT_NONE) + { + DBUG_RETURN(0); + } + + if ((error= ph_index_init_setup(inx, sorted))) + { + DBUG_RETURN(error); + } + if ((error= init_record_priority_queue())) + { + destroy_record_priority_queue(); + DBUG_RETURN(error); + } + + for (/* part_id already set. */; + part_id < MY_BIT_NONE; + part_id= m_part_info->get_next_used_partition(part_id)) + { + if ((error= index_init_in_part(part_id, inx, sorted))) + goto err; + + DBUG_EXECUTE_IF("partition_fail_index_init", { + part_id++; + error= HA_ERR_NO_PARTITION_FOUND; + goto err; + }); + } +err: + if (error) + { + /* End the previously initialized indexes. */ + uint j; + for (j= m_part_info->get_first_used_partition(); + j < part_id; + j= m_part_info->get_next_used_partition(j)) + { + (void) index_end_in_part(j); + } + destroy_record_priority_queue(); + } + DBUG_RETURN(error); +} + + +/** + End of index scan. + + index_end is called at the end of an index scan to clean up any + things needed to clean up. + + @return Operation status. + @retval 0 Success + @retval != 0 Error code +*/ + +int Partition_helper::ph_index_end() +{ + int error= 0; + uint i; + DBUG_ENTER("Partition_helper::ph_index_end"); + + m_part_spec.start_part= NO_CURRENT_PART_ID; + m_ref_usage= REF_NOT_USED; + for (i= m_part_info->get_first_used_partition(); + i < MY_BIT_NONE; + i= m_part_info->get_next_used_partition(i)) + { + int tmp; + if ((tmp= index_end_in_part(i))) + error= tmp; + } + destroy_record_priority_queue(); + m_handler->active_index= MAX_KEY; + DBUG_RETURN(error); +} + + +/** + Read one record in an index scan and start an index scan. + + index_read_map starts a new index scan using a start key. The MySQL Server + will check the end key on its own. Thus to function properly the + partitioned handler need to ensure that it delivers records in the sort + order of the MySQL Server. + index_read_map can be restarted without calling index_end on the previous + index scan and without calling index_init. In this case the index_read_map + is on the same index as the previous index_scan. This is particularly + used in conjunction with multi read ranges. + + @param[out] buf Read row in MySQL Row Format + @param[in] key Key parts in consecutive order + @param[in] keypart_map Which part of key is used + @param[in] find_flag What type of key condition is used + + @return Operation status. + @retval 0 Success + @retval != 0 Error code +*/ + +int Partition_helper::ph_index_read_map(uchar *buf, + const uchar *key, + key_part_map keypart_map, + enum ha_rkey_function find_flag) +{ + DBUG_ENTER("Partition_handler::ph_index_read_map"); + m_handler->end_range= NULL; + m_index_scan_type= PARTITION_INDEX_READ; + m_start_key.key= key; + m_start_key.keypart_map= keypart_map; + m_start_key.flag= find_flag; + DBUG_RETURN(common_index_read(buf, true)); +} + + +/** + Common routine for a number of index_read variants. + + @param[out] buf Buffer where the record should be returned. + @param[in] have_start_key TRUE <=> the left endpoint is available, i.e. + we're in index_read call or in read_range_first + call and the range has left endpoint. + FALSE <=> there is no left endpoint (we're in + read_range_first() call and the range has no left + endpoint). + + @return Operation status + @retval 0 OK + @retval HA_ERR_END_OF_FILE Whole index scanned, without finding the record. + @retval HA_ERR_KEY_NOT_FOUND Record not found, but index cursor positioned. + @retval other Error code. + + @details + Start scanning the range (when invoked from read_range_first()) or doing + an index lookup (when invoked from index_read_XXX): + - If possible, perform partition selection + - Find the set of partitions we're going to use + - Depending on whether we need ordering: + NO: Get the first record from first used partition (see + handle_unordered_scan_next_partition) + YES: Fill the priority queue and get the record that is the first in + the ordering +*/ + +int Partition_helper::common_index_read(uchar *buf, bool have_start_key) +{ + int error; + m_reverse_order= false; + DBUG_ENTER("Partition_helper::common_index_read"); + + DBUG_PRINT("info", ("m_ordered %u m_ordered_scan_ong %u", + m_ordered, m_ordered_scan_ongoing)); + + if (have_start_key) + { + m_start_key.length= calculate_key_len(m_table, + m_handler->active_index, + NULL, + m_start_key.keypart_map); + DBUG_PRINT("info", ("have_start_key map %lu find_flag %u len %u", + m_start_key.keypart_map, m_start_key.flag, + m_start_key.length)); + DBUG_ASSERT(m_start_key.length); + } + if ((error= partition_scan_set_up(buf, have_start_key))) + { + DBUG_RETURN(error); + } + + if (have_start_key && + (m_start_key.flag == HA_READ_KEY_OR_PREV || + m_start_key.flag == HA_READ_PREFIX_LAST || + m_start_key.flag == HA_READ_PREFIX_LAST_OR_PREV || + m_start_key.flag == HA_READ_BEFORE_KEY)) + { + m_reverse_order= true; + m_ordered_scan_ongoing= true; + } + DBUG_PRINT("info", ("m_ordered %u m_o_scan_ong %u have_start_key %u", + m_ordered, m_ordered_scan_ongoing, have_start_key)); + if (!m_ordered_scan_ongoing) + { + /* + We use unordered index scan when read_range is used and flag + is set to not use ordered. + We also use an unordered index scan when the number of partitions to + scan is only one. + The unordered index scan will use the partition set created. + */ + DBUG_PRINT("info", ("doing unordered scan")); + error= handle_unordered_scan_next_partition(buf); + } + else + { + /* + In all other cases we will use the ordered index scan. This will use + the partition set created by the get_partition_set method. + */ + error= handle_ordered_index_scan(buf); + } + DBUG_RETURN(error); +} + + +/** + Start an index scan from leftmost record and return first record. + + index_first() asks for the first key in the index. + This is similar to index_read except that there is no start key since + the scan starts from the leftmost entry and proceeds forward with + index_next. + + @param[out] buf Read row in MySQL Row Format. + + @return Operation status. + @retval 0 Success + @retval != 0 Error code +*/ + +int Partition_helper::ph_index_first(uchar *buf) +{ + DBUG_ENTER("Partition_helper::ph_index_first"); + + m_handler->end_range= NULL; + m_index_scan_type= PARTITION_INDEX_FIRST; + m_reverse_order= false; + DBUG_RETURN(common_first_last(buf)); +} + + +/** + Start an index scan from rightmost record and return first record. + + index_last() asks for the last key in the index. + This is similar to index_read except that there is no start key since + the scan starts from the rightmost entry and proceeds forward with + index_prev. + + @param[out] buf Read row in MySQL Row Format. + + @return Operation status. + @retval 0 Success + @retval != 0 Error code +*/ + +int Partition_helper::ph_index_last(uchar *buf) +{ + DBUG_ENTER("Partition_helper::ph_index_last"); + + m_index_scan_type= PARTITION_INDEX_LAST; + m_reverse_order= true; + DBUG_RETURN(common_first_last(buf)); +} + + +/** + Common routine for index_first/index_last. + + @param[out] buf Read row in MySQL Row Format. + + @return Operation status. + @retval 0 Success + @retval != 0 Error code +*/ + +int Partition_helper::common_first_last(uchar *buf) +{ + int error; + DBUG_ENTER("Partition_helper::common_first_last"); + + if ((error= partition_scan_set_up(buf, false))) + { + DBUG_RETURN(error); + } + if (!m_ordered_scan_ongoing && + m_index_scan_type != PARTITION_INDEX_LAST) + { + DBUG_RETURN(handle_unordered_scan_next_partition(buf)); + } + DBUG_RETURN(handle_ordered_index_scan(buf)); +} + + +/** + Read last using key. + + This is used in join_read_last_key to optimize away an ORDER BY. + Can only be used on indexes supporting HA_READ_ORDER. + + @param[out] buf Read row in MySQL Row Format + @param[in] key Key + @param[in] keypart_map Which part of key is used + + @return Operation status. + @retval 0 Success + @retval != 0 Error code +*/ + +int Partition_helper::ph_index_read_last_map(uchar *buf, + const uchar *key, + key_part_map keypart_map) +{ + DBUG_ENTER("Partition_helper::ph_index_read_last_map"); + + m_ordered= true; // Safety measure + m_handler->end_range= NULL; + m_index_scan_type= PARTITION_INDEX_READ_LAST; + m_start_key.key= key; + m_start_key.keypart_map= keypart_map; + m_start_key.flag= HA_READ_PREFIX_LAST; + DBUG_RETURN(common_index_read(buf, true)); +} + + +/** + Read index by key and keymap. + + Positions an index cursor to the index specified. + Fetches the row if available. If the key value is null, + begin at first key of the index. + + Optimization of the default implementation to take advantage of dynamic + partition pruning. + + @param[out] buf Read row in MySQL Row Format + @param[in] index Index to read from + @param[in] key Key + @param[in] keypart_map Which part of key is used + @param[in] find_flag Direction/how to search. + + @return Operation status. + @retval 0 Success + @retval != 0 Error code +*/ +int Partition_helper::ph_index_read_idx_map(uchar *buf, + uint index, + const uchar *key, + key_part_map keypart_map, + enum ha_rkey_function find_flag) +{ + int error= HA_ERR_KEY_NOT_FOUND; + DBUG_ENTER("Partition_helper::ph_index_read_idx_map"); + + if (find_flag == HA_READ_KEY_EXACT) + { + uint part; + m_start_key.key= key; + m_start_key.keypart_map= keypart_map; + m_start_key.flag= find_flag; + m_start_key.length= calculate_key_len(m_table, + index, + NULL, + m_start_key.keypart_map); + + get_partition_set(m_table, buf, index, &m_start_key, &m_part_spec); + + /* + We have either found exactly 1 partition + (in which case start_part == end_part) + or no matching partitions (start_part > end_part) + */ + DBUG_ASSERT(m_part_spec.start_part >= m_part_spec.end_part); + /* The start part is must be marked as used. */ + DBUG_ASSERT(m_part_spec.start_part > m_part_spec.end_part || + m_part_info->is_partition_used(m_part_spec.start_part)); + + for (part= m_part_spec.start_part; + part <= m_part_spec.end_part; + part= m_part_info->get_next_used_partition(part)) + { + error= index_read_idx_map_in_part(part, + buf, + index, + key, + keypart_map, + find_flag); + if (error != HA_ERR_KEY_NOT_FOUND && + error != HA_ERR_END_OF_FILE) + { + break; + } + } + if (part <= m_part_spec.end_part) + { + m_last_part= part; + } + } + else + { + /* + If not only used with HA_READ_KEY_EXACT, we should investigate if + possible to optimize for other find_flag's as well. + */ + DBUG_ASSERT(0); + error= HA_ERR_INTERNAL_ERROR; + } + DBUG_RETURN(error); +} + + +/** + Read next record in a forward index scan. + + Used to read forward through the index (left to right, low to high). + + @param[out] buf Read row in MySQL Row Format. + + @return Operation status. + @retval 0 Success + @retval != 0 Error code +*/ + +int Partition_helper::ph_index_next(uchar *buf) +{ + DBUG_ENTER("Partition_helper::ph_index_next"); + + /* + TODO(low priority): + If we want partition to work with the HANDLER commands, we + must be able to do index_last() -> index_prev() -> index_next() + and if direction changes, we must step back those partitions in + the record queue so we don't return a value from the wrong direction. + */ + DBUG_ASSERT(m_index_scan_type != PARTITION_INDEX_LAST || + m_table->open_by_handler); + if (!m_ordered_scan_ongoing) + { + DBUG_RETURN(handle_unordered_next(buf, false)); + } + DBUG_RETURN(handle_ordered_next(buf, false)); +} + + +/** + Read next same record. + + This routine is used to read the next but only if the key is the same + as supplied in the call. + + @param[out] buf Read row in MySQL Row Format. + @param[in] key Key. + @param[in] keylen Length of key. + + @return Operation status. + @retval 0 Success + @retval != 0 Error code +*/ + +int Partition_helper::ph_index_next_same(uchar *buf, const uchar *key, uint keylen) +{ + DBUG_ENTER("Partition_helper::ph_index_next_same"); + + DBUG_ASSERT(keylen == m_start_key.length); + DBUG_ASSERT(m_index_scan_type != PARTITION_INDEX_LAST); + if (!m_ordered_scan_ongoing) + DBUG_RETURN(handle_unordered_next(buf, true)); + DBUG_RETURN(handle_ordered_next(buf, true)); +} + + +/** + Read next record when performing index scan backwards. + + Used to read backwards through the index (right to left, high to low). + + @param[out] buf Read row in MySQL Row Format. + + @return Operation status. + @retval 0 Success + @retval != 0 Error code +*/ + +int Partition_helper::ph_index_prev(uchar *buf) +{ + DBUG_ENTER("Partition_helper::ph_index_prev"); + + /* TODO: read comment in index_next */ + DBUG_ASSERT(m_index_scan_type != PARTITION_INDEX_FIRST || + m_table->open_by_handler); + DBUG_RETURN(handle_ordered_prev(buf)); +} + + +/** + Start a read of one range with start and end key. + + We re-implement read_range_first since we don't want the compare_key + check at the end. This is already performed in the partition handler. + read_range_next is very much different due to that we need to scan + all underlying handlers. + + @param start_key Specification of start key. + @param end_key Specification of end key. + @param eq_range_arg Is it equal range. + @param sorted Should records be returned in sorted order. + + @return Operation status. + @retval 0 Success + @retval != 0 Error code +*/ + +int Partition_helper::ph_read_range_first(const key_range *start_key, + const key_range *end_key, + bool eq_range_arg, + bool sorted) +{ + int error= HA_ERR_END_OF_FILE; + bool have_start_key= (start_key != NULL); + uint part_id= m_part_info->get_first_used_partition(); + DBUG_ENTER("Partition_helper::ph_read_range_first"); + + if (part_id == MY_BIT_NONE) + { + /* No partition to scan. */ + m_table->status= STATUS_NOT_FOUND; + DBUG_RETURN(error); + } + + m_ordered= sorted; + set_eq_range(eq_range_arg); + m_handler->set_end_range(end_key); + + set_range_key_part(m_curr_key_info[0]->key_part); + if (have_start_key) + m_start_key= *start_key; + else + m_start_key.key= NULL; + + m_index_scan_type= PARTITION_READ_RANGE; + error= common_index_read(m_table->record[0], have_start_key); + DBUG_RETURN(error); +} + + +/** + Read next record in read of a range with start and end key. + + @return Operation status. + @retval 0 Success + @retval != 0 Error code +*/ + +int Partition_helper::ph_read_range_next() +{ + DBUG_ENTER("Partition_helper::ph_read_range_next"); + + if (m_ordered_scan_ongoing) + { + DBUG_RETURN(handle_ordered_next(m_table->record[0], get_eq_range())); + } + DBUG_RETURN(handle_unordered_next(m_table->record[0], get_eq_range())); +} + + +/** + Common routine to set up index scans. + + Find out which partitions we'll need to read when scanning the specified + range. + + If we need to scan only one partition, set m_ordered_scan_ongoing=FALSE + as we will not need to do merge ordering. + + @param buf Buffer to later return record in (this function + needs it to calculate partitioning function values) + + @param idx_read_flag TRUE <=> m_start_key has range start endpoint which + probably can be used to determine the set of + partitions to scan. + FALSE <=> there is no start endpoint. + + @return Operation status. + @retval 0 Success + @retval !=0 Error code +*/ + +int Partition_helper::partition_scan_set_up(uchar * buf, bool idx_read_flag) +{ + DBUG_ENTER("Partition_helper::partition_scan_set_up"); + + if (idx_read_flag) + get_partition_set(m_table, + buf, + m_handler->active_index, + &m_start_key, + &m_part_spec); + else + { + // TODO: set to get_first_used_part() instead! + m_part_spec.start_part= 0; + // TODO: Implement bitmap_get_last_set() and use that here! + m_part_spec.end_part= m_tot_parts - 1; + } + if (m_part_spec.start_part > m_part_spec.end_part) + { + /* + We discovered a partition set but the set was empty so we report + key not found. + */ + DBUG_PRINT("info", ("scan with no partition to scan")); + m_table->status= STATUS_NOT_FOUND; + DBUG_RETURN(HA_ERR_END_OF_FILE); + } + if (m_part_spec.start_part == m_part_spec.end_part) + { + /* + We discovered a single partition to scan, this never needs to be + performed using the ordered index scan. + */ + DBUG_PRINT("info", ("index scan using the single partition %d", + m_part_spec.start_part)); + m_ordered_scan_ongoing= FALSE; + } + else + { + /* + Set m_ordered_scan_ongoing according how the scan should be done + Only exact partitions are discovered atm by get_partition_set. + Verify this, also bitmap must have at least one bit set otherwise + the result from this table is the empty set. + */ + uint start_part= m_part_info->get_first_used_partition(); + if (start_part == MY_BIT_NONE) + { + DBUG_PRINT("info", ("scan with no partition to scan")); + m_table->status= STATUS_NOT_FOUND; + DBUG_RETURN(HA_ERR_END_OF_FILE); + } + if (start_part > m_part_spec.start_part) + m_part_spec.start_part= start_part; + m_ordered_scan_ongoing= m_ordered; + } + DBUG_ASSERT(m_part_spec.start_part < m_tot_parts); + DBUG_ASSERT(m_part_spec.end_part < m_tot_parts); + DBUG_RETURN(0); +} + + +/** + Common routine to handle index_next with unordered results. + + These routines are used to scan partitions without considering order. + This is performed in two situations. + 1) In read_multi_range this is the normal case + 2) When performing any type of index_read, index_first, index_last where + all fields in the partition function is bound. In this case the index + scan is performed on only one partition and thus it isn't necessary to + perform any sort. + + @param[out] buf Read row in MySQL Row Format. + @param[in] next_same Called from index_next_same. + + @return Operation status. + @retval HA_ERR_END_OF_FILE End of scan + @retval 0 Success + @retval other Error code +*/ + +int Partition_helper::handle_unordered_next(uchar *buf, bool is_next_same) +{ + int error; + DBUG_ENTER("Partition_helper::handle_unordered_next"); + + if (m_part_spec.start_part >= m_tot_parts) + { + /* Should only happen with SQL HANDLER! */ + DBUG_ASSERT(m_table->open_by_handler); + DBUG_RETURN(HA_ERR_END_OF_FILE); + } + + /* + We should consider if this should be split into three functions as + partition_read_range is_next_same are always local constants + */ + + if (m_index_scan_type == PARTITION_READ_RANGE) + { + DBUG_ASSERT(buf == m_table->record[0]); + error= read_range_next_in_part(m_part_spec.start_part, NULL); + } + else if (is_next_same) + { + error= index_next_same_in_part(m_part_spec.start_part, + buf, + m_start_key.key, + m_start_key.length); + } + else + { + error= index_next_in_part(m_part_spec.start_part, buf); + } + + if (error == HA_ERR_END_OF_FILE) + { + m_part_spec.start_part++; // Start using next part + error= handle_unordered_scan_next_partition(buf); + } + else + { + m_last_part= m_part_spec.start_part; + } + DBUG_RETURN(error); +} + + +/** + Handle index_next when changing to new partition. + + This routine is used to start the index scan on the next partition. + Both initial start and after completing scan on one partition. + + @param[out] buf Read row in MySQL Row Format + + @return Operation status. + @retval HA_ERR_END_OF_FILE End of scan + @retval 0 Success + @retval other Error code +*/ + +int Partition_helper::handle_unordered_scan_next_partition(uchar * buf) +{ + uint i= m_part_spec.start_part; + int saved_error= HA_ERR_END_OF_FILE; + DBUG_ENTER("Partition_helper::handle_unordered_scan_next_partition"); + + if (i) + i= m_part_info->get_next_used_partition(i - 1); + else + i= m_part_info->get_first_used_partition(); + + for (; + i <= m_part_spec.end_part; + i= m_part_info->get_next_used_partition(i)) + { + int error; + m_part_spec.start_part= i; + switch (m_index_scan_type) { + case PARTITION_READ_RANGE: + DBUG_ASSERT(buf == m_table->record[0]); + DBUG_PRINT("info", ("read_range_first on partition %d", i)); + error= read_range_first_in_part(i, + NULL, + m_start_key.key? &m_start_key: NULL, + m_handler->end_range, + get_eq_range(), + false); + break; + case PARTITION_INDEX_READ: + DBUG_PRINT("info", ("index_read on partition %d", i)); + error= index_read_map_in_part(i, + buf, + m_start_key.key, + m_start_key.keypart_map, + m_start_key.flag); + break; + case PARTITION_INDEX_FIRST: + DBUG_PRINT("info", ("index_first on partition %d", i)); + error= index_first_in_part(i, buf); + break; + case PARTITION_INDEX_FIRST_UNORDERED: + /* When is this ever used? */ + DBUG_ASSERT(0); + /* + We perform a scan without sorting and this means that we + should not use the index_first since not all handlers + support it and it is also unnecessary to restrict sort + order. + */ + DBUG_PRINT("info", ("read_range_first on partition %d", i)); + DBUG_ASSERT(buf == m_table->record[0]); + error= read_range_first_in_part(i, + NULL, + 0, + m_handler->end_range, + get_eq_range(), + 0); + break; + default: + DBUG_ASSERT(0); + DBUG_RETURN(HA_ERR_INTERNAL_ERROR); + } + if (!error) + { + m_last_part= i; + DBUG_RETURN(0); + } + if ((error != HA_ERR_END_OF_FILE) && (error != HA_ERR_KEY_NOT_FOUND)) + DBUG_RETURN(error); + + /* + If HA_ERR_KEY_NOT_FOUND, we must return that error instead of + HA_ERR_END_OF_FILE, to be able to continue search. + */ + if (saved_error != HA_ERR_KEY_NOT_FOUND) + saved_error= error; + DBUG_PRINT("info", ("END_OF_FILE/KEY_NOT_FOUND on partition %d", i)); + } + if (saved_error == HA_ERR_END_OF_FILE) + m_part_spec.start_part= NO_CURRENT_PART_ID; + DBUG_RETURN(saved_error); +} + + +/** + Common routine to start index scan with ordered results. + + @param[out] buf Read row in MySQL Row Format + + @return Operation status + @retval HA_ERR_END_OF_FILE End of scan + @retval HA_ERR_KEY_NOT_FOUND End of scan + @retval 0 Success + @retval other Error code + + @details + This part contains the logic to handle index scans that require ordered + output. This includes all except those started by read_range_first with + the flag ordered set to FALSE. Thus most direct index_read and all + index_first and index_last. + + We implement ordering by keeping one record plus a key buffer for each + partition. Every time a new entry is requested we will fetch a new + entry from the partition that is currently not filled with an entry. + Then the entry is put into its proper sort position. + + Returning a record is done by getting the top record, copying the + record to the request buffer and setting the partition as empty on + entries. +*/ + +int Partition_helper::handle_ordered_index_scan(uchar *buf) +{ + uint i; + std::vector parts; + bool found= FALSE; + uchar *part_rec_buf_ptr= m_ordered_rec_buffer; + int saved_error= HA_ERR_END_OF_FILE; + DBUG_ENTER("Partition_helper::handle_ordered_index_scan"); + DBUG_ASSERT(part_rec_buf_ptr); + + if (m_key_not_found) + { + m_key_not_found= false; + bitmap_clear_all(&m_key_not_found_partitions); + DBUG_PRINT("info", ("Cleared m_key_not_found_partitions")); + } + m_top_entry= NO_CURRENT_PART_ID; + m_queue->clear(); + parts.reserve(m_queue->capacity()); + DBUG_ASSERT(m_part_info->is_partition_used(m_part_spec.start_part)); + + /* + Position part_rec_buf_ptr to point to the first used partition >= + start_part. There may be partitions marked by used_partitions, + but is before start_part. These partitions has allocated record buffers + but is dynamically pruned, so those buffers must be skipped. + */ + for (i= m_part_info->get_first_used_partition(); + i < m_part_spec.start_part; + i= m_part_info->get_next_used_partition(i)) + { + part_rec_buf_ptr+= m_rec_offset + m_rec_length; + } + DBUG_PRINT("info", ("m_part_spec.start_part %u first_used_part %u", + m_part_spec.start_part, i)); + for (/* continue from above */ ; + i <= m_part_spec.end_part; + i= m_part_info->get_next_used_partition(i)) + { + DBUG_PRINT("info", ("reading from part %u (scan_type: %u inx: %u)", + i, m_index_scan_type, m_handler->active_index)); + DBUG_ASSERT(i == uint2korr(part_rec_buf_ptr)); + uchar *rec_buf_ptr= part_rec_buf_ptr + m_rec_offset; + uchar *read_buf; + int error; + DBUG_PRINT("info", ("part %u, scan_type %d", i, m_index_scan_type)); + + /* ICP relies on Item evaluation, which expects the row in record[0]. */ + if (m_handler->pushed_idx_cond) + read_buf= m_table->record[0]; + else + read_buf= rec_buf_ptr; + + switch (m_index_scan_type) { + case PARTITION_INDEX_READ: + error= index_read_map_in_part(i, + read_buf, + m_start_key.key, + m_start_key.keypart_map, + m_start_key.flag); + break; + case PARTITION_INDEX_FIRST: + error= index_first_in_part(i, read_buf); + break; + case PARTITION_INDEX_LAST: + error= index_last_in_part(i, read_buf); + break; + case PARTITION_INDEX_READ_LAST: + error= index_read_last_map_in_part(i, + read_buf, + m_start_key.key, + m_start_key.keypart_map); + break; + case PARTITION_READ_RANGE: + { + /* + To enable optimization in derived engines, we provide a read buffer + pointer if we want to read into something different than table->record[0] + (which read_range_* always uses). + */ + error= read_range_first_in_part(i, + read_buf == m_table->record[0] + ? NULL : read_buf, + m_start_key.key ? &m_start_key : NULL, + m_handler->end_range, + get_eq_range(), + true); + break; + } + default: + DBUG_ASSERT(false); + DBUG_RETURN(HA_ERR_END_OF_FILE); + } + DBUG_PRINT("info", ("error %d from partition %u", error, i)); + /* When using ICP, copy record[0] to the priority queue for sorting. */ + if (m_handler->pushed_idx_cond) + memcpy(rec_buf_ptr, read_buf, m_rec_length); + if (!error) + { + found= true; + if (m_ref_usage != REF_NOT_USED) + { + /* position_in_last_part needs m_last_part set. */ + m_last_part= i; + position_in_last_part(part_rec_buf_ptr + PARTITION_BYTES_IN_POS, + rec_buf_ptr); + } + /* + Save for later insertion in queue; + */ + parts.push_back(part_rec_buf_ptr); + DBUG_DUMP("row", read_buf, m_rec_length); + } + else if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) + { + DBUG_RETURN(error); + } + else if (error == HA_ERR_KEY_NOT_FOUND) + { + DBUG_PRINT("info", ("HA_ERR_KEY_NOT_FOUND from partition %u", i)); + bitmap_set_bit(&m_key_not_found_partitions, i); + m_key_not_found= true; + saved_error= error; + } + part_rec_buf_ptr+= m_rec_offset + m_rec_length; + } + if (found) + { + /* + We found at least one partition with data, now sort all entries and + after that read the first entry and copy it to the buffer to return in. + */ + m_queue->m_max_at_top= m_reverse_order; + m_queue->m_keys= m_curr_key_info; + DBUG_ASSERT(m_queue->empty()); + /* + If PK, we should not sort by rowid, since that is already done + through the KEY setup. + */ + DBUG_ASSERT(!m_curr_key_info[1] || m_ref_usage == REF_NOT_USED); + m_queue->assign(parts); + return_top_record(buf); + m_table->status= 0; + DBUG_PRINT("info", ("Record returned from partition %d", m_top_entry)); + DBUG_RETURN(0); + } + DBUG_RETURN(saved_error); +} + + +/** + Return the top record in sort order. + + @param[out] buf Row returned in MySQL Row Format. +*/ + +void Partition_helper::return_top_record(uchar *buf) +{ + uint part_id; + uchar *key_buffer= m_queue->top(); + uchar *rec_buffer= key_buffer + m_rec_offset; + + part_id= uint2korr(key_buffer); + copy_cached_row(buf, rec_buffer); + DBUG_PRINT("info", ("from part_id %u", part_id)); + DBUG_DUMP("returned_row", buf, m_table->s->reclength); + m_last_part= part_id; + m_top_entry= part_id; +} + + +/** + Add index_next/prev results from partitions without exact match. + + If there where any partitions that returned HA_ERR_KEY_NOT_FOUND when + ha_index_read_map was done, those partitions must be included in the + following index_next/prev call. +*/ + +int Partition_helper::handle_ordered_index_scan_key_not_found() +{ + int error; + uint i; + size_t old_elements= m_queue->size(); + uchar *part_buf= m_ordered_rec_buffer; + uchar *curr_rec_buf= NULL; + DBUG_ENTER("Partition_helper::handle_ordered_index_scan_key_not_found"); + DBUG_ASSERT(m_key_not_found); + DBUG_ASSERT(part_buf); + /* + Loop over all used partitions to get the correct offset + into m_ordered_rec_buffer. + */ + for (i= m_part_info->get_first_used_partition(); + i < MY_BIT_NONE; + i= m_part_info->get_next_used_partition(i)) + { + if (bitmap_is_set(&m_key_not_found_partitions, i)) + { + /* + This partition is used and did return HA_ERR_KEY_NOT_FOUND + in index_read_map. + */ + uchar *read_buf; + curr_rec_buf= part_buf + m_rec_offset; + /* ICP relies on Item evaluation, which expects the row in record[0]. */ + if (m_handler->pushed_idx_cond) + read_buf= m_table->record[0]; + else + read_buf= curr_rec_buf; + + if (m_reverse_order) + error= index_prev_in_part(i, read_buf); + else + error= index_next_in_part(i, read_buf); + /* HA_ERR_KEY_NOT_FOUND is not allowed from index_next! */ + DBUG_ASSERT(error != HA_ERR_KEY_NOT_FOUND); + DBUG_PRINT("info", ("Filling from partition %u reverse %u error %d", + i, m_reverse_order, error)); + if (!error) + { + /* When using ICP, copy record[0] to the priority queue for sorting. */ + if (m_handler->pushed_idx_cond) + memcpy(curr_rec_buf, read_buf, m_rec_length); + if (m_ref_usage != REF_NOT_USED) + { + /* position_in_last_part needs m_last_part set. */ + m_last_part= i; + position_in_last_part(part_buf + PARTITION_BYTES_IN_POS, + curr_rec_buf); + } + m_queue->push(part_buf); + } + else if (error != HA_ERR_END_OF_FILE && error != HA_ERR_KEY_NOT_FOUND) + DBUG_RETURN(error); + } + part_buf+= m_rec_offset + m_rec_length; + } + DBUG_ASSERT(curr_rec_buf); + bitmap_clear_all(&m_key_not_found_partitions); + m_key_not_found= false; + + if (m_queue->size() > old_elements) + { + /* Update m_top_entry, which may have changed. */ + uchar *key_buffer= m_queue->top(); + m_top_entry= uint2korr(key_buffer); + } + DBUG_RETURN(0); +} + + +/** + Common routine to handle index_next with ordered results. + + @param[out] buf Read row in MySQL Row Format. + @param[in] next_same Called from index_next_same. + + @return Operation status. + @retval HA_ERR_END_OF_FILE End of scan + @retval 0 Success + @retval other Error code +*/ + +int Partition_helper::handle_ordered_next(uchar *buf, bool is_next_same) +{ + int error; + uint part_id= m_top_entry; + uchar *rec_buf= m_queue->empty() ? NULL : m_queue->top() + m_rec_offset; + uchar *read_buf; + DBUG_ENTER("Partition_helper::handle_ordered_next"); + + if (m_reverse_order) + { + /* + TODO: To support change of direction (index_prev -> index_next, + index_read_map(HA_READ_KEY_EXACT) -> index_prev etc.) + We would need to: + - Step back all cursors we have a buffered row from a previous next/prev + call (i.e. for all partitions we previously called index_prev, we must + call index_next and skip that row. + - empty the priority queue and initialize it again with reverse ordering. + */ + DBUG_ASSERT(m_table->open_by_handler); + DBUG_RETURN(HA_ERR_WRONG_COMMAND); + } + + if (m_key_not_found) + { + if (is_next_same) + { + /* Only rows which match the key. */ + m_key_not_found= false; + bitmap_clear_all(&m_key_not_found_partitions); + } + else + { + /* There are partitions not included in the index record queue. */ + size_t old_elements= m_queue->size(); + if ((error= handle_ordered_index_scan_key_not_found())) + DBUG_RETURN(error); + /* + If the queue top changed, i.e. one of the partitions that gave + HA_ERR_KEY_NOT_FOUND in index_read_map found the next record, + return it. + Otherwise replace the old with a call to index_next (fall through). + */ + if (old_elements != m_queue->size() && part_id != m_top_entry) + { + return_top_record(buf); + DBUG_PRINT("info", ("Returning row from part %u (prev KEY_NOT_FOUND)", + m_top_entry)); + DBUG_RETURN(0); + } + } + } + if (part_id >= m_tot_parts) + DBUG_RETURN(HA_ERR_END_OF_FILE); + + DBUG_PRINT("info", ("next row from part %u (inx %u)", + part_id, m_handler->active_index)); + + /* Assert that buffer for fetch is not NULL */ + DBUG_ASSERT(rec_buf); + + /* ICP relies on Item evaluation, which expects the row in record[0]. */ + if (m_handler->pushed_idx_cond) + read_buf= m_table->record[0]; + else + read_buf= rec_buf; + + + if (m_index_scan_type == PARTITION_READ_RANGE) + { + error= read_range_next_in_part(part_id, + read_buf == m_table->record[0] + ? NULL : read_buf); + } + else if (!is_next_same) + error= index_next_in_part(part_id, read_buf); + else + error= index_next_same_in_part(part_id, + read_buf, + m_start_key.key, + m_start_key.length); + if (error) + { + if (error == HA_ERR_END_OF_FILE) + { + /* Return next buffered row */ + if (!m_queue->empty()) + m_queue->pop(); + if (m_queue->empty()) + { + /* + If priority queue is empty, we have finished fetching rows from all + partitions. Reset the value of next partition to NONE. This would + imply HA_ERR_END_OF_FILE for all future calls. + */ + m_top_entry= NO_CURRENT_PART_ID; + } + else + { + return_top_record(buf); + DBUG_PRINT("info", ("Record returned from partition %u (2)", + m_top_entry)); + m_table->status= 0; + error= 0; + } + } + DBUG_RETURN(error); + } + /* When using ICP, copy record[0] to the priority queue for sorting. */ + if (m_handler->pushed_idx_cond) + memcpy(rec_buf, read_buf, m_rec_length); + if (m_ref_usage != REF_NOT_USED) + { + /* position_in_last_part needs m_last_part set. */ + m_last_part= part_id; + position_in_last_part(rec_buf - m_rec_offset + PARTITION_BYTES_IN_POS, + rec_buf); + } + DBUG_DUMP("rec_buf", rec_buf, m_rec_length); + m_queue->update_top(); + return_top_record(buf); + DBUG_PRINT("info", ("Record returned from partition %u", m_top_entry)); + DBUG_RETURN(0); +} + + +/** + Common routine to handle index_prev with ordered results. + + @param[out] buf Read row in MySQL Row Format. + + @return Operation status. + @retval HA_ERR_END_OF_FILE End of scan + @retval 0 Success + @retval other Error code +*/ + +int Partition_helper::handle_ordered_prev(uchar *buf) +{ + int error; + uint part_id= m_top_entry; + uchar *rec_buf= m_queue->empty() ? NULL : m_queue->top() + m_rec_offset; + uchar *read_buf; + DBUG_ENTER("Partition_helper::handle_ordered_prev"); + + if (!m_reverse_order) + { + /* TODO: See comment in handle_ordered_next(). */ + DBUG_ASSERT(m_table->open_by_handler); + DBUG_RETURN(HA_ERR_WRONG_COMMAND); + } + + if (m_key_not_found) + { + /* There are partitions not included in the index record queue. */ + size_t old_elements= m_queue->size(); + if ((error= handle_ordered_index_scan_key_not_found())) + DBUG_RETURN(error); + if (old_elements != m_queue->size() && part_id != m_top_entry) + { + /* + Should only be possible for when HA_READ_KEY_EXACT was previously used, + which is not supported to have a subsequent call for PREV. + I.e. HA_READ_KEY_EXACT is considered to not have reverse order! + */ + DBUG_ASSERT(0); + /* + If the queue top changed, i.e. one of the partitions that gave + HA_ERR_KEY_NOT_FOUND in index_read_map found the next record, + return it. + Otherwise replace the old with a call to index_next (fall through). + */ + return_top_record(buf); + DBUG_RETURN(0); + } + } + + if (part_id >= m_tot_parts) + { + /* This should never happen, except for SQL HANDLER calls! */ + DBUG_ASSERT(m_table->open_by_handler); + DBUG_RETURN(HA_ERR_END_OF_FILE); + } + + /* Assert that buffer for fetch is not NULL */ + DBUG_ASSERT(rec_buf); + + /* ICP relies on Item evaluation, which expects the row in record[0]. */ + if (m_handler->pushed_idx_cond) + read_buf= m_table->record[0]; + else + read_buf= rec_buf; + + if ((error= index_prev_in_part(part_id, read_buf))) + { + if (error == HA_ERR_END_OF_FILE) + { + if (!m_queue->empty()) + m_queue->pop(); + if (m_queue->empty()) + { + /* + If priority queue is empty, we have finished fetching rows from all + partitions. Reset the value of next partition to NONE. This would + imply HA_ERR_END_OF_FILE for all future calls. + */ + m_top_entry= NO_CURRENT_PART_ID; + } + else + { + return_top_record(buf); + DBUG_PRINT("info", ("Record returned from partition %d (2)", + m_top_entry)); + error= 0; + m_table->status= 0; + } + } + DBUG_RETURN(error); + } + /* When using ICP, copy record[0] to the priority queue for sorting. */ + if (m_handler->pushed_idx_cond) + memcpy(rec_buf, read_buf, m_rec_length); + + if (m_ref_usage != REF_NOT_USED) + { + /* position_in_last_part needs m_last_part set. */ + m_last_part= part_id; + position_in_last_part(rec_buf - m_rec_offset + PARTITION_BYTES_IN_POS, + rec_buf); + } + m_queue->update_top(); + return_top_record(buf); + DBUG_PRINT("info", ("Record returned from partition %d", m_top_entry)); + DBUG_RETURN(0); +} + +/** + Get statistics from a specific partition. + + @param[out] stat_info Area to report values into. + @param[out] check_sum Check sum of partition. + @param[in] part_id Partition to report from. +*/ +void +Partition_helper::get_dynamic_partition_info_low(PARTITION_STATS *stat_info, + ha_checksum *check_sum, + uint part_id) +{ + ha_statistics *part_stat= &m_handler->stats; + DBUG_ASSERT(bitmap_is_set(&m_part_info->read_partitions, part_id)); + DBUG_ASSERT(bitmap_is_subset(&m_part_info->read_partitions, + &m_part_info->lock_partitions)); + DBUG_ASSERT(bitmap_is_subset(&m_part_info->lock_partitions, + &m_part_info->read_partitions)); + bitmap_clear_all(&m_part_info->read_partitions); + bitmap_set_bit(&m_part_info->read_partitions, part_id); + m_handler->info(HA_STATUS_TIME | + HA_STATUS_VARIABLE | + HA_STATUS_VARIABLE_EXTRA | + HA_STATUS_NO_LOCK); + stat_info->records= part_stat->records; + stat_info->mean_rec_length= part_stat->mean_rec_length; + stat_info->data_file_length= part_stat->data_file_length; + stat_info->max_data_file_length= part_stat->max_data_file_length; + stat_info->index_file_length= part_stat->index_file_length; + stat_info->delete_length= part_stat->delete_length; + stat_info->create_time= part_stat->create_time; + stat_info->update_time= part_stat->update_time; + stat_info->check_time= part_stat->check_time; + if (get_thd()->variables.old_mode ? + m_handler->ha_table_flags() & HA_HAS_OLD_CHECKSUM : + m_handler->ha_table_flags() & HA_HAS_NEW_CHECKSUM) + { + *check_sum= checksum_in_part(part_id); + } + bitmap_copy(&m_part_info->read_partitions, &m_part_info->lock_partitions); +} + + +/** + Get checksum for table. + + @return Checksum or 0 if not supported, which also may be a correct checksum!. +*/ + +ha_checksum Partition_helper::ph_checksum() const +{ + ha_checksum sum= 0; + if (get_thd()->variables.old_mode ? + m_handler->ha_table_flags() & HA_HAS_OLD_CHECKSUM : + m_handler->ha_table_flags() & HA_HAS_NEW_CHECKSUM) + { + for (uint i= 0; i < m_tot_parts; i++) + { + sum+= checksum_in_part(i); + } + } + return sum; +} diff --git a/sql/partitioning/partition_handler.h b/sql/partitioning/partition_handler.h new file mode 100644 index 00000000000..cf4e1dcb24b --- /dev/null +++ b/sql/partitioning/partition_handler.h @@ -0,0 +1,1113 @@ +#ifndef PARTITION_HANDLER_INCLUDED +#define PARTITION_HANDLER_INCLUDED + +/* + Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; version 2 of + the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "my_global.h" // uint etc. +#include "my_base.h" // ha_rows. +#include "handler.h" // Handler_share +#include "sql_partition.h" // part_id_range +#include "mysqld_error.h" // ER_ILLEGAL_HA +#include "priority_queue.h" +#include "key.h" // key_rec_cmp +#include "ha_partition.h" +#include + +#define PARTITION_BYTES_IN_POS 2 + +/* forward declarations */ +typedef struct st_mem_root MEM_ROOT; + +static const uint NO_CURRENT_PART_ID= UINT_MAX32; + +/** + bits in Partition_handler::alter_flags(): + + HA_PARTITION_FUNCTION_SUPPORTED indicates that the function is + supported at all. + HA_FAST_CHANGE_PARTITION means that optimized variants of the changes + exists but they are not necessarily done online. + + HA_ONLINE_DOUBLE_WRITE means that the handler supports writing to both + the new partition and to the old partitions when updating through the + old partitioning schema while performing a change of the partitioning. + This means that we can support updating of the table while performing + the copy phase of the change. For no lock at all also a double write + from new to old must exist and this is not required when this flag is + set. + This is actually removed even before it was introduced the first time. + The new idea is that handlers will handle the lock level already in + store_lock for ALTER TABLE partitions. + TODO: Implement this via the alter-inplace api. +*/ + +enum enum_part_operation { + OPTIMIZE_PARTS= 0, + ANALYZE_PARTS, + CHECK_PARTS, + REPAIR_PARTS, + ASSIGN_KEYCACHE_PARTS, + PRELOAD_KEYS_PARTS +}; + + +/** + Initialize partitioning (currently only PSI keys). +*/ +void partitioning_init(); + + +/** + Class for partitioning specific operations. + + Returned from handler::get_partition_handler(). +*/ +class Partition_handler :public Sql_alloc +{ +public: + Partition_handler() {} + ~Partition_handler() {} + + bool init(uint num_parts); + + /** + Get dynamic table information from partition. + + @param[out] stat_info Statistics struct to fill in. + @param[out] check_sum Check sum value to fill in if supported. + @param[in] part_id Partition to report for. + + @note stat_info and check_sum are initialized by caller. + check_sum is only expected to be updated if HA_HAS_CHECKSUM. + */ + virtual void get_dynamic_partition_info(PARTITION_STATS *stat_info, + uint part_id) = 0; + + /** + Get default number of partitions. + + Used during creating a partitioned table. + + @param info Create info. + @return Number of default partitions. + */ + virtual int get_default_num_partitions(HA_CREATE_INFO *info) { return 1;} + /** + Setup auto partitioning. + + Called for engines with HA_USE_AUTO_PARTITION to setup the partition info + object + + @param[in,out] part_info Partition object to setup. + */ + virtual void set_auto_partitions(partition_info *part_info) { return; } + /** + Get number of partitions for table in SE + + @param name normalized path(same as open) to the table + + @param[out] num_parts Number of partitions + + @retval false for success + @retval true for failure, for example table didn't exist in engine + */ + virtual bool get_num_parts(const char *name, + uint *num_parts) + { + *num_parts= 0; + return false; + } + /** + Set the partition info object to be used by the handler. + + @param part_info Partition info to be used by the handler. + @param early True if called when part_info only created and parsed, + but not setup, checked or fixed. + */ + virtual void set_part_info(partition_info *part_info) = 0; + /** + Initialize partition. + + @param mem_root Memory root for memory allocations. + + @return Operation status + @retval false Success. + @retval true Failure. + */ + virtual bool initialize_partition(MEM_ROOT *mem_root) {return false;} + + + /** + Alter flags. + + Given a set of alter table flags, return which is supported. + + @param flags Alter table operation flags. + + @return Supported alter table flags. + */ + virtual uint alter_flags(uint flags) const + { return 0; } + +private: + /** + Truncate partition. + + Low-level primitive for handler, implementing + Partition_handler::truncate_partition(). + + @return Operation status + @retval 0 Success. + @retval != 0 Error code. + */ + virtual int truncate_partition_low() + { return HA_ERR_WRONG_COMMAND; } + /** + Truncate partition. + + Low-level primitive for handler, implementing + Partition_handler::change_partitions(). + + @param[in] create_info Table create info. + @param[in] path Path including table name. + @param[out] copied Number of rows copied. + @param[out] deleted Number of rows deleted. + + @return Operation status + @retval 0 Success. + @retval != 0 Error code. + */ + virtual int change_partitions_low(HA_CREATE_INFO *create_info, + const char *path, + ulonglong * const copied, + ulonglong * const deleted) + { + my_error(ER_ILLEGAL_HA, MYF(0), create_info->alias); + return HA_ERR_WRONG_COMMAND; + } + /** + Return the table handler. + + For some partitioning specific functions it is still needed to access + the handler directly for transaction handling (mark_trx_read_write()) + and to assert correct locking. + + @return handler or NULL if not supported. + */ + virtual handler *get_handler() + { return NULL; } +}; + + +/// Maps compare function to strict weak ordering required by Priority_queue. +struct Key_rec_less +{ + typedef int (*key_compare_fun)(void*, uchar *, uchar *); + + explicit Key_rec_less(KEY **keys) + : m_keys(keys), m_fun(key_rec_cmp), m_max_at_top(false) + { + } + + bool operator()(uchar *first, uchar *second) + { + const int cmpval= + (*m_fun)(m_keys, first + m_rec_offset, second + m_rec_offset); + return m_max_at_top ? cmpval < 0 : cmpval > 0; + } + + KEY **m_keys; + key_compare_fun m_fun; + uint m_rec_offset; + bool m_max_at_top; +}; + + +/** + Partition_helper is a helper class that implements most generic partitioning + functionality such as: + table scan, index scan (both ordered and non-ordered), + insert (write_row()), delete and update. + And includes ALTER TABLE ... ADD/COALESCE/DROP/REORGANIZE/... PARTITION + support. + It also implements a cache for the auto increment value and check/repair for + rows in wrong partition. + + How to use it: + Inherit it and implement: + - *_in_part() functions for row operations. + - prepare_for_new_partitions(), create_new_partition(), close_new_partitions() + write_row_in_new_part() for handling 'fast' alter partition. +*/ +class Partition_helper : public Sql_alloc +{ + typedef Priority_queue, Key_rec_less> Prio_queue; +public: + Partition_helper(handler *main_handler); + ~Partition_helper(); + + /** + Set partition info. + + To be called from Partition_handler. + + @param part_info Partition info to use. + @param early True if called when part_info only created and parsed, + but not setup, checked or fixed. + */ + virtual void set_part_info_low(partition_info *part_info, bool early); + /** + Initialize variables used before the table is opened. + + @param mem_root Memory root to allocate things from (not yet used). + + @return Operation status. + @retval false success. + @retval true failure. + */ + inline bool init_partitioning(MEM_ROOT *mem_root) + { +#ifndef DBUG_OFF + m_key_not_found_partitions.bitmap= NULL; +#endif + return false; + } + + + /** + INSERT/UPDATE/DELETE functions. + @see handler.h + @{ + */ + + /** + Insert a row to the partitioned table. + + @param buf The row in MySQL Row Format. + + @return Operation status. + @retval 0 Success + @retval != 0 Error code + */ + int ph_write_row(uchar *buf); + /** + Update an existing row in the partitioned table. + + Yes, update_row() does what you expect, it updates a row. old_data will + have the previous row record in it, while new_data will have the newest + data in it. + Keep in mind that the server can do updates based on ordering if an + ORDER BY clause was used. Consecutive ordering is not guaranteed. + + If the new record belongs to a different partition than the old record + then it will be inserted into the new partition and deleted from the old. + + new_data is always record[0] + old_data is always record[1] + + @param old_data The old record in MySQL Row Format. + @param new_data The new record in MySQL Row Format. + + @return Operation status. + @retval 0 Success + @retval != 0 Error code + */ + int ph_update_row(const uchar *old_data, uchar *new_data); + /** + Delete an existing row in the partitioned table. + + This will delete a row. buf will contain a copy of the row to be deleted. + The server will call this right after the current row has been read + (from either a previous rnd_xxx() or index_xxx() call). + If you keep a pointer to the last row or can access a primary key it will + make doing the deletion quite a bit easier. + Keep in mind that the server does no guarantee consecutive deletions. + ORDER BY clauses can be used. + + buf is either record[0] or record[1] + + @param buf The record in MySQL Row Format. + + @return Operation status. + @retval 0 Success + @retval != 0 Error code + */ + int ph_delete_row(const uchar *buf); + + /** @} */ + + /** Release unused auto increment values. */ + void ph_release_auto_increment(); + /** + Calculate key hash value from an null terminated array of fields. + Support function for KEY partitioning. + + @param field_array An array of the fields in KEY partitioning + + @return hash_value calculated + + @note Uses the hash function on the character set of the field. + Integer and floating point fields use the binary character set by default. + */ + static uint32 ph_calculate_key_hash_value(Field **field_array); + /** Get checksum for table. + @return Checksum or 0 if not supported (which also may be a correct checksum!). + */ + ha_checksum ph_checksum() const; + + /** + MODULE full table scan + + This module is used for the most basic access method for any table + handler. This is to fetch all data through a full table scan. No + indexes are needed to implement this part. + It contains one method to start the scan (rnd_init) that can also be + called multiple times (typical in a nested loop join). Then proceeding + to the next record (rnd_next) and closing the scan (rnd_end). + To remember a record for later access there is a method (position) + and there is a method used to retrieve the record based on the stored + position. + The position can be a file position, a primary key, a ROWID dependent + on the handler below. + + unlike index_init(), rnd_init() can be called two times + without rnd_end() in between (it only makes sense if scan=1). + then the second call should prepare for the new table scan + (e.g if rnd_init allocates the cursor, second call should + position it to the start of the table, no need to deallocate + and allocate it again. + @see handler.h + @{ + */ + + int ph_rnd_init(bool scan); + int ph_rnd_end(); + int ph_rnd_next(uchar *buf); + void ph_position(const uchar *record); + int ph_rnd_pos(uchar *buf, uchar *pos); + int ph_rnd_pos_by_record(uchar *record); + + /** @} */ + + /** + MODULE index scan + + This part of the handler interface is used to perform access through + indexes. The interface is defined as a scan interface but the handler + can also use key lookup if the index is a unique index or a primary + key index. + Index scans are mostly useful for SELECT queries but are an important + part also of UPDATE, DELETE, REPLACE and CREATE TABLE table AS SELECT + and so forth. + Naturally an index is needed for an index scan and indexes can either + be ordered, hash based. Some ordered indexes can return data in order + but not necessarily all of them. + There are many flags that define the behavior of indexes in the + various handlers. These methods are found in the optimizer module. + ------------------------------------------------------------------------- + + index_read is called to start a scan of an index. The find_flag defines + the semantics of the scan. These flags are defined in + include/my_base.h + index_read_idx is the same but also initializes index before calling doing + the same thing as index_read. Thus it is similar to index_init followed + by index_read. This is also how we implement it. + + index_read/index_read_idx does also return the first row. Thus for + key lookups, the index_read will be the only call to the handler in + the index scan. + + index_init initializes an index before using it and index_end does + any end processing needed. + @{ + */ + + int ph_index_init_setup(uint key_nr, bool sorted); + int ph_index_init(uint key_nr, bool sorted); + int ph_index_end(); + /* + These methods are used to jump to next or previous entry in the index + scan. There are also methods to jump to first and last entry. + */ + int ph_index_first(uchar *buf); + int ph_index_last(uchar *buf); + int ph_index_next(uchar *buf); + int ph_index_next_same(uchar *buf, const uchar *key, uint keylen); + int ph_index_prev(uchar *buf); + int ph_index_read_map(uchar *buf, + const uchar *key, + key_part_map keypart_map, + enum ha_rkey_function find_flag); + int ph_index_read_last_map(uchar *buf, + const uchar *key, + key_part_map keypart_map); + int ph_index_read_idx_map(uchar *buf, + uint index, + const uchar *key, + key_part_map keypart_map, + enum ha_rkey_function find_flag); + int ph_read_range_first(const key_range *start_key, + const key_range *end_key, + bool eq_range_arg, + bool sorted); + int ph_read_range_next(); + /** @} */ + + /** + Functions matching Partition_handler API. + @{ + */ + + /** + Get statistics from a specific partition. + @param[out] stat_info Area to report values into. + @param[out] check_sum Check sum of partition. + @param[in] part_id Partition to report from. + */ + virtual void get_dynamic_partition_info_low(PARTITION_STATS *stat_info, + ha_checksum *check_sum, + uint part_id); + + /** + Implement the partition changes defined by ALTER TABLE of partitions. + + Add and copy if needed a number of partitions, during this operation + only read operation is ongoing in the server. This is used by + ADD PARTITION all types as well as by REORGANIZE PARTITION. For + one-phased implementations it is used also by DROP and COALESCE + PARTITIONs. + One-phased implementation needs the new frm file, other handlers will + get zero length and a NULL reference here. + + @param[in] create_info HA_CREATE_INFO object describing all + fields and indexes in table + @param[in] path Complete path of db and table name + @param[out] copied Output parameter where number of copied + records are added + @param[out] deleted Output parameter where number of deleted + records are added + + @return Operation status + @retval 0 Success + @retval != 0 Failure + */ + int change_partitions(HA_CREATE_INFO *create_info, + const char *path, + ulonglong * const copied, + ulonglong * const deleted); + /** @} */ + +protected: + /* Common helper functions to be used by inheriting engines. */ + + /* + open/close functions. + */ + + /** + Set m_part_share, Allocate internal bitmaps etc. used by open tables. + + @param mem_root Memory root to allocate things from (not yet used). + + @return Operation status. + @retval false success. + @retval true failure. + */ + bool open_partitioning(Partition_share *part_share); + /** + Close partitioning for a table. + + Frees memory and release other resources. + */ + void close_partitioning(); + + /** + Lock auto increment value if needed. + */ + inline void lock_auto_increment() + { + /* lock already taken */ + if (m_auto_increment_safe_stmt_log_lock) + return; + DBUG_ASSERT(!m_auto_increment_lock); + if(m_table->s->tmp_table == NO_TMP_TABLE) + { + m_auto_increment_lock= true; + m_part_share->lock_auto_inc(); + } + } + /** + unlock auto increment. + */ + inline void unlock_auto_increment() + { + /* + If m_auto_increment_safe_stmt_log_lock is true, we have to keep the lock. + It will be set to false and thus unlocked at the end of the statement by + ha_partition::release_auto_increment. + */ + if(m_auto_increment_lock && !m_auto_increment_safe_stmt_log_lock) + { + m_part_share->unlock_auto_inc(); + m_auto_increment_lock= false; + } + } + /** + Get auto increment. + + Only to be used for auto increment values that are the first field in + an unique index. + + @param[in] increment Increment between generated numbers. + @param[in] nb_desired_values Number of values requested. + @param[out] first_value First reserved value (ULLONG_MAX on error). + @param[out] nb_reserved_values Number of values reserved. + */ + void get_auto_increment_first_field(ulonglong increment, + ulonglong nb_desired_values, + ulonglong *first_value, + ulonglong *nb_reserved_values); + + /** + Initialize the record priority queue used for sorted index scans. + @return Operation status. + @retval 0 Success. + @retval != 0 Error code. + */ + int init_record_priority_queue(); + /** + Destroy the record priority queue used for sorted index scans. + */ + void destroy_record_priority_queue(); + /* + Administrative support functions. + */ + + /** Print partitioning specific error. + @param error Error code. + @param errflag Error flag. + @return false if error is printed else true. + */ + bool print_partition_error(int error, myf errflag); +#if 0 + /** + Print a message row formatted for ANALYZE/CHECK/OPTIMIZE/REPAIR TABLE. + + Modeled after mi_check_print_msg. + + @param thd Thread context. + @param len Needed length for message buffer. + @param msg_type Message type. + @param db_name Database name. + @param table_name Table name. + @param op_name Operation name. + @param fmt Message (in printf format with additional arguments). + + @return Operation status. + @retval false for success else true. + */ + bool print_admin_msg(THD *thd, + uint len, + const char *msg_type, + const char *db_name, + const char *table_name, + const char *op_name, + const char *fmt, + ...); +#endif + /** + Check/fix misplaced rows. + + @param part_id Partition to check/fix. + @param repair If true, move misplaced rows to correct partition. + + @return Operation status. + @retval 0 Success + @retval != 0 Error + */ + int check_misplaced_rows(uint part_id, bool repair); + /** + Set used partitions bitmap from Alter_info. + + @return false if success else true. + */ + bool set_altered_partitions(); + +private: + enum partition_index_scan_type + { + PARTITION_INDEX_READ= 1, + PARTITION_INDEX_FIRST, + PARTITION_INDEX_FIRST_UNORDERED, + PARTITION_INDEX_LAST, + PARTITION_INDEX_READ_LAST, + PARTITION_READ_RANGE, + PARTITION_NO_INDEX_SCAN + }; + + /** handler to use (ha_partition, ha_innopart etc.) */ + handler *m_handler; + /** Convenience pointer to table from m_handler (i.e. m_handler->table). */ + TABLE *m_table; + + /* + Access methods to protected areas in handler to avoid adding + friend class Partition_helper in class handler. + */ + virtual THD *get_thd() const = 0; + virtual TABLE *get_table() const = 0; + virtual bool get_eq_range() const = 0; + virtual void set_eq_range(bool eq_range) = 0; + virtual void set_range_key_part(KEY_PART_INFO *key_part) = 0; + + /* + Implementation of per partition operation by instantiated engine. + These must be implemented in the 'real' partition_helper subclass. + */ + + /** + Write a row in the specified partition. + + @see handler::write_row(). + + @param part_id Partition to write to. + @param buf Buffer with data to write. + + @return Operation status. + @retval 0 Success. + @retval != 0 Error code. + */ + virtual int write_row_in_part(uint part_id, uchar *buf) = 0; + /** + Update a row in the specified partition. + + @see handler::update_row(). + + @param part_id Partition to update in. + @param old_data Buffer containing old row. + @param new_data Buffer containing new row. + + @return Operation status. + @retval 0 Success. + @retval != 0 Error code. + */ + virtual int update_row_in_part(uint new_part_id, + const uchar *old_data, + uchar *new_data) = 0; + /** + Delete an existing row in the specified partition. + + @see handler::delete_row(). + + @param part_id Partition to delete from. + @param buf Buffer containing row to delete. + + @return Operation status. + @retval 0 Success. + @retval != 0 Error code. + */ + virtual int delete_row_in_part(uint part_id, const uchar *buf) = 0; + /** + Initialize the shared auto increment value. + + @param no_lock If HA_STATUS_NO_LOCK should be used in info(HA_STATUS_AUTO). + + Also sets stats.auto_increment_value. + */ + virtual int initialize_auto_increment(bool no_lock) = 0; + /** Release auto_increment in all underlying partitions. */ + virtual void release_auto_increment_all_parts() {} + /** Save or persist the current max auto increment. */ + virtual void save_auto_increment(ulonglong nr) {} + /** + Per partition equivalent of rnd_* and index_* functions. + + @see class handler. + */ + virtual int rnd_init_in_part(uint part_id, bool table_scan) = 0; + int ph_rnd_next_in_part(uint part_id, uchar *buf); + virtual int rnd_next_in_part(uint part_id, uchar *buf) = 0; + virtual int rnd_end_in_part(uint part_id, bool scan) = 0; + virtual void position_in_last_part(uchar *ref, const uchar *row) = 0; + /* If ph_rnd_pos is used then this needs to be implemented! */ + virtual int rnd_pos_in_part(uint part_id, uchar *buf, uchar *pos) + { DBUG_ASSERT(0); return HA_ERR_WRONG_COMMAND; } + virtual int rnd_pos_by_record_in_last_part(uchar *row) + { + /* + Not much overhead to use default function. This avoids out-of-sync code. + */ + return m_handler->rnd_pos_by_record(row); + } + virtual int index_init_in_part(uint part, uint keynr, bool sorted) + { DBUG_ASSERT(0); return HA_ERR_WRONG_COMMAND; } + virtual int index_end_in_part(uint part) + { DBUG_ASSERT(0); return HA_ERR_WRONG_COMMAND; } + virtual int index_first_in_part(uint part, uchar *buf) = 0; + virtual int index_last_in_part(uint part, uchar *buf) = 0; + virtual int index_prev_in_part(uint part, uchar *buf) = 0; + virtual int index_next_in_part(uint part, uchar *buf) = 0; + virtual int index_next_same_in_part(uint part, + uchar *buf, + const uchar *key, + uint length) = 0; + virtual int index_read_map_in_part(uint part, + uchar *buf, + const uchar *key, + key_part_map keypart_map, + enum ha_rkey_function find_flag) = 0; + virtual int index_read_last_map_in_part(uint part, + uchar *buf, + const uchar *key, + key_part_map keypart_map) = 0; + /** + Do read_range_first in the specified partition. + If buf is set, then copy the result there instead of table->record[0]. + */ + virtual int read_range_first_in_part(uint part, + uchar *buf, + const key_range *start_key, + const key_range *end_key, + bool eq_range, + bool sorted) = 0; + /** + Do read_range_next in the specified partition. + If buf is set, then copy the result there instead of table->record[0]. + */ + virtual int read_range_next_in_part(uint part, uchar *buf) = 0; + virtual int index_read_idx_map_in_part(uint part, + uchar *buf, + uint index, + const uchar *key, + key_part_map keypart_map, + enum ha_rkey_function find_flag) = 0; + /** + Initialize engine specific resources for the record priority queue + used duing ordered index reads for multiple partitions. + + @param used_parts Number of partitions used in query + (number of set bits in m_part_info->read_partitions). + + @return Operation status. + @retval 0 Success. + @retval != 0 Error code. + */ + virtual int init_record_priority_queue_for_parts(uint used_parts) + { + return 0; + } + /** + Destroy and release engine specific resources used by the record + priority queue. + */ + virtual void destroy_record_priority_queue_for_parts() {} + /** + Checksum for a partition. + + @param part_id Partition to checksum. + */ + virtual ha_checksum checksum_in_part(uint part_id) const + { DBUG_ASSERT(0); return 0; } + /** + Copy a cached row. + + Used when copying a row from the record priority queue to the return buffer. + For some engines, like InnoDB, only marked columns must be copied, + to preserve non-read columns. + + @param[out] to_rec Buffer to copy to. + @param[in] from_rec Buffer to copy from. + */ + virtual void copy_cached_row(uchar *to_rec, const uchar *from_rec) + { memcpy(to_rec, from_rec, m_rec_length); } + /** + Prepare for creating new partitions during ALTER TABLE ... PARTITION. + @param num_partitions Number of new partitions to be created. + @param only_create True if only creating the partition + (no open/lock is needed). + + @return Operation status. + @retval 0 Success. + @retval != 0 Error code. + */ + virtual int prepare_for_new_partitions(uint num_partitions, + bool only_create) = 0; + /** + Create a new partition to be filled during ALTER TABLE ... PARTITION. + @param table Table to create the partition in. + @param create_info Table/partition specific create info. + @param part_name Partition name. + @param new_part_id Partition id in new table. + @param part_elem Partition element. + + @return Operation status. + @retval 0 Success. + @retval != 0 Error code. + */ + virtual int create_new_partition(TABLE *table, + HA_CREATE_INFO *create_info, + const char *part_name, + uint new_part_id, + partition_element *part_elem) = 0; + /** + Close and finalize new partitions. + */ + virtual void close_new_partitions() = 0; + /** + write row to new partition. + @param new_part New partition to write to. + + @return Operation status. + @retval 0 Success. + @retval != 0 Error code. + */ + virtual int write_row_in_new_part(uint new_part) = 0; + + /* Internal helper functions*/ + /** + Update auto increment value if current row contains a higher value. + */ + inline void set_auto_increment_if_higher(); + /** + Common routine to set up index scans. + + Find out which partitions we'll need to read when scanning the specified + range. + + If we need to scan only one partition, set m_ordered_scan_ongoing=FALSE + as we will not need to do merge ordering. + + @param buf Buffer to later return record in (this function + needs it to calculate partitioning function values) + + @param idx_read_flag True <=> m_start_key has range start endpoint which + probably can be used to determine the set of + partitions to scan. + False <=> there is no start endpoint. + + @return Operation status. + @retval 0 Success + @retval !=0 Error code + */ + int partition_scan_set_up(uchar *buf, bool idx_read_flag); + /** + Common routine to handle index_next with unordered results. + + These routines are used to scan partitions without considering order. + This is performed in two situations. + 1) In read_multi_range this is the normal case + 2) When performing any type of index_read, index_first, index_last where + all fields in the partition function is bound. In this case the index + scan is performed on only one partition and thus it isn't necessary to + perform any sort. + + @param[out] buf Read row in MySQL Row Format. + @param[in] next_same Called from index_next_same. + + @return Operation status. + @retval HA_ERR_END_OF_FILE End of scan + @retval 0 Success + @retval other Error code + */ + int handle_unordered_next(uchar *buf, bool is_next_same); + /** + Handle index_next when changing to new partition. + + This routine is used to start the index scan on the next partition. + Both initial start and after completing scan on one partition. + + @param[out] buf Read row in MySQL Row Format + + @return Operation status. + @retval HA_ERR_END_OF_FILE End of scan + @retval 0 Success + @retval other Error code + */ + int handle_unordered_scan_next_partition(uchar *buf); + /** + Common routine to start index scan with ordered results. + + @param[out] buf Read row in MySQL Row Format + + @return Operation status + @retval HA_ERR_END_OF_FILE End of scan + @retval HA_ERR_KEY_NOT_FOUND End of scan + @retval 0 Success + @retval other Error code + */ + int handle_ordered_index_scan(uchar *buf); + /** + Add index_next/prev results from partitions without exact match. + + If there where any partitions that returned HA_ERR_KEY_NOT_FOUND when + ha_index_read_map was done, those partitions must be included in the + following index_next/prev call. + + @return Operation status + @retval HA_ERR_END_OF_FILE End of scan + @retval 0 Success + @retval other Error code + */ + int handle_ordered_index_scan_key_not_found(); + /** + Common routine to handle index_prev with ordered results. + + @param[out] buf Read row in MySQL Row Format. + + @return Operation status. + @retval HA_ERR_END_OF_FILE End of scan + @retval 0 Success + @retval other Error code + */ + int handle_ordered_prev(uchar *buf); + /** + Common routine to handle index_next with ordered results. + + @param[out] buf Read row in MySQL Row Format. + @param[in] next_same Called from index_next_same. + + @return Operation status. + @retval HA_ERR_END_OF_FILE End of scan + @retval 0 Success + @retval other Error code + */ + int handle_ordered_next(uchar *buf, bool is_next_same); + /** + Common routine for a number of index_read variants. + + @param[out] buf Buffer where the record should be returned. + @param[in] have_start_key TRUE <=> the left endpoint is available, i.e. + we're in index_read call or in read_range_first + call and the range has left endpoint. + FALSE <=> there is no left endpoint (we're in + read_range_first() call and the range has no + left endpoint). + + @return Operation status + @retval 0 OK + @retval HA_ERR_END_OF_FILE Whole index scanned, without finding the record. + @retval HA_ERR_KEY_NOT_FOUND Record not found, but index cursor positioned. + @retval other Error code. + */ + int common_index_read(uchar *buf, bool have_start_key); + /** + Common routine for index_first/index_last. + + @param[out] buf Read row in MySQL Row Format. + + @return Operation status. + @retval 0 Success + @retval != 0 Error code + */ + int common_first_last(uchar *buf); + /** + Return the top record in sort order. + + @param[out] buf Row returned in MySQL Row Format. + */ + void return_top_record(uchar *buf); + /** + Copy partitions as part of ALTER TABLE of partitions. + + change_partitions has done all the preparations, now it is time to + actually copy the data from the reorganized partitions to the new + partitions. + + @param[out] copied Number of records copied. + @param[out] deleted Number of records deleted. + + @return Operation status + @retval 0 Success + @retval >0 Error code + */ + virtual int copy_partitions(ulonglong * const copied, + ulonglong * const deleted); + + /** + Set table->read_set taking partitioning expressions into account. + */ + void set_partition_read_set(); + + /* + These could be private as well, + but easier to expose them to derived classes to use. + */ +protected: + /** All internal partitioning data! @{ */ + /** Tables partitioning info (same as table->part_info) */ + partition_info *m_part_info; + /** Is primary key clustered. */ + bool m_pkey_is_clustered; + /** Cached value of m_part_info->is_sub_partitioned(). */ + bool m_is_sub_partitioned; + /** Partition share for auto_inc handling. */ + Partition_share *m_part_share; + /** Total number of partitions. */ + uint m_tot_parts; + uint m_last_part; // Last accessed partition. + const uchar *m_err_rec; // record which gave error. + bool m_auto_increment_safe_stmt_log_lock; + bool m_auto_increment_lock; + part_id_range m_part_spec; // Which parts to scan + uint m_scan_value; // Value passed in rnd_init + // call + key_range m_start_key; // index read key range + enum partition_index_scan_type m_index_scan_type;// What type of index + // scan + uint m_rec_length; // Local copy of record length + + bool m_ordered; // Ordered/Unordered index scan. + bool m_ordered_scan_ongoing; // Ordered index scan ongoing. + bool m_reverse_order; // Scanning in reverse order (prev). + /** Row and key buffer for ordered index scan. */ + uchar *m_ordered_rec_buffer; + /** Prio queue used by sorted read. */ + Prio_queue *m_queue; + /** Which partition is to deliver next result. */ + uint m_top_entry; + /** Offset in m_ordered_rec_buffer from part buffer to its record buffer. */ + uint m_rec_offset; + /** + Current index used for sorting. + If clustered PK exists, then it will be used as secondary index to + sort on if the first is equal in key_rec_cmp. + So if clustered pk: m_curr_key_info[0]= current index and + m_curr_key_info[1]= pk and [2]= NULL. + Otherwise [0]= current index, [1]= NULL, and we will + sort by rowid as secondary sort key if equal first key. + */ + KEY *m_curr_key_info[3]; + enum enum_using_ref { + /** handler::ref is not copied to the PQ. */ + REF_NOT_USED= 0, + /** + handler::ref is copied to the PQ but does not need to be used in sorting. + */ + REF_STORED_IN_PQ, + /** handler::ref is copied to the PQ and must be used during sorting. */ + REF_USED_FOR_SORT}; + /** How handler::ref is used in the priority queue. */ + enum_using_ref m_ref_usage; + /** Set if previous index_* call returned HA_ERR_KEY_NOT_FOUND. */ + bool m_key_not_found; + /** Partitions that returned HA_ERR_KEY_NOT_FOUND. */ + MY_BITMAP m_key_not_found_partitions; + /** @} */ +}; +#endif /* PARTITION_HANDLER_INCLUDED */ diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index b30240f64c3..0aeaa058cf9 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7523,8 +7523,17 @@ WARN_VERS_PARAMETERS WARN_VERS_PART_ROTATION eng "Switching from partition %`s to %`s" +WARN_VERS_TRX_MISSING + eng "VTQ missing transaction ID %lu" + +WARN_VERS_PART_NON_HISTORICAL + eng "Partition %`s contains non-historical data" + ER_VERS_NOT_ALLOWED eng "%`s is not allowed for versioned table" ER_VERS_WRONG_QUERY_TYPE eng "%`s works only with %`s query type" + +ER_WRONG_TABLESPACE_NAME 42000 + eng "Incorrect tablespace name `%-.192s`" diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index b358fe3386e..caca441e5e4 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -67,6 +67,7 @@ #include "opt_range.h" // store_key_image_to_rec #include "sql_alter.h" // Alter_table_ctx #include "sql_select.h" +#include "sql_tablespace.h" // check_tablespace_name #include using std::max; @@ -3458,7 +3459,10 @@ int vers_get_partition_id(partition_info *part_info, { table->s->busy_rotation= true; mysql_mutex_unlock(&table->s->LOCK_rotation); - if (part_info->vers_limit_exceed() || part_info->vers_interval_exceed(sys_trx_end->get_timestamp())) + // transaction is not yet pushed to VTQ, so we use now-time + my_time_t end_ts= sys_trx_end->table->versioned_by_engine() ? + my_time(0) : sys_trx_end->get_timestamp(); + if (part_info->vers_limit_exceed() || part_info->vers_interval_exceed(end_ts)) { part_info->vers_part_rotate(thd); } @@ -7388,6 +7392,39 @@ err: } #endif + +/* + Prepare for calling val_int on partition function by setting fields to + point to the record where the values of the PF-fields are stored. + + SYNOPSIS + set_field_ptr() + ptr Array of fields to change ptr + new_buf New record pointer + old_buf Old record pointer + + DESCRIPTION + Set ptr in field objects of field array to refer to new_buf record + instead of previously old_buf. Used before calling val_int and after + it is used to restore pointers to table->record[0]. + This routine is placed outside of partition code since it can be useful + also for other programs. +*/ + +void set_field_ptr(Field **ptr, const uchar *new_buf, + const uchar *old_buf) +{ + my_ptrdiff_t diff= (new_buf - old_buf); + DBUG_ENTER("set_field_ptr"); + + do + { + (*ptr)->move_field_offset(diff); + } while (*(++ptr)); + DBUG_VOID_RETURN; +} + + /* Prepare for calling val_int on partition function by setting fields to point to the record where the values of the PF-fields are stored. @@ -7426,6 +7463,61 @@ void set_key_field_ptr(KEY *key_info, const uchar *new_buf, } +/** + Append all fields in read_set to string + + @param[in,out] str String to append to. + @param[in] row Row to append. + @param[in] table Table containing read_set and fields for the row. +*/ +void append_row_to_str(String &str, const uchar *row, TABLE *table) +{ + Field **fields, **field_ptr; + const uchar *rec; + uint num_fields= bitmap_bits_set(table->read_set); + uint curr_field_index= 0; + bool is_rec0= !row || row == table->record[0]; + if (!row) + rec= table->record[0]; + else + rec= row; + + /* Create a new array of all read fields. */ + fields= (Field**) my_malloc(sizeof(void*) * (num_fields + 1), + MYF(0)); + if (!fields) + return; + fields[num_fields]= NULL; + for (field_ptr= table->field; + *field_ptr; + field_ptr++) + { + if (!bitmap_is_set(table->read_set, (*field_ptr)->field_index)) + continue; + fields[curr_field_index++]= *field_ptr; + } + + + if (!is_rec0) + set_field_ptr(fields, rec, table->record[0]); + + for (field_ptr= fields; + *field_ptr; + field_ptr++) + { + Field *field= *field_ptr; + str.append(" "); + str.append(field->field_name); + str.append(":"); + field_unpack(&str, field, rec, 0, false); + } + + if (!is_rec0) + set_field_ptr(fields, table->record[0], rec); + my_free(fields); +} + + /* SYNOPSIS mem_alloc_error() @@ -8595,4 +8687,52 @@ uint get_partition_field_store_length(Field *field) store_length+= HA_KEY_BLOB_LENGTH; return store_length; } + +// FIXME: duplicate of ha_partition::set_up_table_before_create +bool set_up_table_before_create(THD *thd, + TABLE_SHARE *share, + const char *partition_name_with_path, + HA_CREATE_INFO *info, + partition_element *part_elem) +{ + bool error= false; + const char *partition_name; + DBUG_ENTER("set_up_table_before_create"); + + DBUG_ASSERT(part_elem); + + if (!part_elem) + DBUG_RETURN(true); + share->max_rows= part_elem->part_max_rows; + share->min_rows= part_elem->part_min_rows; + partition_name= strrchr(partition_name_with_path, FN_LIBCHAR); + if ((part_elem->index_file_name && + (error= append_file_to_dir(thd, + const_cast(&part_elem->index_file_name), + partition_name+1))) || + (part_elem->data_file_name && + (error= append_file_to_dir(thd, + const_cast(&part_elem->data_file_name), + partition_name+1)))) + { + DBUG_RETURN(error); + } + if (part_elem->index_file_name != NULL) + { + info->index_file_name= part_elem->index_file_name; + } + if (part_elem->data_file_name != NULL) + { + info->data_file_name= part_elem->data_file_name; + } + if (part_elem->tablespace_name != NULL) + { + if (check_tablespace_name(part_elem->tablespace_name) != IDENT_NAME_OK) + { + DBUG_RETURN(true); + } + info->tablespace= part_elem->tablespace_name; + } + DBUG_RETURN(error); +} #endif diff --git a/sql/sql_partition.h b/sql/sql_partition.h index c2665a8366b..aef4a6ce5e1 100644 --- a/sql/sql_partition.h +++ b/sql/sql_partition.h @@ -40,6 +40,7 @@ typedef struct st_key_range key_range; #define HA_CAN_UPDATE_PARTITION_KEY (1 << 1) #define HA_CAN_PARTITION_UNIQUE (1 << 2) #define HA_USE_AUTO_PARTITION (1 << 3) +#define HA_ONLY_VERS_PARTITION (1 << 4) #define NORMAL_PART_NAME 0 #define TEMP_PART_NAME 1 @@ -127,6 +128,14 @@ uint32 get_partition_id_range_for_endpoint(partition_info *part_info, bool check_part_func_fields(Field **ptr, bool ok_with_charsets); bool field_is_partition_charset(Field *field); Item* convert_charset_partition_constant(Item *item, CHARSET_INFO *cs); +/** + Append all fields in read_set to string + + @param[in,out] str String to append to. + @param[in] row Row to append. + @param[in] table Table containing read_set and fields for the row. +*/ +void append_row_to_str(String &str, const uchar *row, TABLE *table); void mem_alloc_error(size_t size); void truncate_partition_filename(char *path); @@ -291,6 +300,31 @@ void create_subpartition_name(char *out, const char *in1, void set_key_field_ptr(KEY *key_info, const uchar *new_buf, const uchar *old_buf); +/** Set up table for creating a partition. +Copy info from partition to the table share so the created partition +has the correct info. + @param thd THD object + @param share Table share to be updated. + @param info Create info to be updated. + @param part_elem partition_element containing the info. + + @return status + @retval TRUE Error + @retval FALSE Success + + @details + Set up + 1) Comment on partition + 2) MAX_ROWS, MIN_ROWS on partition + 3) Index file name on partition + 4) Data file name on partition +*/ +bool set_up_table_before_create(THD *thd, + TABLE_SHARE *share, + const char *partition_name_with_path, + HA_CREATE_INFO *info, + partition_element *part_elem); + extern const LEX_STRING partition_keywords[]; #endif /* SQL_PARTITION_INCLUDED */ diff --git a/sql/sql_table.cc b/sql/sql_table.cc index b5cf35ed17c..3a921e0dc79 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -4492,7 +4492,10 @@ handler *mysql_create_frm_image(THD *thd, part_info->part_info_string= part_syntax_buf; part_info->part_info_len= syntax_len; if ((!(engine_type->partition_flags && - engine_type->partition_flags() & HA_CAN_PARTITION)) || + ((engine_type->partition_flags() & HA_CAN_PARTITION) || + (part_info->part_type == VERSIONING_PARTITION && + engine_type->partition_flags() & HA_ONLY_VERS_PARTITION)) + )) || create_info->db_type == partition_hton) { /* diff --git a/sql/sql_tablespace.cc b/sql/sql_tablespace.cc index 8b9e14e5a18..318be320640 100644 --- a/sql/sql_tablespace.cc +++ b/sql/sql_tablespace.cc @@ -22,6 +22,70 @@ #include "sql_table.h" // write_bin_log #include "sql_class.h" // THD +/** + Check if tablespace name is valid + + @param tablespace_name Name of the tablespace + + @note Tablespace names are not reflected in the file system, so + character case conversion or consideration is not relevant. + + @note Checking for path characters or ending space is not done. + The only checks are for identifier length, both in terms of + number of characters and number of bytes. + + @retval IDENT_NAME_OK Identifier name is ok (Success) + @retval IDENT_NAME_WRONG Identifier name is wrong, if length == 0 +* (ER_WRONG_TABLESPACE_NAME) + @retval IDENT_NAME_TOO_LONG Identifier name is too long if it is greater + than 64 characters (ER_TOO_LONG_IDENT) + + @note In case of IDENT_NAME_TOO_LONG or IDENT_NAME_WRONG, the function + reports an error (using my_error()). +*/ + +enum_ident_name_check check_tablespace_name(const char *tablespace_name) +{ + size_t name_length= 0; //< Length as number of bytes + size_t name_length_symbols= 0; //< Length as number of symbols + + // Name must be != NULL and length must be > 0 + if (!tablespace_name || (name_length= strlen(tablespace_name)) == 0) + { + my_error(ER_WRONG_TABLESPACE_NAME, MYF(0), tablespace_name); + return IDENT_NAME_WRONG; + } + + // If we do not have too many bytes, we must check the number of symbols, + // provided the system character set may use more than one byte per symbol. + if (name_length <= NAME_LEN && use_mb(system_charset_info)) + { + const char *name= tablespace_name; //< The actual tablespace name + const char *end= name + name_length; //< Pointer to first byte after name + + // Loop over all symbols as long as we don't have too many already + while (name != end && name_length_symbols <= NAME_CHAR_LEN) + { + int len= my_ismbchar(system_charset_info, name, end); + if (len) + name += len; + else + name++; + + name_length_symbols++; + } + } + + if (name_length_symbols > NAME_CHAR_LEN || name_length > NAME_LEN) + { + my_error(ER_TOO_LONG_IDENT, MYF(0), tablespace_name); + return IDENT_NAME_TOO_LONG; + } + + return IDENT_NAME_OK; +} + + int mysql_alter_tablespace(THD *thd, st_alter_tablespace *ts_info) { int error= HA_ADMIN_NOT_IMPLEMENTED; diff --git a/sql/sql_tablespace.h b/sql/sql_tablespace.h index ae77d15cbcb..b97c64f7965 100644 --- a/sql/sql_tablespace.h +++ b/sql/sql_tablespace.h @@ -19,6 +19,41 @@ class THD; class st_alter_tablespace; +/** + Enumerate possible status of a identifier name while determining + its validity +*/ +enum enum_ident_name_check +{ + IDENT_NAME_OK, + IDENT_NAME_WRONG, + IDENT_NAME_TOO_LONG +}; + +/** + Check if tablespace name is valid + + @param tablespace_name Name of the tablespace + + @note Tablespace names are not reflected in the file system, so + character case conversion or consideration is not relevant. + + @note Checking for path characters or ending space is not done. + The only checks are for identifier length, both in terms of + number of characters and number of bytes. + + @retval IDENT_NAME_OK Identifier name is ok (Success) + @retval IDENT_NAME_WRONG Identifier name is wrong, if length == 0 + (ER_WRONG_TABLESPACE_NAME) + @retval IDENT_NAME_TOO_LONG Identifier name is too long if it is greater + than 64 characters (ER_TOO_LONG_IDENT) + + @note In case of IDENT_NAME_TOO_LONG or IDENT_NAME_WRONG, the function + reports an error (using my_error()). +*/ + +enum_ident_name_check check_tablespace_name(const char *tablespace_name); + int mysql_alter_tablespace(THD* thd, st_alter_tablespace *ts_info); #endif /* SQL_TABLESPACE_INCLUDED */ diff --git a/sql/table.cc b/sql/table.cc index 354658ba476..b256b3e91b6 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -3261,6 +3261,20 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share, } outparam->part_info->is_auto_partitioned= share->auto_partitioned; DBUG_PRINT("info", ("autopartitioned: %u", share->auto_partitioned)); + if (outparam->part_info->part_type == VERSIONING_PARTITION && + share->db_type()->vers_upgrade_handler) + { + outparam->file= share->db_type()->vers_upgrade_handler( + outparam->file, &outparam->mem_root); + if (!outparam->file) + { + thd->stmt_arena= backup_stmt_arena_ptr; + thd->restore_active_arena(&part_func_arena, &backup_arena); + my_error(ER_OUTOFMEMORY, MYF(0), 4095); + error_reported= TRUE; + goto err; + } + } /* We should perform the fix_partition_func in either local or caller's arena depending on work_part_info_used value. -- cgit v1.2.1 From b8bfc06b26dc47ff42a671fdd7f62b75152f7570 Mon Sep 17 00:00:00 2001 From: kevg Date: Mon, 27 Feb 2017 16:31:37 +0300 Subject: SQL, Tests: temporal_current_timestamp for setting default AS OF timestamp [closes #117] --- sql/mysqld.cc | 3 +++ sql/mysqld.h | 1 + sql/sql_select.cc | 23 +++++++++++++++++++++++ sql/sys_vars.cc | 5 +++++ 4 files changed, 32 insertions(+) (limited to 'sql') diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 5196ec41c11..b7eccea80c6 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -781,6 +781,9 @@ char *relay_log_info_file, *report_user, *report_password, *report_host; char *opt_relay_logname = 0, *opt_relaylog_index_name=0; char *opt_logname, *opt_slow_logname, *opt_bin_logname; +/* System Versioning */ +char *temporal_current_timestamp; + /* Static variables */ static volatile sig_atomic_t kill_in_progress; diff --git a/sql/mysqld.h b/sql/mysqld.h index 46382396a5a..ece0992dd82 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -175,6 +175,7 @@ extern char *opt_backup_history_logname, *opt_backup_progress_logname, *opt_backup_settings_name; extern const char *log_output_str; extern const char *log_backup_output_str; +extern char *temporal_current_timestamp; extern char *mysql_home_ptr, *pidfile_name_ptr; extern MYSQL_PLUGIN_IMPORT char glob_hostname[FN_REFLEN]; extern char mysql_home[FN_REFLEN]; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 2ba2f2e7234..3d5f158df36 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -62,6 +62,7 @@ #include #include #include +#include "sys_vars_shared.h" /* A key part number that means we're using a fulltext scan. @@ -775,6 +776,28 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, DBUG_RETURN(-1); } + if (vers_conditions.type == FOR_SYSTEM_TIME_UNSPECIFIED) + { + const char var[]= "temporal_current_timestamp"; + sys_var *sv= intern_find_sys_var(var, sizeof(var) - 1); + DBUG_ASSERT(sv); + const char *data= *(char **)sv->option.value; + DBUG_ASSERT(data); + if (0 == strcmp(data, "all")) + { + vers_conditions.init(FOR_SYSTEM_TIME_ALL, UNIT_TIMESTAMP); + } + else if (0 != strcmp(data, "now")) + { + Item *ts= create_temporal_literal(thd, data, strlen(data), + system_charset_info, + MYSQL_TYPE_DATETIME, true); + if (!ts) + DBUG_RETURN(-1); + vers_conditions.init(FOR_SYSTEM_TIME_AS_OF, UNIT_TIMESTAMP, ts); + } + } + if (vers_conditions.type != FOR_SYSTEM_TIME_UNSPECIFIED) { switch (slex->lock_type) diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index da8fa18cb26..bc2b37d9d6d 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -382,6 +382,11 @@ static Sys_var_charptr Sys_basedir( READ_ONLY GLOBAL_VAR(mysql_home_ptr), CMD_LINE(REQUIRED_ARG, 'b'), IN_FS_CHARSET, DEFAULT(0)); +static Sys_var_charptr sys_temporal_current_timestamp( + "temporal_current_timestamp", "Default AS OF value for versioned tables", + GLOBAL_VAR(temporal_current_timestamp), CMD_LINE(REQUIRED_ARG, 'b'), + IN_FS_CHARSET, DEFAULT("now")); + static Sys_var_ulonglong Sys_binlog_cache_size( "binlog_cache_size", "The size of the transactional cache for " "updates to transactional engines for the binary log. " -- cgit v1.2.1 From 204b54d2d9f966f5fec0baef3974eff98ec125ad Mon Sep 17 00:00:00 2001 From: kevg Date: Sat, 4 Mar 2017 23:05:45 +0300 Subject: SQL: create versioned tmp table from query [fixes #144] --- sql/handler.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/handler.cc b/sql/handler.cc index ba947fd7a2d..f7620a0543b 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -6724,7 +6724,8 @@ bool Vers_parse_info::check_and_fix_implicit( generated_as_row.start || generated_as_row.end || period_for_system_time.start || period_for_system_time.end; - if (with == 0 && (not_set == 0 || !table_with_system_versioning)) + if (!thd->lex->tmp_table() && with == 0 && + (not_set == 0 || !table_with_system_versioning)) { my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, "versioned fields missing"); -- cgit v1.2.1 From 17745222a1a1dbc72ecbe5a9c0ca0daeacc675a8 Mon Sep 17 00:00:00 2001 From: kevg Date: Sun, 5 Mar 2017 23:51:02 +0300 Subject: SQL: incorrect check on specific JOIN query [fixes #145] --- sql/sql_base.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'sql') diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 02adbb656c1..e8ec8104f8b 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -6420,8 +6420,9 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, if (!my_strcasecmp(system_charset_info, field_name_1, cur_field_name_2)) { DBUG_PRINT ("info", ("match c1.is_common=%d", nj_col_1->is_common)); - if (cur_nj_col_2->is_common || - (found && (!using_fields || is_using_column_1))) + if ((!it_1.field() || !it_1.field()->vers_sys_field()) && + (cur_nj_col_2->is_common || + (found && (!using_fields || is_using_column_1)))) { my_error(ER_NON_UNIQ_ERROR, MYF(0), field_name_1, thd->where); goto err; -- cgit v1.2.1 From 9355e3e9661f80bc3a6813411c7d5f33f6fec60d Mon Sep 17 00:00:00 2001 From: kevg Date: Mon, 6 Mar 2017 15:53:53 +0300 Subject: SQL: CREATE TABLE LIKE for versioned tables [fixes #146] --- sql/handler.cc | 27 +++++++++++++++++++++++++++ sql/handler.h | 2 ++ sql/sql_parse.cc | 9 +++++---- sql/sql_table.cc | 7 +++++++ 4 files changed, 41 insertions(+), 4 deletions(-) (limited to 'sql') diff --git a/sql/handler.cc b/sql/handler.cc index f7620a0543b..2bbc52fb19f 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -6850,6 +6850,33 @@ bool Vers_parse_info::check_and_fix_alter(THD *thd, Alter_info *alter_info, check_generated_type(table_name, alter_info, integer_fields))); } +bool Vers_parse_info::fix_create_like(THD *thd, Alter_info *alter_info, + HA_CREATE_INFO *create_info) +{ + List_iterator it(alter_info->create_list); + Create_field *f= NULL; + + DBUG_ASSERT(alter_info->create_list.elements > 2); + for (uint i= 0; i < alter_info->create_list.elements - 1; ++i) + f= it++; + DBUG_ASSERT(f->flags & VERS_SYS_START_FLAG); + if (create_string(thd->mem_root, &generated_as_row.start, f->field_name) || + create_string(thd->mem_root, &period_for_system_time.start, + f->field_name)) + return true; + + f= it++; + DBUG_ASSERT(f->flags & VERS_SYS_END_FLAG); + if (create_string(thd->mem_root, &generated_as_row.end, f->field_name) || + create_string(thd->mem_root, &period_for_system_time.end, f->field_name)) + return true; + + create_info->options|= HA_VERSIONED_TABLE; + + return false; +} + + bool Vers_parse_info::check_with_conditions(const char *table_name) const { if (!generated_as_row.start) diff --git a/sql/handler.h b/sql/handler.h index f5e3d83d8d9..19a0b2963e4 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -1723,6 +1723,8 @@ public: bool integer_fields, const char *table_name); bool check_and_fix_alter(THD *thd, Alter_info *alter_info, HA_CREATE_INFO *create_info, TABLE_SHARE *share); + bool fix_create_like(THD *thd, Alter_info *alter_info, + HA_CREATE_INFO *create_info); /** User has added 'WITH SYSTEM VERSIONING' to table definition */ bool declared_with_system_versioning : 1; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 75e608a0377..d16211101fd 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3877,10 +3877,11 @@ mysql_execute_command(THD *thd) create_info.use_default_db_type(thd); DBUG_ASSERT(create_info.db_type); - if (create_info.vers_info.check_and_fix_implicit(thd, - &alter_info, - create_info.db_type->flags & HTON_SUPPORTS_SYS_VERSIONING, - create_table->table_name)) + if (!create_info.like() && + create_info.vers_info.check_and_fix_implicit( + thd, &alter_info, + create_info.db_type->flags & HTON_SUPPORTS_SYS_VERSIONING, + create_table->table_name)) { goto end_with_restore_list; } diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 3a921e0dc79..cb6b18eee4c 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -5378,6 +5378,13 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, thd->work_part_info= src_table->table->part_info->get_clone(thd); #endif + if (src_table->table->versioned() && + local_create_info.vers_info.fix_create_like(thd, &local_alter_info, + &local_create_info)) + { + goto err; + } + /* Adjust description of source table before using it for creation of target table. -- cgit v1.2.1 From 7a22dd37164c1277410319984411994fb098e85b Mon Sep 17 00:00:00 2001 From: kevg Date: Mon, 6 Mar 2017 20:07:10 +0300 Subject: SQL: INNER JOIN USING with versioned tables [fixes #147] --- sql/sql_base.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/sql_base.cc b/sql/sql_base.cc index e8ec8104f8b..b0fe02aba69 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -5350,7 +5350,7 @@ find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name, { if (!my_strcasecmp(system_charset_info, curr_nj_col->name(), name)) { - if (nj_col) + if (nj_col && !curr_nj_col->table_field->field->vers_sys_field()) { my_error(ER_NON_UNIQ_ERROR, MYF(0), name, thd->where); DBUG_RETURN(NULL); -- cgit v1.2.1 From cbb674282aab725acb87c73dc2fb4699eb8b52c2 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Thu, 9 Mar 2017 12:16:53 +0700 Subject: SQL: moved VTQ items to separate file --- sql/CMakeLists.txt | 1 + sql/item.h | 1 + sql/item_timefunc.cc | 257 ---------------------------------------------- sql/item_timefunc.h | 115 --------------------- sql/item_vers.cc | 280 +++++++++++++++++++++++++++++++++++++++++++++++++++ sql/item_vers.h | 140 ++++++++++++++++++++++++++ 6 files changed, 422 insertions(+), 372 deletions(-) create mode 100644 sql/item_vers.cc create mode 100644 sql/item_vers.h (limited to 'sql') diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 08a39b1975d..ee34891d0c9 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -143,6 +143,7 @@ SET (SQL_SOURCE rpl_gtid.cc rpl_parallel.cc sql_type.cc sql_type.h item_windowfunc.cc sql_window.cc + item_vers.cc sql_cte.cc sql_cte.h sql_sequence.cc sql_sequence.h ha_sequence.h ${WSREP_SOURCES} diff --git a/sql/item.h b/sql/item.h index f7bc1110898..eb695726472 100644 --- a/sql/item.h +++ b/sql/item.h @@ -4937,6 +4937,7 @@ public: #include "item_xmlfunc.h" #include "item_jsonfunc.h" #include "item_create.h" +#include "item_vers.h" #endif /** diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 2f9b467408f..a1a9ca5c8ae 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -3273,260 +3273,3 @@ bool Item_func_last_day::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) ltime->time_type= MYSQL_TIMESTAMP_DATE; return (null_value= 0); } - -Item_func_vtq_ts::Item_func_vtq_ts( - THD *thd, - Item* a, - vtq_field_t _vtq_field, - handlerton* hton) : - VTQ_common(thd, a, hton), - vtq_field(_vtq_field) -{ - decimals= 6; - null_value= true; - DBUG_ASSERT(arg_count == 1 && args[0]); -} - -Item_func_vtq_ts::Item_func_vtq_ts( - THD *thd, - Item* a, - vtq_field_t _vtq_field) : - VTQ_common(thd, a), - vtq_field(_vtq_field) -{ - decimals= 6; - null_value= true; - DBUG_ASSERT(arg_count == 1 && args[0]); -} - -template -void -VTQ_common::init_hton() -{ - if (!hton) - { - if (Item_func_X::args[0]->type() == Item::FIELD_ITEM) - { - Item_field *f= - static_cast(Item_func_X::args[0]); - DBUG_ASSERT( - f->field && - f->field->table && - f->field->table->s && - f->field->table->s->db_plugin); - hton= f->field->table->file->partition_ht(); - DBUG_ASSERT(hton); - } - else if (innodb_plugin) - { - hton= plugin_hton(plugin_int_to_ref(innodb_plugin)); - DBUG_ASSERT(hton); - } - if (hton && !(hton->flags & HTON_SUPPORTS_SYS_VERSIONING)) - { - my_error(ER_VERS_ENGINE_UNSUPPORTED, MYF(0), Item::name ? Item::name : this->func_name()); - hton= NULL; - } - } -} - -bool -Item_func_vtq_ts::get_date(MYSQL_TIME *res, ulonglong fuzzy_date) -{ - THD *thd= current_thd; // can it differ from constructor's? - DBUG_ASSERT(thd); - ulonglong trx_id= args[0]->val_uint(); - if (trx_id == ULONGLONG_MAX) - { - null_value= false; - thd->variables.time_zone->gmt_sec_to_TIME(res, TIMESTAMP_MAX_VALUE); - return false; - } - - init_hton(); - - if (!hton) - return true; - - DBUG_ASSERT(hton->vers_query_trx_id); - null_value= !hton->vers_query_trx_id(thd, res, trx_id, vtq_field); - - return false; -} - - -Item_func_vtq_id::Item_func_vtq_id( - THD *thd, - Item* a, - vtq_field_t _vtq_field, - bool _backwards) : - VTQ_common(thd, a), - vtq_field(_vtq_field), - backwards(_backwards) -{ - memset(&cached_result, 0, sizeof(cached_result)); - decimals= 0; - unsigned_flag= 1; - null_value= true; - DBUG_ASSERT(arg_count == 1 && args[0]); -} - -Item_func_vtq_id::Item_func_vtq_id( - THD *thd, - Item* a, - Item* b, - vtq_field_t _vtq_field) : - VTQ_common(thd, a, b), - vtq_field(_vtq_field), - backwards(false) -{ - memset(&cached_result, 0, sizeof(cached_result)); - decimals= 0; - unsigned_flag= 1; - null_value= true; - DBUG_ASSERT(arg_count == 2 && args[0] && args[1]); -} - -longlong -Item_func_vtq_id::get_by_trx_id(ulonglong trx_id) -{ - ulonglong res; - THD *thd= current_thd; // can it differ from constructor's? - DBUG_ASSERT(thd); - - if (trx_id == ULONGLONG_MAX) - { - null_value= true; - return 0; - } - - DBUG_ASSERT(hton->vers_query_trx_id); - null_value= !hton->vers_query_trx_id(thd, &res, trx_id, vtq_field); - return res; -} - -longlong -Item_func_vtq_id::get_by_commit_ts(MYSQL_TIME &commit_ts, bool backwards) -{ - THD *thd= current_thd; // can it differ from constructor's? - DBUG_ASSERT(thd); - - DBUG_ASSERT(hton->vers_query_commit_ts); - null_value= !hton->vers_query_commit_ts(thd, &cached_result, commit_ts, VTQ_ALL, backwards); - if (null_value) - { - return 0; - } - - switch (vtq_field) - { - case VTQ_COMMIT_ID: - return cached_result.commit_id; - case VTQ_ISO_LEVEL: - return cached_result.iso_level; - case VTQ_TRX_ID: - return cached_result.trx_id; - default: - DBUG_ASSERT(0); - null_value= true; - } - - return 0; -} - -longlong -Item_func_vtq_id::val_int() -{ - init_hton(); - - if (!hton) - { - null_value= true; - return 0; - } - - if (args[0]->is_null()) - { - if (arg_count < 2 || vtq_field == VTQ_TRX_ID) - { - null_value= true; - return 0; - } - return get_by_trx_id(args[1]->val_uint()); - } - else - { - MYSQL_TIME commit_ts; - if (args[0]->get_date(&commit_ts, 0)) - { - null_value= true; - return 0; - } - if (arg_count > 1) - { - backwards= args[1]->val_bool(); - DBUG_ASSERT(arg_count == 2); - } - return get_by_commit_ts(commit_ts, backwards); - } -} - -Item_func_vtq_trx_sees::Item_func_vtq_trx_sees( - THD *thd, - Item* a, - Item* b) : - VTQ_common(thd, a, b), - accept_eq(false) -{ - null_value= true; - DBUG_ASSERT(arg_count == 2 && args[0] && args[1]); -} - -longlong -Item_func_vtq_trx_sees::val_int() -{ - THD *thd= current_thd; - DBUG_ASSERT(thd); - - init_hton(); - - if (!hton) - { - null_value= true; - return 0; - } - - ulonglong trx_id1, trx_id0; - ulonglong commit_id1= 0; - ulonglong commit_id0= 0; - uchar iso_level1= 0; - - DBUG_ASSERT(arg_count > 1); - trx_id1= args[0]->val_uint(); - trx_id0= args[1]->val_uint(); - - vtq_record_t *cached= args[0]->vtq_cached_result(); - if (cached && cached->commit_id) - { - commit_id1= cached->commit_id; - iso_level1= cached->iso_level; - } - - cached= args[1]->vtq_cached_result(); - if (cached && cached->commit_id) - { - commit_id0= cached->commit_id; - } - - if (accept_eq && trx_id1 && trx_id1 == trx_id0) - { - null_value= false; - return true; - } - - DBUG_ASSERT(hton->vers_trx_sees); - bool result= false; - null_value= !hton->vers_trx_sees(thd, result, trx_id1, trx_id0, commit_id1, iso_level1, commit_id0); - return result; -} - diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index 4d7935b8506..f1a719fb775 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -1286,119 +1286,4 @@ public: { return get_item_copy(thd, mem_root, this); } }; -#include "vtq.h" - -template -class VTQ_common : public Item_func_X -{ -protected: - handlerton *hton; - void init_hton(); -public: - VTQ_common(THD *thd, Item* a) : - Item_func_X(thd, a), - hton(NULL) {} - VTQ_common(THD *thd, Item* a, Item* b) : - Item_func_X(thd, a, b), - hton(NULL) {} - VTQ_common(THD *thd, Item* a, handlerton* _hton) : - Item_func_X(thd, a), - hton(_hton) {} -}; - -class Item_func_vtq_ts : - public VTQ_common -{ - vtq_field_t vtq_field; -public: - Item_func_vtq_ts(THD *thd, Item* a, vtq_field_t _vtq_field, handlerton *hton); - Item_func_vtq_ts(THD *thd, Item* a, vtq_field_t _vtq_field); - const char *func_name() const - { - if (vtq_field == VTQ_BEGIN_TS) - { - return "vtq_begin_ts"; - } - return "vtq_commit_ts"; - } - bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy(thd, mem_root, this); } -}; - -class Item_func_vtq_id : - public VTQ_common -{ - vtq_field_t vtq_field; - vtq_record_t cached_result; - bool backwards; - - longlong get_by_trx_id(ulonglong trx_id); - longlong get_by_commit_ts(MYSQL_TIME &commit_ts, bool backwards); - -public: - Item_func_vtq_id(THD *thd, Item* a, vtq_field_t _vtq_field, bool _backwards= false); - Item_func_vtq_id(THD *thd, Item* a, Item* b, vtq_field_t _vtq_field); - - vtq_record_t *vtq_cached_result() { return &cached_result; } - - const char *func_name() const - { - switch (vtq_field) - { - case VTQ_TRX_ID: - return "vtq_trx_id"; - case VTQ_COMMIT_ID: - return "vtq_commit_id"; - case VTQ_ISO_LEVEL: - return "vtq_iso_level"; - default: - DBUG_ASSERT(0); - } - return NULL; - } - - void fix_length_and_dec() - { - Item_int_func::fix_length_and_dec(); - max_length= 20; - } - - longlong val_int(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy(thd, mem_root, this); } -}; - -class Item_func_vtq_trx_sees : - public VTQ_common -{ -protected: - bool accept_eq; - -public: - Item_func_vtq_trx_sees(THD *thd, Item* a, Item* b); - const char *func_name() const - { - return "vtq_trx_sees"; - } - longlong val_int(); - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy(thd, mem_root, this); } -}; - -class Item_func_vtq_trx_sees_eq : - public Item_func_vtq_trx_sees -{ -public: - Item_func_vtq_trx_sees_eq(THD *thd, Item* a, Item* b) : - Item_func_vtq_trx_sees(thd, a, b) - { - accept_eq= true; - } - const char *func_name() const - { - return "vtq_trx_sees_eq"; - } -}; - #endif /* ITEM_TIMEFUNC_INCLUDED */ diff --git a/sql/item_vers.cc b/sql/item_vers.cc new file mode 100644 index 00000000000..0a15d67b535 --- /dev/null +++ b/sql/item_vers.cc @@ -0,0 +1,280 @@ +/* Copyright (c) 2017, MariaDB Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ + + +/** + @brief + System Versioning items +*/ + +#include "item.h" +#include "sql_class.h" +#include "tztime.h" + +Item_func_vtq_ts::Item_func_vtq_ts( + THD *thd, + Item* a, + vtq_field_t _vtq_field, + handlerton* hton) : + VTQ_common(thd, a, hton), + vtq_field(_vtq_field) +{ + decimals= 6; + null_value= true; + DBUG_ASSERT(arg_count == 1 && args[0]); +} + +Item_func_vtq_ts::Item_func_vtq_ts( + THD *thd, + Item* a, + vtq_field_t _vtq_field) : + VTQ_common(thd, a), + vtq_field(_vtq_field) +{ + decimals= 6; + null_value= true; + DBUG_ASSERT(arg_count == 1 && args[0]); +} + +template +void +VTQ_common::init_hton() +{ + if (!hton) + { + if (Item_func_X::args[0]->type() == Item::FIELD_ITEM) + { + Item_field *f= + static_cast(Item_func_X::args[0]); + DBUG_ASSERT( + f->field && + f->field->table && + f->field->table->s && + f->field->table->s->db_plugin); + hton= f->field->table->file->partition_ht(); + DBUG_ASSERT(hton); + } + else if (innodb_plugin) + { + hton= plugin_hton(plugin_int_to_ref(innodb_plugin)); + DBUG_ASSERT(hton); + } + if (hton && !(hton->flags & HTON_SUPPORTS_SYS_VERSIONING)) + { + my_error(ER_VERS_ENGINE_UNSUPPORTED, MYF(0), Item::name ? Item::name : this->func_name()); + hton= NULL; + } + } +} + +bool +Item_func_vtq_ts::get_date(MYSQL_TIME *res, ulonglong fuzzy_date) +{ + THD *thd= current_thd; // can it differ from constructor's? + DBUG_ASSERT(thd); + ulonglong trx_id= args[0]->val_uint(); + if (trx_id == ULONGLONG_MAX) + { + null_value= false; + thd->variables.time_zone->gmt_sec_to_TIME(res, TIMESTAMP_MAX_VALUE); + return false; + } + + init_hton(); + + if (!hton) + return true; + + DBUG_ASSERT(hton->vers_query_trx_id); + null_value= !hton->vers_query_trx_id(thd, res, trx_id, vtq_field); + + return false; +} + + +Item_func_vtq_id::Item_func_vtq_id( + THD *thd, + Item* a, + vtq_field_t _vtq_field, + bool _backwards) : + VTQ_common(thd, a), + vtq_field(_vtq_field), + backwards(_backwards) +{ + memset(&cached_result, 0, sizeof(cached_result)); + decimals= 0; + unsigned_flag= 1; + null_value= true; + DBUG_ASSERT(arg_count == 1 && args[0]); +} + +Item_func_vtq_id::Item_func_vtq_id( + THD *thd, + Item* a, + Item* b, + vtq_field_t _vtq_field) : + VTQ_common(thd, a, b), + vtq_field(_vtq_field), + backwards(false) +{ + memset(&cached_result, 0, sizeof(cached_result)); + decimals= 0; + unsigned_flag= 1; + null_value= true; + DBUG_ASSERT(arg_count == 2 && args[0] && args[1]); +} + +longlong +Item_func_vtq_id::get_by_trx_id(ulonglong trx_id) +{ + ulonglong res; + THD *thd= current_thd; // can it differ from constructor's? + DBUG_ASSERT(thd); + + if (trx_id == ULONGLONG_MAX) + { + null_value= true; + return 0; + } + + DBUG_ASSERT(hton->vers_query_trx_id); + null_value= !hton->vers_query_trx_id(thd, &res, trx_id, vtq_field); + return res; +} + +longlong +Item_func_vtq_id::get_by_commit_ts(MYSQL_TIME &commit_ts, bool backwards) +{ + THD *thd= current_thd; // can it differ from constructor's? + DBUG_ASSERT(thd); + + DBUG_ASSERT(hton->vers_query_commit_ts); + null_value= !hton->vers_query_commit_ts(thd, &cached_result, commit_ts, VTQ_ALL, backwards); + if (null_value) + { + return 0; + } + + switch (vtq_field) + { + case VTQ_COMMIT_ID: + return cached_result.commit_id; + case VTQ_ISO_LEVEL: + return cached_result.iso_level; + case VTQ_TRX_ID: + return cached_result.trx_id; + default: + DBUG_ASSERT(0); + null_value= true; + } + + return 0; +} + +longlong +Item_func_vtq_id::val_int() +{ + init_hton(); + + if (!hton) + { + null_value= true; + return 0; + } + + if (args[0]->is_null()) + { + if (arg_count < 2 || vtq_field == VTQ_TRX_ID) + { + null_value= true; + return 0; + } + return get_by_trx_id(args[1]->val_uint()); + } + else + { + MYSQL_TIME commit_ts; + if (args[0]->get_date(&commit_ts, 0)) + { + null_value= true; + return 0; + } + if (arg_count > 1) + { + backwards= args[1]->val_bool(); + DBUG_ASSERT(arg_count == 2); + } + return get_by_commit_ts(commit_ts, backwards); + } +} + +Item_func_vtq_trx_sees::Item_func_vtq_trx_sees( + THD *thd, + Item* a, + Item* b) : + VTQ_common(thd, a, b), + accept_eq(false) +{ + null_value= true; + DBUG_ASSERT(arg_count == 2 && args[0] && args[1]); +} + +longlong +Item_func_vtq_trx_sees::val_int() +{ + THD *thd= current_thd; + DBUG_ASSERT(thd); + + init_hton(); + + if (!hton) + { + null_value= true; + return 0; + } + + ulonglong trx_id1, trx_id0; + ulonglong commit_id1= 0; + ulonglong commit_id0= 0; + uchar iso_level1= 0; + + DBUG_ASSERT(arg_count > 1); + trx_id1= args[0]->val_uint(); + trx_id0= args[1]->val_uint(); + + vtq_record_t *cached= args[0]->vtq_cached_result(); + if (cached && cached->commit_id) + { + commit_id1= cached->commit_id; + iso_level1= cached->iso_level; + } + + cached= args[1]->vtq_cached_result(); + if (cached && cached->commit_id) + { + commit_id0= cached->commit_id; + } + + if (accept_eq && trx_id1 && trx_id1 == trx_id0) + { + null_value= false; + return true; + } + + DBUG_ASSERT(hton->vers_trx_sees); + bool result= false; + null_value= !hton->vers_trx_sees(thd, result, trx_id1, trx_id0, commit_id1, iso_level1, commit_id0); + return result; +} diff --git a/sql/item_vers.h b/sql/item_vers.h new file mode 100644 index 00000000000..aa5575ff9b1 --- /dev/null +++ b/sql/item_vers.h @@ -0,0 +1,140 @@ +#ifndef ITEM_VERS_INCLUDED +#define ITEM_VERS_INCLUDED +/* Copyright (c) 2017, MariaDB Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ + + +/* System Versioning items */ + +#include "vtq.h" + +#ifdef USE_PRAGMA_INTERFACE +#pragma interface /* gcc class implementation */ +#endif + +template +class VTQ_common : public Item_func_X +{ +protected: + handlerton *hton; + void init_hton(); +public: + VTQ_common(THD *thd, Item* a) : + Item_func_X(thd, a), + hton(NULL) {} + VTQ_common(THD *thd, Item* a, Item* b) : + Item_func_X(thd, a, b), + hton(NULL) {} + VTQ_common(THD *thd, Item* a, handlerton* _hton) : + Item_func_X(thd, a), + hton(_hton) {} +}; + +class Item_func_vtq_ts : + public VTQ_common +{ + vtq_field_t vtq_field; +public: + Item_func_vtq_ts(THD *thd, Item* a, vtq_field_t _vtq_field, handlerton *hton); + Item_func_vtq_ts(THD *thd, Item* a, vtq_field_t _vtq_field); + const char *func_name() const + { + if (vtq_field == VTQ_BEGIN_TS) + { + return "vtq_begin_ts"; + } + return "vtq_commit_ts"; + } + bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy(thd, mem_root, this); } +}; + +class Item_func_vtq_id : + public VTQ_common +{ + vtq_field_t vtq_field; + vtq_record_t cached_result; + bool backwards; + + longlong get_by_trx_id(ulonglong trx_id); + longlong get_by_commit_ts(MYSQL_TIME &commit_ts, bool backwards); + +public: + Item_func_vtq_id(THD *thd, Item* a, vtq_field_t _vtq_field, bool _backwards= false); + Item_func_vtq_id(THD *thd, Item* a, Item* b, vtq_field_t _vtq_field); + + vtq_record_t *vtq_cached_result() { return &cached_result; } + + const char *func_name() const + { + switch (vtq_field) + { + case VTQ_TRX_ID: + return "vtq_trx_id"; + case VTQ_COMMIT_ID: + return "vtq_commit_id"; + case VTQ_ISO_LEVEL: + return "vtq_iso_level"; + default: + DBUG_ASSERT(0); + } + return NULL; + } + + void fix_length_and_dec() + { + Item_int_func::fix_length_and_dec(); + max_length= 20; + } + + longlong val_int(); + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy(thd, mem_root, this); } +}; + +class Item_func_vtq_trx_sees : + public VTQ_common +{ +protected: + bool accept_eq; + +public: + Item_func_vtq_trx_sees(THD *thd, Item* a, Item* b); + const char *func_name() const + { + return "vtq_trx_sees"; + } + longlong val_int(); + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy(thd, mem_root, this); } +}; + +class Item_func_vtq_trx_sees_eq : + public Item_func_vtq_trx_sees +{ +public: + Item_func_vtq_trx_sees_eq(THD *thd, Item* a, Item* b) : + Item_func_vtq_trx_sees(thd, a, b) + { + accept_eq= true; + } + const char *func_name() const + { + return "vtq_trx_sees_eq"; + } +}; + +#endif /* ITEM_VERS_INCLUDED */ -- cgit v1.2.1 From 0e0103893691eae714e2f278498300b8d005cebf Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Fri, 10 Mar 2017 20:25:30 +0700 Subject: SQL: forced, hidden versioning [closes #32] --- sql/handler.cc | 10 +++++++++- sql/handler.h | 3 ++- sql/mysqld.cc | 2 ++ sql/mysqld.h | 2 ++ sql/sql_parse.cc | 5 +---- sql/sql_show.cc | 8 ++++++-- sql/sys_vars.cc | 8 ++++++++ 7 files changed, 30 insertions(+), 8 deletions(-) (limited to 'sql') diff --git a/sql/handler.cc b/sql/handler.cc index 2bbc52fb19f..909e0aa6fee 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -6669,9 +6669,17 @@ bool Vers_parse_info::fix_implicit(THD *thd, Alter_info *alter_info, bool Vers_parse_info::check_and_fix_implicit( THD *thd, Alter_info *alter_info, - bool integer_fields, + HA_CREATE_INFO *create_info, const char* table_name) { + bool integer_fields= + create_info->db_type->flags & HTON_SUPPORTS_SYS_VERSIONING; + + if (vers_force) { + declared_with_system_versioning= true; + create_info->options|= HA_VERSIONED_TABLE; + } + if (!need_to_check()) return false; diff --git a/sql/handler.h b/sql/handler.h index 19a0b2963e4..a0da6852da4 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -1720,7 +1720,8 @@ private: public: bool check_and_fix_implicit(THD *thd, Alter_info *alter_info, - bool integer_fields, const char *table_name); + HA_CREATE_INFO *create_info, + const char *table_name); bool check_and_fix_alter(THD *thd, Alter_info *alter_info, HA_CREATE_INFO *create_info, TABLE_SHARE *share); bool fix_create_like(THD *thd, Alter_info *alter_info, diff --git a/sql/mysqld.cc b/sql/mysqld.cc index b7eccea80c6..bb96893e49d 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -783,6 +783,8 @@ char *opt_logname, *opt_slow_logname, *opt_bin_logname; /* System Versioning */ char *temporal_current_timestamp; +my_bool vers_force= false; +my_bool vers_hide= false; /* Static variables */ diff --git a/sql/mysqld.h b/sql/mysqld.h index ece0992dd82..20300895fe5 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -176,6 +176,8 @@ extern char *opt_backup_history_logname, *opt_backup_progress_logname, extern const char *log_output_str; extern const char *log_backup_output_str; extern char *temporal_current_timestamp; +extern my_bool vers_force; +extern my_bool vers_hide; extern char *mysql_home_ptr, *pidfile_name_ptr; extern MYSQL_PLUGIN_IMPORT char glob_hostname[FN_REFLEN]; extern char mysql_home[FN_REFLEN]; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index d16211101fd..677dd269722 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3876,12 +3876,9 @@ mysql_execute_command(THD *thd) if (!(create_info.used_fields & HA_CREATE_USED_ENGINE)) create_info.use_default_db_type(thd); - DBUG_ASSERT(create_info.db_type); if (!create_info.like() && create_info.vers_info.check_and_fix_implicit( - thd, &alter_info, - create_info.db_type->flags & HTON_SUPPORTS_SYS_VERSIONING, - create_table->table_name)) + thd, &alter_info, &create_info, create_table->table_name)) { goto end_with_restore_list; } diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 93836c82085..0f1098dcede 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2064,6 +2064,10 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, { uint flags = field->flags; + if (vers_hide && + (flags & (VERS_SYS_START_FLAG | VERS_SYS_END_FLAG))) + continue; + if (ptr != table->field) packet->append(STRING_WITH_LEN(",\n")); @@ -2229,7 +2233,7 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, hton->index_options); } - if (table->versioned()) + if (table->versioned() && !vers_hide) { const Field *fs = table->vers_start_field(); const Field *fe = table->vers_end_field(); @@ -2278,7 +2282,7 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, add_table_options(thd, table, create_info_arg, table_list->schema_table != 0, 0, packet); - if (table->versioned()) + if (table->versioned() && !vers_hide) { packet->append(STRING_WITH_LEN(" WITH SYSTEM VERSIONING")); } diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index bc2b37d9d6d..b6f84e88d07 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -387,6 +387,14 @@ static Sys_var_charptr sys_temporal_current_timestamp( GLOBAL_VAR(temporal_current_timestamp), CMD_LINE(REQUIRED_ARG, 'b'), IN_FS_CHARSET, DEFAULT("now")); +static Sys_var_mybool Sys_vers_force( + "vers_force", "Force system versioning for all created tables", + GLOBAL_VAR(vers_force), CMD_LINE(OPT_ARG), DEFAULT(FALSE)); + +static Sys_var_mybool Sys_vers_hide( + "vers_hide", "Hide system versioning from being displayed in table info", + GLOBAL_VAR(vers_hide), CMD_LINE(OPT_ARG), DEFAULT(FALSE)); + static Sys_var_ulonglong Sys_binlog_cache_size( "binlog_cache_size", "The size of the transactional cache for " "updates to transactional engines for the binary log. " -- cgit v1.2.1 From d85e7a5e016819447578646cd0d7cab0d8f2260b Mon Sep 17 00:00:00 2001 From: kevg Date: Fri, 10 Mar 2017 16:55:14 +0300 Subject: SQL: NATUAL JOIN on view + table [fixes #148] --- sql/sql_select.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 3d5f158df36..a75a33e1405 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -832,7 +832,8 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, Item *row_start= NULL; Item *row_end= NULL; - if (table->is_derived() && !table->is_recursive_with_table()) + if ((table->is_derived() && !table->is_recursive_with_table()) || + table->join_columns) { row_start= newx Item_field(thd, &slex->context, NULL, NULL, fstart->field_name); -- cgit v1.2.1 From 4782b74023083b9d5956c76c7c444f1a3f6a3419 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Sat, 11 Mar 2017 13:09:07 +0700 Subject: Misc: warnings fix related to b0419561a3475ff3464316c7104455685819acca --- sql/item_vers.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/item_vers.cc b/sql/item_vers.cc index 0a15d67b535..81cc3808651 100644 --- a/sql/item_vers.cc +++ b/sql/item_vers.cc @@ -19,9 +19,9 @@ System Versioning items */ -#include "item.h" #include "sql_class.h" #include "tztime.h" +#include "item.h" Item_func_vtq_ts::Item_func_vtq_ts( THD *thd, -- cgit v1.2.1 From a37cf5258b69c88d555fee73d3df0978aab226d3 Mon Sep 17 00:00:00 2001 From: kevg Date: Mon, 13 Mar 2017 14:29:21 +0300 Subject: SQL: CREATE VIEW with view_list from versioned table [fixes #151] --- sql/sql_view.cc | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) (limited to 'sql') diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 8ee4a75e259..215d81f5724 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -463,12 +463,25 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, TABLE_SHARE *s= tl->table->s; if (s->versioned) { + const char *start= s->vers_start_field()->field_name; + const char *end = s->vers_end_field()->field_name; + select_lex->item_list.push_back(new (thd->mem_root) Item_field( - thd, &select_lex->context, NULL, NULL, - s->vers_start_field()->field_name)); + thd, &select_lex->context, NULL, NULL, start)); select_lex->item_list.push_back(new (thd->mem_root) Item_field( - thd, &select_lex->context, NULL, NULL, - s->vers_end_field()->field_name)); + thd, &select_lex->context, NULL, NULL, end)); + + if (lex->view_list.elements) + { + if (LEX_STRING *s= thd->make_lex_string(start, strlen(start))) + lex->view_list.push_back(s); + else + goto err; + if (LEX_STRING *s= thd->make_lex_string(end, strlen(end))) + lex->view_list.push_back(s); + else + goto err; + } } } } -- cgit v1.2.1 From 9ea02899f85162039d6b2a200217fc4eec38b777 Mon Sep 17 00:00:00 2001 From: kevg Date: Mon, 13 Mar 2017 20:14:24 +0300 Subject: SQL: nested equi-join for versioned table [fixes #150] --- sql/sql_select.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'sql') diff --git a/sql/sql_select.cc b/sql/sql_select.cc index a75a33e1405..f9e67a8caaa 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -835,10 +835,10 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, if ((table->is_derived() && !table->is_recursive_with_table()) || table->join_columns) { - row_start= newx Item_field(thd, &slex->context, NULL, NULL, + row_start= newx Item_field(thd, &slex->context, table->db, table->alias, fstart->field_name); - row_end= - newx Item_field(thd, &slex->context, NULL, NULL, fend->field_name); + row_end= newx Item_field(thd, &slex->context, table->db, table->alias, + fend->field_name); } else { -- cgit v1.2.1 From 92c7a87119d1a212d0028dd05a834f458cc9e6f8 Mon Sep 17 00:00:00 2001 From: kevg Date: Tue, 14 Mar 2017 10:09:44 +0300 Subject: Misc: vers_setup_select() Item_field ctor fix Related to #150 --- sql/sql_select.cc | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) (limited to 'sql') diff --git a/sql/sql_select.cc b/sql/sql_select.cc index f9e67a8caaa..1421a41a9f4 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -827,24 +827,13 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, dst_cond= &table->on_expr; } - Field *fstart= table->table->vers_start_field(); - Field *fend= table->table->vers_end_field(); + const char *fstart= table->table->vers_start_field()->field_name; + const char *fend= table->table->vers_end_field()->field_name; - Item *row_start= NULL; - Item *row_end= NULL; - if ((table->is_derived() && !table->is_recursive_with_table()) || - table->join_columns) - { - row_start= newx Item_field(thd, &slex->context, table->db, table->alias, - fstart->field_name); - row_end= newx Item_field(thd, &slex->context, table->db, table->alias, - fend->field_name); - } - else - { - row_start= newx Item_field(thd, &slex->context, fstart); - row_end= newx Item_field(thd, &slex->context, fend); - } + Item *row_start= + newx Item_field(thd, &slex->context, table->db, table->alias, fstart); + Item *row_end= + newx Item_field(thd, &slex->context, table->db, table->alias, fend); Item *row_end2= row_end; if (table->table->versioned_by_sql()) -- cgit v1.2.1 From 4ebf680c9bb7fa42656a87024e9d464226b089d4 Mon Sep 17 00:00:00 2001 From: kevg Date: Tue, 14 Mar 2017 13:05:39 +0300 Subject: SQL: VIEW over a JOIN of versioned tables [fixes #153] --- sql/sql_view.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sql') diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 215d81f5724..d542a5710f6 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -467,9 +467,9 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, const char *end = s->vers_end_field()->field_name; select_lex->item_list.push_back(new (thd->mem_root) Item_field( - thd, &select_lex->context, NULL, NULL, start)); + thd, &select_lex->context, tables->db, tables->alias, start)); select_lex->item_list.push_back(new (thd->mem_root) Item_field( - thd, &select_lex->context, NULL, NULL, end)); + thd, &select_lex->context, tables->db, tables->alias, end)); if (lex->view_list.elements) { -- cgit v1.2.1 From fb0b3e590288564317a8ba048914a3a912e7627c Mon Sep 17 00:00:00 2001 From: kevg Date: Wed, 15 Mar 2017 16:32:44 +0300 Subject: SQL: NATURAL LEFT JOIN for versioned tables [fixes #156] --- sql/sql_base.cc | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'sql') diff --git a/sql/sql_base.cc b/sql/sql_base.cc index b0fe02aba69..14bc741057b 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -5350,7 +5350,7 @@ find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name, { if (!my_strcasecmp(system_charset_info, curr_nj_col->name(), name)) { - if (nj_col && !curr_nj_col->table_field->field->vers_sys_field()) + if (nj_col) { my_error(ER_NON_UNIQ_ERROR, MYF(0), name, thd->where); DBUG_RETURN(NULL); @@ -6379,6 +6379,10 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, bool is_using_column_1; if (!(nj_col_1= it_1.get_or_create_column_ref(thd, leaf_1))) goto err; + + if (nj_col_1->field() && nj_col_1->field()->vers_sys_field()) + continue; + field_name_1= nj_col_1->name(); is_using_column_1= using_fields && test_if_string_in_list(field_name_1, using_fields); @@ -6420,9 +6424,8 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, if (!my_strcasecmp(system_charset_info, field_name_1, cur_field_name_2)) { DBUG_PRINT ("info", ("match c1.is_common=%d", nj_col_1->is_common)); - if ((!it_1.field() || !it_1.field()->vers_sys_field()) && - (cur_nj_col_2->is_common || - (found && (!using_fields || is_using_column_1)))) + if (cur_nj_col_2->is_common || + (found && (!using_fields || is_using_column_1))) { my_error(ER_NON_UNIQ_ERROR, MYF(0), field_name_1, thd->where); goto err; -- cgit v1.2.1 From 352d83569b29725ded23202844079398b569fa46 Mon Sep 17 00:00:00 2001 From: kevg Date: Thu, 16 Mar 2017 18:35:36 +0300 Subject: SQL: versioning for tmp HEAP tables created from IB tables [closes #158] --- sql/sql_select.cc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 1421a41a9f4..79bbe9c90d6 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -864,7 +864,14 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, } Item *cond1= 0, *cond2= 0, *curr= 0; - if (table->table->versioned_by_sql() || vers_simple_select) + // Temporary tables of type HEAP can be created from INNODB tables and + // thus will have uint64 type of sys_trx_(start|end) field. + // They need special handling. + TABLE *t= table->table; + if ((t->s->table_category == TABLE_CATEGORY_TEMPORARY + ? t->vers_start_field()->type() != MYSQL_TYPE_LONGLONG + : t->versioned_by_sql()) || + vers_simple_select) { switch (vers_conditions.type) { -- cgit v1.2.1 From 21e8b22f531face05c2007aa6d5a6ae6907c62e3 Mon Sep 17 00:00:00 2001 From: kevg Date: Tue, 21 Mar 2017 13:33:26 +0300 Subject: Misc: vers_select_conds_t::init_from_sysvar() --- sql/sql_select.cc | 43 +++++++++++++++++++++++++------------------ sql/table.h | 2 ++ 2 files changed, 27 insertions(+), 18 deletions(-) (limited to 'sql') diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 79bbe9c90d6..c91895ce056 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -668,6 +668,29 @@ setup_without_group(THD *thd, Ref_ptr_array ref_pointer_array, DBUG_RETURN(res); } +bool vers_select_conds_t::init_from_sysvar(THD *thd) +{ + const char var[]= "temporal_current_timestamp"; + sys_var *sv= intern_find_sys_var(var, sizeof(var) - 1); + DBUG_ASSERT(sv); + const char *data= *(char **)sv->option.value; + DBUG_ASSERT(data); + if (0 == strcmp(data, "all")) + { + init(FOR_SYSTEM_TIME_ALL, UNIT_TIMESTAMP); + } + else if (0 != strcmp(data, "now")) + { + Item *ts= + create_temporal_literal(thd, data, strlen(data), system_charset_info, + MYSQL_TYPE_DATETIME, true); + if (!ts) + return true; + init(FOR_SYSTEM_TIME_AS_OF, UNIT_TIMESTAMP, ts); + } + return false; +} + int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, SELECT_LEX *slex) { @@ -778,24 +801,8 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, if (vers_conditions.type == FOR_SYSTEM_TIME_UNSPECIFIED) { - const char var[]= "temporal_current_timestamp"; - sys_var *sv= intern_find_sys_var(var, sizeof(var) - 1); - DBUG_ASSERT(sv); - const char *data= *(char **)sv->option.value; - DBUG_ASSERT(data); - if (0 == strcmp(data, "all")) - { - vers_conditions.init(FOR_SYSTEM_TIME_ALL, UNIT_TIMESTAMP); - } - else if (0 != strcmp(data, "now")) - { - Item *ts= create_temporal_literal(thd, data, strlen(data), - system_charset_info, - MYSQL_TYPE_DATETIME, true); - if (!ts) - DBUG_RETURN(-1); - vers_conditions.init(FOR_SYSTEM_TIME_AS_OF, UNIT_TIMESTAMP, ts); - } + if (vers_conditions.init_from_sysvar(thd)) + DBUG_RETURN(-1); } if (vers_conditions.type != FOR_SYSTEM_TIME_UNSPECIFIED) diff --git a/sql/table.h b/sql/table.h index 12e5830089d..ba497f1c365 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1882,6 +1882,8 @@ struct vers_select_conds_t start= s; end= e; } + + bool init_from_sysvar(THD *thd); }; struct LEX; -- cgit v1.2.1 From e8ae9f1ae9e361c41148f32f9efbae49ccadff19 Mon Sep 17 00:00:00 2001 From: kevg Date: Thu, 23 Mar 2017 16:14:19 +0300 Subject: SQL: VIEW NATURAL JOIN TABLE [fixes #161] --- sql/sql_base.cc | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'sql') diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 14bc741057b..2ca2c9141f3 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -6383,6 +6383,15 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, if (nj_col_1->field() && nj_col_1->field()->vers_sys_field()) continue; + if (table_ref_1->is_view() && table_ref_1->table->versioned()) + { + Item *item= nj_col_1->view_field->item; + DBUG_ASSERT(item->type() == Item::FIELD_ITEM); + Item_field *item_field= (Item_field *)item; + if (item_field->field->vers_sys_field()) + continue; + } + field_name_1= nj_col_1->name(); is_using_column_1= using_fields && test_if_string_in_list(field_name_1, using_fields); -- cgit v1.2.1 From 14f007f907355baf1cb0d4a63bd3ab4f1e01398d Mon Sep 17 00:00:00 2001 From: kevg Date: Thu, 23 Mar 2017 23:19:22 +0300 Subject: SQL: versioning in embedded JOINs [fixes #160] --- sql/sql_select.cc | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'sql') diff --git a/sql/sql_select.cc b/sql/sql_select.cc index c91895ce056..7658bb9e2d0 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -834,6 +834,12 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, dst_cond= &table->on_expr; } + if (TABLE_LIST *t= table->embedding) + { + if (t->on_expr) + dst_cond= &t->on_expr; + } + const char *fstart= table->table->vers_start_field()->field_name; const char *fend= table->table->vers_end_field()->field_name; -- cgit v1.2.1 From 7a525e7e93c651352ef869325b2c2337d4806125 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Fri, 24 Mar 2017 14:53:20 +0300 Subject: Parser: no implicit NOT NULL for system fields [fixes #163] --- sql/field.cc | 2 ++ sql/field.h | 4 +++- sql/sql_yacc.yy | 20 ++++++++++++++++++-- 3 files changed, 23 insertions(+), 3 deletions(-) (limited to 'sql') diff --git a/sql/field.cc b/sql/field.cc index 3c34d973bf5..3a1fee59937 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -10630,6 +10630,8 @@ Column_definition::Column_definition(THD *thd, Field *old_field, check_constraint= orig_field ? orig_field->check_constraint : 0; option_list= old_field->option_list; pack_flag= 0; + versioning= VERSIONING_NOT_SET; + implicit_not_null= false; switch (sql_type) { case MYSQL_TYPE_BLOB: diff --git a/sql/field.h b/sql/field.h index e955323b917..317ae018066 100644 --- a/sql/field.h +++ b/sql/field.h @@ -3871,6 +3871,7 @@ public: *check_constraint; // Check constraint enum_column_versioning versioning; + bool implicit_not_null; Column_definition(): comment(null_lex_str), @@ -3880,7 +3881,8 @@ public: srid(0), geom_type(Field::GEOM_GEOMETRY), option_list(NULL), pack_flag(0), vcol_info(0), default_value(0), check_constraint(0), - versioning(VERSIONING_NOT_SET) + versioning(VERSIONING_NOT_SET), + implicit_not_null(false) { interval_list.empty(); } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 7a17b90a191..5eab752df3f 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -6305,6 +6305,11 @@ field_def: my_yyabort_error((ER_VERS_WRONG_PARAMS, MYF(0), table_name, err)); } *p= field_name; + if (lex->last_field->implicit_not_null) + { + lex->last_field->flags&= ~NOT_NULL_FLAG; + lex->last_field->implicit_not_null= false; + } } ; @@ -6517,7 +6522,10 @@ field_type: Unless --explicit-defaults-for-timestamp is given. */ if (!opt_explicit_defaults_for_timestamp) + { Lex->last_field->flags|= NOT_NULL_FLAG; + Lex->last_field->implicit_not_null= true; + } $$.set(opt_mysql56_temporal_format ? MYSQL_TYPE_TIMESTAMP2 : MYSQL_TYPE_TIMESTAMP, $2); } @@ -6704,7 +6712,11 @@ opt_attribute_list: ; attribute: - NULL_SYM { Lex->last_field->flags&= ~ NOT_NULL_FLAG; } + NULL_SYM + { + Lex->last_field->flags&= ~ NOT_NULL_FLAG; + Lex->last_field->implicit_not_null= false; + } | DEFAULT column_default_expr { Lex->last_field->default_value= $2; } | ON UPDATE_SYM NOW_SYM opt_default_time_precision { @@ -6731,7 +6743,11 @@ attribute: ; serial_attribute: - not NULL_SYM { Lex->last_field->flags|= NOT_NULL_FLAG; } + not NULL_SYM + { + Lex->last_field->flags|= NOT_NULL_FLAG; + Lex->last_field->implicit_not_null= false; + } | opt_primary KEY_SYM { LEX *lex=Lex; -- cgit v1.2.1 From 67cd92b6f47b6f229fbdf62812ff805f38a03750 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Fri, 24 Mar 2017 16:00:42 +0300 Subject: SQL, IB: Copy history via CREATE .. SELECT [closes #157, #152] --- sql/handler.cc | 102 ++++++++++++++++++++++++++++++++++++++++++++++-------- sql/handler.h | 5 ++- sql/mysqld.cc | 2 +- sql/mysqld.h | 8 ++++- sql/sql_base.cc | 21 +++++++++-- sql/sql_insert.cc | 6 ++++ sql/sql_parse.cc | 12 +++---- sql/sql_select.cc | 7 +++- sql/sql_show.cc | 6 ++-- sql/sys_vars.cc | 11 ++++-- sql/table.cc | 4 +-- 11 files changed, 149 insertions(+), 35 deletions(-) (limited to 'sql') diff --git a/sql/handler.cc b/sql/handler.cc index 909e0aa6fee..62091b0f19e 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -6599,6 +6599,14 @@ bool Vers_parse_info::is_trx_end(const char *name) const DBUG_ASSERT(name); return generated_as_row.end && !strcmp(generated_as_row.end->c_ptr(), name); } +bool Vers_parse_info::is_trx_start(const Create_field &f) const +{ + return f.flags & VERS_SYS_START_FLAG; +} +bool Vers_parse_info::is_trx_end(const Create_field &f) const +{ + return f.flags & VERS_SYS_END_FLAG; +} static bool create_string(MEM_ROOT *mem_root, String **s, const char *value) { @@ -6675,12 +6683,28 @@ bool Vers_parse_info::check_and_fix_implicit( bool integer_fields= create_info->db_type->flags & HTON_SUPPORTS_SYS_VERSIONING; - if (vers_force) { + SELECT_LEX &slex= thd->lex->select_lex; + int vers_tables= 0; + bool from_select= slex.item_list.elements ? true : false; + + if (from_select) + { + for (TABLE_LIST *table= slex.table_list.first; table; table= table->next_local) + { + if (table->table && table->table->versioned()) + vers_tables++; + } + } + + // CREATE ... SELECT: if at least one table in SELECT is versioned, + // then created table will be versioned. + if (vers_force || vers_tables > 0) + { declared_with_system_versioning= true; create_info->options|= HA_VERSIONED_TABLE; } - if (!need_to_check()) + if (!need_check()) return false; if (declared_without_system_versioning) @@ -6697,11 +6721,43 @@ bool Vers_parse_info::check_and_fix_implicit( return true; } + TABLE *orig_table= NULL; List_iterator it(alter_info->create_list); while (Create_field *f= it++) { - if (is_trx_start(f->field_name) || is_trx_end(f->field_name)) + if (is_trx_start(*f)) + { + if (!generated_as_row.start) // not inited in CREATE ... SELECT + { + DBUG_ASSERT(vers_tables > 0); + if (orig_table && orig_table != f->field->orig_table) + { + err_different_tables: + my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, + "system fields selected from different tables"); + return true; + } + orig_table= f->field->orig_table; + generated_as_row.start= new (thd->mem_root) String(f->field_name, system_charset_info); + period_for_system_time.start= generated_as_row.start; + } continue; + } + if (is_trx_end(*f)) + { + if (!generated_as_row.end) + { + DBUG_ASSERT(vers_tables > 0); + if (orig_table && orig_table != f->field->orig_table) + { + goto err_different_tables; + } + orig_table= f->field->orig_table; + generated_as_row.end= new (thd->mem_root) String(f->field_name, system_charset_info); + period_for_system_time.end= generated_as_row.end; + } + continue; + } if ((f->versioning == Column_definition::VERSIONING_NOT_SET && !declared_with_system_versioning) || @@ -6711,32 +6767,50 @@ bool Vers_parse_info::check_and_fix_implicit( } } - if (fix_implicit(thd, alter_info, integer_fields)) + if (vers_tables > 0) + { + if (!generated_as_row.start && !generated_as_row.end) + { + declared_with_system_versioning= false; + create_info->options&= ~HA_VERSIONED_TABLE; + return false; + } + if (!generated_as_row.start || !generated_as_row.end) + { + my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, + "both ROW START and ROW END system fields required in SELECT resultset"); + return true; + } + } + else if (fix_implicit(thd, alter_info, integer_fields)) return true; - int not_set= 0; - int with= 0; + int plain_cols= 0; // column doesn't have WITH or WITHOUT SYSTEM VERSIONING + int vers_cols= 0; // column has WITH SYSTEM VERSIONING it.rewind(); while (const Create_field *f= it++) { - if (is_trx_start(f->field_name) || is_trx_end(f->field_name)) + if (is_trx_start(*f) || is_trx_end(*f)) continue; if (f->versioning == Column_definition::VERSIONING_NOT_SET) - not_set++; + plain_cols++; else if (f->versioning == Column_definition::WITH_VERSIONING) - with++; + vers_cols++; } bool table_with_system_versioning= generated_as_row.start || generated_as_row.end || period_for_system_time.start || period_for_system_time.end; - if (!thd->lex->tmp_table() && with == 0 && - (not_set == 0 || !table_with_system_versioning)) + if (!thd->lex->tmp_table() && + // CREATE from SELECT (Create_fields are not yet added) + !from_select && + vers_cols == 0 && + (plain_cols == 0 || !table_with_system_versioning)) { my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, - "versioned fields missing"); + "no columns defined with system versioning!"); return true; } @@ -6762,7 +6836,7 @@ bool Vers_parse_info::check_and_fix_alter(THD *thd, Alter_info *alter_info, create_info->db_type->flags & HTON_SUPPORTS_SYS_VERSIONING; const char *table_name= share->table_name.str; - if (!need_to_check() && !share->versioned) + if (!need_check() && !share->versioned) return false; if (declared_without_system_versioning) @@ -6934,7 +7008,7 @@ bool Vers_parse_info::check_generated_type(const char *table_name, List_iterator it(alter_info->create_list); while (Create_field *f= it++) { - if (is_trx_start(f->field_name) || is_trx_end(f->field_name)) + if (is_trx_start(*f) || is_trx_end(*f)) { if (integer_fields) { diff --git a/sql/handler.h b/sql/handler.h index a0da6852da4..76d6bae734f 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -1670,6 +1670,7 @@ struct Schema_specification_st } }; +class Create_field; struct Vers_parse_info { @@ -1701,8 +1702,10 @@ struct Vers_parse_info private: bool is_trx_start(const char *name) const; bool is_trx_end(const char *name) const; + bool is_trx_start(const Create_field &f) const; + bool is_trx_end(const Create_field &f) const; bool fix_implicit(THD *thd, Alter_info *alter_info, bool integer_fields); - bool need_to_check() const + bool need_check() const { return has_versioned_fields || diff --git a/sql/mysqld.cc b/sql/mysqld.cc index bb96893e49d..1696f689aab 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -784,7 +784,7 @@ char *opt_logname, *opt_slow_logname, *opt_bin_logname; /* System Versioning */ char *temporal_current_timestamp; my_bool vers_force= false; -my_bool vers_hide= false; +ulong vers_hide= VERS_HIDE_AUTO; /* Static variables */ diff --git a/sql/mysqld.h b/sql/mysqld.h index 20300895fe5..8cf14cc19a5 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -177,7 +177,13 @@ extern const char *log_output_str; extern const char *log_backup_output_str; extern char *temporal_current_timestamp; extern my_bool vers_force; -extern my_bool vers_hide; +enum vers_hide_enum { + VERS_HIDE_AUTO= 0, + VERS_HIDE_IMPLICIT, + VERS_HIDE_FULL, + VERS_HIDE_NEVER +}; +extern ulong vers_hide; extern char *mysql_home_ptr, *pidfile_name_ptr; extern MYSQL_PLUGIN_IMPORT char glob_hostname[FN_REFLEN]; extern char mysql_home[FN_REFLEN]; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 2ca2c9141f3..df50b2a7afa 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -7586,10 +7586,27 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, { Item_field *f= static_cast(item); DBUG_ASSERT(f->field); - if (f->field->flags & HIDDEN_FLAG) + uint32 fl= f->field->flags; + bool sys_field= fl & (VERS_SYS_START_FLAG | VERS_SYS_END_FLAG); + SELECT_LEX *slex= thd->lex->current_select; + TABLE *table= f->field->table; + DBUG_ASSERT(table && table->pos_in_table_list); + TABLE_LIST *tl= table->pos_in_table_list; + vers_range_type_t vers_type= + tl->vers_conditions.type == FOR_SYSTEM_TIME_UNSPECIFIED ? + slex->vers_conditions.type : tl->vers_conditions.type; + + if ((sys_field && vers_hide == VERS_HIDE_FULL && + thd->lex->sql_command != SQLCOM_CREATE_TABLE) || + ((fl & HIDDEN_FLAG) && ( + !sys_field || + vers_hide == VERS_HIDE_IMPLICIT || + (vers_hide == VERS_HIDE_AUTO && ( + vers_type == FOR_SYSTEM_TIME_UNSPECIFIED || + vers_type == FOR_SYSTEM_TIME_AS_OF))))) continue; } - if (item->type() == Item::REF_ITEM) + else if (item->type() == Item::REF_ITEM) { Item *i= item; while (i->type() == Item::REF_ITEM) diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 50b98678271..b0e1d06365f 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -4164,6 +4164,12 @@ static TABLE *create_table_from_items(THD *thd, alter_info->create_list.push_back(cr_field, thd->mem_root); } + if (create_info->vers_info.check_and_fix_implicit( + thd, alter_info, create_info, create_table->table_name)) + { + DBUG_RETURN(NULL); + } + DEBUG_SYNC(thd,"create_table_select_before_create"); /* Check if LOCK TABLES + CREATE OR REPLACE of existing normal table*/ diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 677dd269722..cc0dcf9a3a8 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3876,13 +3876,6 @@ mysql_execute_command(THD *thd) if (!(create_info.used_fields & HA_CREATE_USED_ENGINE)) create_info.use_default_db_type(thd); - if (!create_info.like() && - create_info.vers_info.check_and_fix_implicit( - thd, &alter_info, &create_info, create_table->table_name)) - { - goto end_with_restore_list; - } - /* If we are using SET CHARSET without DEFAULT, add an implicit DEFAULT to not confuse old users. (This may change). @@ -4069,6 +4062,11 @@ mysql_execute_command(THD *thd) } else { + if (create_info.vers_info.check_and_fix_implicit( + thd, &alter_info, &create_info, create_table->table_name)) + { + goto end_with_restore_list; + } /* In STATEMENT format, we probably have to replicate also temporary tables, like mysql replication does. Also check if the requested diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 7658bb9e2d0..1c57ca2e77a 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -16173,7 +16173,12 @@ Field *create_tmp_field_from_field(THD *thd, Field *org_field, item->result_field= new_field; else new_field->field_name= name; - new_field->flags|= (org_field->flags & NO_DEFAULT_VALUE_FLAG); + new_field->flags|= (org_field->flags & ( + NO_DEFAULT_VALUE_FLAG | + HIDDEN_FLAG | + VERS_SYS_START_FLAG | + VERS_SYS_END_FLAG | + VERS_OPTIMIZED_UPDATE_FLAG)); if (org_field->maybe_null() || (item && item->maybe_null)) new_field->flags&= ~NOT_NULL_FLAG; // Because of outer join if (org_field->type() == MYSQL_TYPE_VAR_STRING || diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 0f1098dcede..bedc32d6010 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2064,7 +2064,7 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, { uint flags = field->flags; - if (vers_hide && + if (vers_hide == VERS_HIDE_FULL && (flags & (VERS_SYS_START_FLAG | VERS_SYS_END_FLAG))) continue; @@ -2233,7 +2233,7 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, hton->index_options); } - if (table->versioned() && !vers_hide) + if (table->versioned() && vers_hide != VERS_HIDE_FULL) { const Field *fs = table->vers_start_field(); const Field *fe = table->vers_end_field(); @@ -2282,7 +2282,7 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, add_table_options(thd, table, create_info_arg, table_list->schema_table != 0, 0, packet); - if (table->versioned() && !vers_hide) + if (table->versioned() && vers_hide != VERS_HIDE_FULL) { packet->append(STRING_WITH_LEN(" WITH SYSTEM VERSIONING")); } diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index b6f84e88d07..9fb32174d1c 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -391,9 +391,14 @@ static Sys_var_mybool Sys_vers_force( "vers_force", "Force system versioning for all created tables", GLOBAL_VAR(vers_force), CMD_LINE(OPT_ARG), DEFAULT(FALSE)); -static Sys_var_mybool Sys_vers_hide( - "vers_hide", "Hide system versioning from being displayed in table info", - GLOBAL_VAR(vers_hide), CMD_LINE(OPT_ARG), DEFAULT(FALSE)); +static const char *vers_hide_keywords[]= {"AUTO", "IMPLICIT", "FULL", "NEVER", NullS}; +static Sys_var_enum Sys_vers_hide( + "vers_hide", "Hide system versioning from being displayed in table info. " + "AUTO: hide implicit system fields only in non-versioned and AS OF queries; " + "IMPLICIT: hide implicit system fields in all queries; " + "FULL: hide any system fields in all queries and hide versioning info in SHOW commands; " + "NEVER: don't hide system fields", + GLOBAL_VAR(vers_hide), CMD_LINE(OPT_ARG), vers_hide_keywords, DEFAULT(VERS_HIDE_AUTO)); static Sys_var_ulonglong Sys_binlog_cache_size( "binlog_cache_size", "The size of the transactional cache for " diff --git a/sql/table.cc b/sql/table.cc index b256b3e91b6..6470d5dfa4a 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -2563,8 +2563,8 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, DBUG_PRINT("info", ("Columns with system versioning: [%d, %d]", row_start, row_end)); versioned= true; vers_init(); - row_start_field = row_start; - row_end_field = row_end; + row_start_field= row_start; + row_end_field= row_end; vers_start_field()->flags|= VERS_SYS_START_FLAG; vers_end_field()->flags|= VERS_SYS_END_FLAG; } // if (system_period == NULL) -- cgit v1.2.1 From f77a4135bf9deffa7220fd4d4e4c597313e886d7 Mon Sep 17 00:00:00 2001 From: kevg Date: Thu, 30 Mar 2017 15:32:31 +0300 Subject: SQL: parsing of QUERY FOR [fixes #159] Reverts 46e36bbffa7cd8d9eb861a22755025ffe8751449 - SQL: fix assertion failure in parser --- sql/sql_lex.cc | 15 +++++++++++++++ sql/sql_yacc.yy | 27 +++++++++++---------------- 2 files changed, 26 insertions(+), 16 deletions(-) (limited to 'sql') diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 5260e81ff8b..5f3c83eed71 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1368,6 +1368,21 @@ int MYSQLlex(YYSTYPE *yylval, THD *thd) return FOR_SYM; } break; + case QUERY_SYM: + { + CHARSET_INFO *cs= thd->charset(); + const char *p= lip->get_ptr(); + while (my_isspace(cs, *p)) + ++p; + if (lip->get_end_of_query() - p > 3 && my_isspace(cs, p[3]) && + 0 == strncasecmp(p, "for", 3)) + { + token= lex_one_token(yylval, thd); + lip->add_digest_token(token, yylval); + return QUERY_FOR_SYM; + } + return QUERY_SYM; + } default: break; } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 5eab752df3f..4abe1dbc24a 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -861,10 +861,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %parse-param { THD *thd } %lex-param { THD *thd } /* - Currently there are 106 shift/reduce conflicts. + Currently there are 103 shift/reduce conflicts. We should not introduce new conflicts any more. */ -%expect 106 +%expect 103 /* Comments for TOKENS. @@ -1353,6 +1353,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token PURGE %token QUARTER_SYM %token QUERY_SYM +%token QUERY_FOR_SYM /* INTERNAL */ %token QUICK %token RAISE_SYM /* Oracle-PLSQL-R */ %token RANGE_SYM /* SQL-2003-R */ @@ -8828,7 +8829,7 @@ trans_or_timestamp: opt_query_for_system_time_clause: /* empty */ {} - | QUERY_SYM FOR_SYSTEM_TIME_SYM for_system_time_expr + | QUERY_FOR_SYM SYSTEM_TIME_SYM for_system_time_expr { DBUG_ASSERT(Select); Select->vers_conditions= Lex->vers_conditions; @@ -11705,21 +11706,15 @@ date_time_type: | TIMESTAMP {$$=MYSQL_TIMESTAMP_DATETIME;} ; +table_alias: + /* empty */ + | AS + | '=' + ; + opt_table_alias: /* empty */ { $$=0; } - | ident - { - $$= (LEX_STRING*) thd->memdup(&$1,sizeof(LEX_STRING)); - if ($$ == NULL) - MYSQL_YYABORT; - } - | AS ident - { - $$= (LEX_STRING*) thd->memdup(&$2,sizeof(LEX_STRING)); - if ($$ == NULL) - MYSQL_YYABORT; - } - | '=' ident + | table_alias ident { $$= (LEX_STRING*) thd->memdup(&$2,sizeof(LEX_STRING)); if ($$ == NULL) -- cgit v1.2.1 From 9e9af76eaf4eec4f105e029975ec5a142ec7e8f2 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Thu, 30 Mar 2017 12:57:31 +0300 Subject: SQL: vers_current_time refactoring [closes #117] * session sysvars; * moved value parsing to set variable phase; * renamed 'temporal_current_timestamp' to 'vers_current_time'. --- sql/handler.cc | 2 +- sql/mysqld.cc | 16 ++++-- sql/mysqld.h | 26 ++++++++- sql/set_var.h | 7 +++ sql/sql_base.cc | 1 + sql/sql_class.h | 4 ++ sql/sql_select.cc | 27 ++++----- sql/sql_show.cc | 1 + sql/sys_vars.cc | 10 ++-- sql/sys_vars.ic | 162 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ sql/table.h | 10 ---- 11 files changed, 226 insertions(+), 40 deletions(-) (limited to 'sql') diff --git a/sql/handler.cc b/sql/handler.cc index 62091b0f19e..13c4031b02d 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -6698,7 +6698,7 @@ bool Vers_parse_info::check_and_fix_implicit( // CREATE ... SELECT: if at least one table in SELECT is versioned, // then created table will be versioned. - if (vers_force || vers_tables > 0) + if (thd->variables.vers_force || vers_tables > 0) { declared_with_system_versioning= true; create_info->options|= HA_VERSIONED_TABLE; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 1696f689aab..454b5d10965 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -781,11 +781,6 @@ char *relay_log_info_file, *report_user, *report_password, *report_host; char *opt_relay_logname = 0, *opt_relaylog_index_name=0; char *opt_logname, *opt_slow_logname, *opt_bin_logname; -/* System Versioning */ -char *temporal_current_timestamp; -my_bool vers_force= false; -ulong vers_hide= VERS_HIDE_AUTO; - /* Static variables */ static volatile sig_atomic_t kill_in_progress; @@ -9315,6 +9310,17 @@ mysqld_get_one_option(int optid, const struct my_option *opt, char *argument) WSREP_SYNC_WAIT_BEFORE_READ); break; #endif /* WITH_WSREP */ + case OPT_VERS_CURRENT_TIME: + sys_var *var= static_cast(opt->app_type); + DBUG_ASSERT(var); + if (var->option_updated()) + { + sql_print_error("Can't start server: " + "cannot process --vers-current-time=%.*s", + FN_REFLEN, argument); + return 1; + } + break; } return 0; } diff --git a/sql/mysqld.h b/sql/mysqld.h index 8cf14cc19a5..e8a72b0b063 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -175,15 +175,34 @@ extern char *opt_backup_history_logname, *opt_backup_progress_logname, *opt_backup_settings_name; extern const char *log_output_str; extern const char *log_backup_output_str; -extern char *temporal_current_timestamp; -extern my_bool vers_force; + +enum vers_range_type_t +{ + FOR_SYSTEM_TIME_UNSPECIFIED = 0, + FOR_SYSTEM_TIME_AS_OF, + FOR_SYSTEM_TIME_FROM_TO, + FOR_SYSTEM_TIME_BETWEEN, + FOR_SYSTEM_TIME_ALL, + FOR_SYSTEM_TIME_BEFORE +}; + +struct st_vers_current_time +{ // This struct must be POD, so no virtual-anything! + char *str_value; // must be first + vers_range_type_t type; + MYSQL_TIME ltime; + st_vers_current_time() : + str_value(NULL), + type(FOR_SYSTEM_TIME_UNSPECIFIED) + {} +}; + enum vers_hide_enum { VERS_HIDE_AUTO= 0, VERS_HIDE_IMPLICIT, VERS_HIDE_FULL, VERS_HIDE_NEVER }; -extern ulong vers_hide; extern char *mysql_home_ptr, *pidfile_name_ptr; extern MYSQL_PLUGIN_IMPORT char glob_hostname[FN_REFLEN]; extern char mysql_home[FN_REFLEN]; @@ -652,6 +671,7 @@ enum options_mysqld OPT_SSL_KEY, OPT_THREAD_CONCURRENCY, OPT_WANT_CORE, + OPT_VERS_CURRENT_TIME, #ifdef WITH_WSREP OPT_WSREP_CAUSAL_READS, OPT_WSREP_SYNC_WAIT, diff --git a/sql/set_var.h b/sql/set_var.h index ddd6a225eb8..9c7d7f1135b 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -223,6 +223,13 @@ private: virtual bool session_update(THD *thd, set_var *var) = 0; virtual bool global_update(THD *thd, set_var *var) = 0; +public: + virtual bool option_updated() + { + DBUG_ASSERT(false); + return true; + } + protected: /** A pointer to a value of the variable for SHOW. diff --git a/sql/sql_base.cc b/sql/sql_base.cc index df50b2a7afa..d0c723af397 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -7478,6 +7478,7 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, Field_iterator_table_ref field_iterator; bool found; char name_buff[SAFE_NAME_LEN+1]; + ulong vers_hide= thd->variables.vers_hide; DBUG_ENTER("insert_fields"); DBUG_PRINT("arena", ("stmt arena: 0x%lx", (ulong)thd->stmt_arena)); diff --git a/sql/sql_class.h b/sql/sql_class.h index 49f62f9ea4e..e3601f141fa 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -706,6 +706,10 @@ typedef struct system_variables uint idle_transaction_timeout; uint idle_readonly_transaction_timeout; uint idle_readwrite_transaction_timeout; + + st_vers_current_time vers_current_time; + my_bool vers_force; + ulong vers_hide; } SV; /** diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 1c57ca2e77a..6739d51f360 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -670,24 +670,19 @@ setup_without_group(THD *thd, Ref_ptr_array ref_pointer_array, bool vers_select_conds_t::init_from_sysvar(THD *thd) { - const char var[]= "temporal_current_timestamp"; - sys_var *sv= intern_find_sys_var(var, sizeof(var) - 1); - DBUG_ASSERT(sv); - const char *data= *(char **)sv->option.value; - DBUG_ASSERT(data); - if (0 == strcmp(data, "all")) - { - init(FOR_SYSTEM_TIME_ALL, UNIT_TIMESTAMP); - } - else if (0 != strcmp(data, "now")) - { - Item *ts= - create_temporal_literal(thd, data, strlen(data), system_charset_info, - MYSQL_TYPE_DATETIME, true); - if (!ts) + st_vers_current_time &in= thd->variables.vers_current_time; + type= in.type; + unit= UNIT_TIMESTAMP; + if (type != FOR_SYSTEM_TIME_UNSPECIFIED && type != FOR_SYSTEM_TIME_ALL) + { + DBUG_ASSERT(type == FOR_SYSTEM_TIME_AS_OF); + start= new (thd->mem_root) Item_datetime_literal(thd, &in.ltime, 6); + if (!start) return true; - init(FOR_SYSTEM_TIME_AS_OF, UNIT_TIMESTAMP, ts); } + else + start= NULL; + end= NULL; return false; } diff --git a/sql/sql_show.cc b/sql/sql_show.cc index bedc32d6010..a3ce1fd19c6 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1986,6 +1986,7 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, TABLE *table= table_list->table; TABLE_SHARE *share= table->s; sql_mode_t sql_mode= thd->variables.sql_mode; + ulong vers_hide= thd->variables.vers_hide; bool foreign_db_mode= sql_mode & (MODE_POSTGRESQL | MODE_ORACLE | MODE_MSSQL | MODE_DB2 | MODE_MAXDB | MODE_ANSI); diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 9fb32174d1c..0b80684cd77 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -382,14 +382,14 @@ static Sys_var_charptr Sys_basedir( READ_ONLY GLOBAL_VAR(mysql_home_ptr), CMD_LINE(REQUIRED_ARG, 'b'), IN_FS_CHARSET, DEFAULT(0)); -static Sys_var_charptr sys_temporal_current_timestamp( - "temporal_current_timestamp", "Default AS OF value for versioned tables", - GLOBAL_VAR(temporal_current_timestamp), CMD_LINE(REQUIRED_ARG, 'b'), +static Sys_var_vers_asof Sys_vers_current_time( + "vers_current_time", "Default AS OF value for versioned tables", + SESSION_VAR(vers_current_time), CMD_LINE(REQUIRED_ARG, OPT_VERS_CURRENT_TIME), IN_FS_CHARSET, DEFAULT("now")); static Sys_var_mybool Sys_vers_force( "vers_force", "Force system versioning for all created tables", - GLOBAL_VAR(vers_force), CMD_LINE(OPT_ARG), DEFAULT(FALSE)); + SESSION_VAR(vers_force), CMD_LINE(OPT_ARG), DEFAULT(FALSE)); static const char *vers_hide_keywords[]= {"AUTO", "IMPLICIT", "FULL", "NEVER", NullS}; static Sys_var_enum Sys_vers_hide( @@ -398,7 +398,7 @@ static Sys_var_enum Sys_vers_hide( "IMPLICIT: hide implicit system fields in all queries; " "FULL: hide any system fields in all queries and hide versioning info in SHOW commands; " "NEVER: don't hide system fields", - GLOBAL_VAR(vers_hide), CMD_LINE(OPT_ARG), vers_hide_keywords, DEFAULT(VERS_HIDE_AUTO)); + SESSION_VAR(vers_hide), CMD_LINE(OPT_ARG), vers_hide_keywords, DEFAULT(VERS_HIDE_AUTO)); static Sys_var_ulonglong Sys_binlog_cache_size( "binlog_cache_size", "The size of the transactional cache for " diff --git a/sql/sys_vars.ic b/sql/sys_vars.ic index 780450b484b..76bf8631630 100644 --- a/sql/sys_vars.ic +++ b/sql/sys_vars.ic @@ -2485,3 +2485,165 @@ public: bool global_update(THD *thd, set_var *var); uchar *global_value_ptr(THD *thd, const LEX_STRING *base); }; + + +class Sys_var_vers_asof: public sys_var +{ +public: + Sys_var_vers_asof( + const char *name_arg, + const char *comment, + int flag_args, + ptrdiff_t off, + size_t size, + CMD_LINE getopt, + enum charset_enum is_os_charset_arg, + const char *def_val, + on_check_function on_check_func=0, + on_update_function on_update_func=0) : + sys_var( + &all_sys_vars, + name_arg, + comment, + flag_args, + off, + getopt.id, + getopt.arg_type, + SHOW_CHAR, + (intptr) def_val, + 0, + VARIABLE_NOT_IN_BINLOG, + on_check_func, + on_update_func, + 0) + { + option.var_type|= GET_STR; + if (global_update(def_val)) + { + DBUG_ASSERT(false); + } + } + + bool do_check(THD *thd, set_var *var) + { return false; } + + bool update(String &in, st_vers_current_time &out) + { + if (in.length() == 3 && + 0 == my_strcasecmp( + in.charset(), + "ALL", + in.ptr())) + { + out.type= FOR_SYSTEM_TIME_ALL; + } + else if (in.length() == 3 && + 0 == my_strcasecmp( + in.charset(), + "NOW", + in.ptr())) + { + out.type= FOR_SYSTEM_TIME_UNSPECIFIED; + } + else + { + MYSQL_TIME_STATUS status; + if (str_to_datetime( + in.ptr(), + in.length(), + &out.ltime, + flags, + &status) || + out.ltime.time_type != MYSQL_TIMESTAMP_DATETIME || + (status.warnings & ~MYSQL_TIME_NOTE_TRUNCATED) != 0) + { + return true; + } + out.type= FOR_SYSTEM_TIME_AS_OF; + } + return false; + } + bool update(THD *thd, set_var *var, st_vers_current_time &out) + { + Item *item= var->value; + + switch (item->result_type()) + { + case TIME_RESULT: + { + if (item->get_date(&out.ltime, 0)) + break; + out.type= FOR_SYSTEM_TIME_AS_OF; + return false; + } + + case STRING_RESULT: + { + String *str= item->val_str(); + if (!str || update(*str, out)) + break; + return false; + } + default: + break; + } + String *str= item->val_str(); + const char *cstr= str ? str->c_ptr_safe() : "NULL"; + my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), var->var->name.str, cstr); + return true; + } + bool global_update(const char *in) + { + String s(in, &my_charset_utf8_general_ci); + return update(s, global_var(st_vers_current_time)); + } + bool option_updated() + { + return global_update(global_var(st_vers_current_time).str_value); + } + bool global_update(THD *thd, set_var *var) + { + return update(thd, var, global_var(st_vers_current_time)); + } + bool session_update(THD *thd, set_var *var) + { + return update(thd, var, session_var(thd, st_vers_current_time)); + } + uchar *valptr(THD *thd, st_vers_current_time &val) + { + switch (val.type) + { + case FOR_SYSTEM_TIME_UNSPECIFIED: + return (uchar*) thd->strdup("NOW"); + case FOR_SYSTEM_TIME_ALL: + return (uchar*) thd->strdup("ALL"); + case FOR_SYSTEM_TIME_AS_OF: + { + uchar *buf= (uchar*) thd->alloc(MAX_DATE_STRING_REP_LENGTH); + if (buf) + { + if (!my_datetime_to_str(&val.ltime, (char*) buf, 6)) + { + my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), "vers_current_time", "NULL (wrong datetime)"); + return (uchar*) thd->strdup("Error: wrong datetime"); + } + } + return buf; + } + default: + break; + } + my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), "vers_current_time", "NULL (wrong range type)"); + return (uchar*) thd->strdup("Error: wrong range type"); + } + void session_save_default(THD *thd, set_var *var) + { DBUG_ASSERT(false); } + void global_save_default(THD *thd, set_var *var) + { DBUG_ASSERT(false); } + uchar *session_value_ptr(THD *thd, const LEX_STRING *base) + { return valptr(thd, session_var(thd, st_vers_current_time)); } + uchar *global_value_ptr(THD *thd, const LEX_STRING *base) + { return valptr(thd, global_var(st_vers_current_time)); } + uchar *default_value_ptr(THD *thd) + { return (uchar *)option.def_value; } +}; diff --git a/sql/table.h b/sql/table.h index ba497f1c365..f6736658674 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1841,16 +1841,6 @@ class Item_in_subselect; 4) jtbm semi-join (jtbm_subselect != NULL) */ -enum vers_range_type_t -{ - FOR_SYSTEM_TIME_UNSPECIFIED = 0, - FOR_SYSTEM_TIME_AS_OF, - FOR_SYSTEM_TIME_FROM_TO, - FOR_SYSTEM_TIME_BETWEEN, - FOR_SYSTEM_TIME_ALL, - FOR_SYSTEM_TIME_BEFORE -}; - enum vers_range_unit_t { UNIT_TIMESTAMP = 0, -- cgit v1.2.1 From d85fa886768001e9bb4bd5f63eee9641aa8abc92 Mon Sep 17 00:00:00 2001 From: kevg Date: Fri, 31 Mar 2017 13:21:30 +0300 Subject: Misc: disable clang warning 'offset for non-POD type' --- sql/sys_vars.ic | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'sql') diff --git a/sql/sys_vars.ic b/sql/sys_vars.ic index 76bf8631630..27987a8d8b8 100644 --- a/sql/sys_vars.ic +++ b/sql/sys_vars.ic @@ -77,6 +77,11 @@ #define GET_HA_ROWS GET_ULONG #endif +// Disable warning caused by SESSION_VAR() macro +#ifdef __clang__ +#pragma clang diagnostic ignored "-Winvalid-offsetof" +#endif + /* special assert for sysvars. Tells the name of the variable, and fails even in non-debug builds. -- cgit v1.2.1 From 994cdf1b7ac2d6f375aade57d72f3c26d0da0376 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Mon, 17 Apr 2017 11:22:52 +0300 Subject: Parser: Oracle mode fix --- sql/sql_yacc_ora.yy | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'sql') diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy index b30e47dca8f..f989e3b93ff 100644 --- a/sql/sql_yacc_ora.yy +++ b/sql/sql_yacc_ora.yy @@ -505,6 +505,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token FORCE_SYM %token FOREIGN /* SQL-2003-R */ %token FOR_SYM /* SQL-2003-R */ +%token FOR_SYSTEM_TIME_SYM /* INTERNAL */ %token FORMAT_SYM %token FOUND_SYM /* SQL-2003-R */ %token FROM @@ -735,6 +736,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token PARTITIONING_SYM %token PASSWORD_SYM %token PERCENT_RANK_SYM +%token PERIOD_SYM /* 32N2439 */ %token PERSISTENT_SYM %token PHASE_SYM %token PLUGINS_SYM @@ -761,6 +763,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token PURGE %token QUARTER_SYM %token QUERY_SYM +%token QUERY_FOR_SYM /* INTERNAL */ %token QUICK %token RAISE_SYM /* Oracle-PLSQL-R */ %token RANGE_SYM /* SQL-2003-R */ @@ -899,6 +902,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token SWAPS_SYM %token SWITCHES_SYM %token SYSDATE +%token SYSTEM /* 32N2439 */ +%token SYSTEM_TIME_SYM /* 32N2439 */ %token TABLES %token TABLESPACE %token TABLE_REF_PRIORITY @@ -967,6 +972,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token VARIANCE_SYM %token VARYING /* SQL-2003-R */ %token VAR_SAMP_SYM +%token VERSIONING_SYM /* 32N2439 */ %token VIA_SYM %token VIEW_SYM /* SQL-2003-N */ %token VIRTUAL_SYM @@ -979,8 +985,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token WINDOW_SYM %token WHILE_SYM %token WITH /* SQL-2003-R */ +%token WITHOUT /* SQL-2003-R */ %token WITH_CUBE_SYM /* INTERNAL */ %token WITH_ROLLUP_SYM /* INTERNAL */ +%token WITH_SYSTEM_SYM /* INTERNAL */ %token WORK_SYM /* SQL-2003-N */ %token WRAPPER_SYM %token WRITE_SYM /* SQL-2003-N */ -- cgit v1.2.1 From ecd18bc099bceabe309a7d4a954f2f49bddb8604 Mon Sep 17 00:00:00 2001 From: kevg Date: Wed, 5 Apr 2017 21:37:31 +0300 Subject: SQL: allow FOR SYSTEM_TIME BEFORE for SELECT queries [closes #170] --- sql/sql_select.cc | 8 -------- 1 file changed, 8 deletions(-) (limited to 'sql') diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 6739d51f360..47de223d590 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -786,14 +786,6 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, table->vers_conditions.type == FOR_SYSTEM_TIME_UNSPECIFIED ? slex->vers_conditions : table->vers_conditions; - if (vers_conditions.type == FOR_SYSTEM_TIME_BEFORE && - thd->lex->sql_command != SQLCOM_TRUNCATE) - { - my_error(ER_VERS_WRONG_QUERY_TYPE, MYF(0), "FOR SYSTEM_TIME BEFORE", - "TRUNCATE"); - DBUG_RETURN(-1); - } - if (vers_conditions.type == FOR_SYSTEM_TIME_UNSPECIFIED) { if (vers_conditions.init_from_sysvar(thd)) -- cgit v1.2.1 From d64702d43a0913c0710347cc1c7f9a48293719df Mon Sep 17 00:00:00 2001 From: kevg Date: Tue, 11 Apr 2017 17:58:56 +0300 Subject: SQL: different results when querying a VIEW from PREPARED STATEMENT and without it [fixes #176] --- sql/sql_select.cc | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'sql') diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 47de223d590..45dc3ea4583 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -703,11 +703,6 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, DBUG_RETURN(0); } - while (tables && tables->is_view() && !thd->stmt_arena->is_stmt_prepare()) - { - tables= tables->view->select_lex.table_list.first; - } - for (table= tables; table; table= table->next_local) { if (table->table && table->table->versioned()) @@ -16879,9 +16874,9 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List &fields, continue; // Some kind of const item } - if (type == Item::FIELD_ITEM) + if (type == Item::FIELD_ITEM || type == Item::REF_ITEM) { - Item_field *item_field= (Item_field *)item; + Item_field *item_field= (Item_field *)item->real_item(); Field *field= item_field->field; TABLE_SHARE *s= field->table->s; if (s->versioned) -- cgit v1.2.1 From 06ad9c01a618dc793fc4388c49a4a24204389276 Mon Sep 17 00:00:00 2001 From: kevg Date: Tue, 11 Apr 2017 18:19:01 +0300 Subject: Misc: unneeded code after c17b5a3c973dff544613043b74db7d131e0cb55a --- sql/handler.cc | 7 ------- 1 file changed, 7 deletions(-) (limited to 'sql') diff --git a/sql/handler.cc b/sql/handler.cc index 13c4031b02d..b66e616dfda 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -6038,9 +6038,6 @@ int handler::ha_update_row(const uchar *old_data, uchar *new_data) DBUG_ASSERT(new_data == table->record[0]); DBUG_ASSERT(old_data == table->record[1]); - // it is important to keep 'old_data' intact for versioning to work correctly on slave side - if (table->file->check_table_binlog_row_based(1) && table->versioned()) - memcpy(table->record[2], table->record[1], table->s->reclength); MYSQL_UPDATE_ROW_START(table_share->db.str, table_share->table_name.str); mark_trx_read_write(); increment_statistics(&SSV::ha_update_count); @@ -6053,11 +6050,7 @@ int handler::ha_update_row(const uchar *old_data, uchar *new_data) { rows_changed++; if (table->file->check_table_binlog_row_based(1)) - { - if (table->versioned()) - memcpy(table->record[1], table->record[2], table->s->reclength); error= binlog_log_row(table, old_data, new_data, log_func); - } } return error; } -- cgit v1.2.1 From a1d42a6f205bbac25c26e4776e1592369f54b391 Mon Sep 17 00:00:00 2001 From: kevg Date: Tue, 11 Apr 2017 18:28:24 +0300 Subject: Misc: code cleanup --- sql/sql_update.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/sql_update.cc b/sql/sql_update.cc index c6e2736f4ce..b5f4d593a10 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -366,7 +366,8 @@ int mysql_update(THD *thd, { DBUG_RETURN(1); } - bool has_vers_fields= check_has_vers_fields(fields); + bool has_vers_fields= + table->versioned() ? check_has_vers_fields(fields) : false; if (check_key_in_view(thd, table_list)) { my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "UPDATE"); -- cgit v1.2.1 From ecc6cd95c455b5cff25db1e2825a238d73256fd8 Mon Sep 17 00:00:00 2001 From: kevg Date: Tue, 11 Apr 2017 19:37:48 +0300 Subject: SQL: Default 'simple' algorithm for InnoDB 'AS OF' [closes #175] --- sql/sql_class.h | 1 + sql/sql_select.cc | 24 ++++++++++++------------ sql/sys_vars.cc | 6 ++++++ 3 files changed, 19 insertions(+), 12 deletions(-) (limited to 'sql') diff --git a/sql/sql_class.h b/sql/sql_class.h index e3601f141fa..303992c40d8 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -710,6 +710,7 @@ typedef struct system_variables st_vers_current_time vers_current_time; my_bool vers_force; ulong vers_hide; + my_bool vers_innodb_algorithm_simple; } SV; /** diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 45dc3ea4583..578fdc753a7 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -771,8 +771,6 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, } } - const static bool vers_simple_select= false; - for (table= tables; table; table= table->next_local) { if (table->table && table->table->versioned()) @@ -831,7 +829,10 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, newx Item_field(thd, &slex->context, table->db, table->alias, fend); Item *row_end2= row_end; - if (table->table->versioned_by_sql()) + bool tmp_from_ib= + table->table->s->table_category == TABLE_CATEGORY_TEMPORARY && + table->table->vers_start_field()->type() == MYSQL_TYPE_LONGLONG; + if (table->table->versioned_by_sql() && !tmp_from_ib) { if (vers_conditions.unit == UNIT_TRX_ID) { @@ -839,8 +840,9 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, DBUG_RETURN(-1); } } - else if (vers_simple_select && vers_conditions.unit == UNIT_TIMESTAMP - && vers_conditions.type != FOR_SYSTEM_TIME_UNSPECIFIED) + else if (thd->variables.vers_innodb_algorithm_simple && + vers_conditions.unit == UNIT_TIMESTAMP && + vers_conditions.type != FOR_SYSTEM_TIME_UNSPECIFIED) { DBUG_ASSERT(table->table->s && table->table->s->db_plugin); handlerton *hton= plugin_hton(table->table->s->db_plugin); @@ -859,19 +861,17 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, } Item *cond1= 0, *cond2= 0, *curr= 0; - // Temporary tables of type HEAP can be created from INNODB tables and - // thus will have uint64 type of sys_trx_(start|end) field. + // Temporary tables of can be created from INNODB tables and thus will + // have uint64 type of sys_trx_(start|end) field. // They need special handling. TABLE *t= table->table; - if ((t->s->table_category == TABLE_CATEGORY_TEMPORARY - ? t->vers_start_field()->type() != MYSQL_TYPE_LONGLONG - : t->versioned_by_sql()) || - vers_simple_select) + if (tmp_from_ib || t->versioned_by_sql() || + thd->variables.vers_innodb_algorithm_simple) { switch (vers_conditions.type) { case FOR_SYSTEM_TIME_UNSPECIFIED: - if (table->table->versioned_by_sql()) + if (table->table->versioned_by_sql() && !tmp_from_ib) { MYSQL_TIME max_time; thd->variables.time_zone->gmt_sec_to_TIME(&max_time, TIMESTAMP_MAX_VALUE); diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 0b80684cd77..f1a674388fc 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -400,6 +400,12 @@ static Sys_var_enum Sys_vers_hide( "NEVER: don't hide system fields", SESSION_VAR(vers_hide), CMD_LINE(OPT_ARG), vers_hide_keywords, DEFAULT(VERS_HIDE_AUTO)); +static Sys_var_mybool Sys_vers_innodb_algorithm_simple( + "vers_innodb_algorithm_simple", + "Use simple algorithm of timestamp handling in InnoDB instead of TRX_SEES", + SESSION_VAR(vers_innodb_algorithm_simple), CMD_LINE(OPT_ARG), + DEFAULT(TRUE)); + static Sys_var_ulonglong Sys_binlog_cache_size( "binlog_cache_size", "The size of the transactional cache for " "updates to transactional engines for the binary log. " -- cgit v1.2.1 From 915963ba306b209c9428bee3a548e7a07ac81094 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Fri, 14 Apr 2017 11:07:58 +0300 Subject: SQL: SIGABRT fix on versioned partitioning error [fixes #177] --- sql/table.cc | 1 + 1 file changed, 1 insertion(+) (limited to 'sql') diff --git a/sql/table.cc b/sql/table.cc index 6470d5dfa4a..c4f3a05f5c5 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -3448,6 +3448,7 @@ partititon_err: if (err) { + outparam->file->ha_close(); error= OPEN_FRM_OPEN_ERROR; error_reported= true; goto err; -- cgit v1.2.1 From 7d2ed77e31c18c28fa891699ae4dafcb3b087afe Mon Sep 17 00:00:00 2001 From: kevg Date: Mon, 17 Apr 2017 20:35:39 +0300 Subject: SQL: SIGSEGV in create_tmp_table() [fixes #179] tests are in main,rpl suites --- sql/sql_select.cc | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'sql') diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 578fdc753a7..d1d0d08d69a 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -16876,15 +16876,18 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List &fields, if (type == Item::FIELD_ITEM || type == Item::REF_ITEM) { - Item_field *item_field= (Item_field *)item->real_item(); - Field *field= item_field->field; - TABLE_SHARE *s= field->table->s; - if (s->versioned) + if (item->real_item()->type() == Item::FIELD_ITEM) { - if (field->flags & VERS_SYS_START_FLAG) - sys_trx_start= new_field; - else if (field->flags & VERS_SYS_END_FLAG) - sys_trx_end= new_field; + Item_field *item_field= (Item_field *)item->real_item(); + Field *field= item_field->field; + TABLE_SHARE *s= field->table->s; + if (s->versioned) + { + if (field->flags & VERS_SYS_START_FLAG) + sys_trx_start= new_field; + else if (field->flags & VERS_SYS_END_FLAG) + sys_trx_end= new_field; + } } } if (type == Item::TYPE_HOLDER) -- cgit v1.2.1 From 27a6ef0a9e3185db5bb59e2706fc3f3324c37b27 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Wed, 19 Apr 2017 16:34:36 +0300 Subject: IB,SQL: Innopart UPDATE [fixes #178] --- sql/partition_info.cc | 1 + 1 file changed, 1 insertion(+) (limited to 'sql') diff --git a/sql/partition_info.cc b/sql/partition_info.cc index c1a792c87e0..2b1bc41931d 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -1211,6 +1211,7 @@ bool partition_info::vers_setup_2(THD * thd, bool is_create_table_ind) } else if (vers_scan_min_max(thd, el)) { + table->s->stat_trx= NULL; // may be a leak on endless table open error= true; break; } -- cgit v1.2.1 From 1e8a81dea67a66e49dcfe048f4339159c851c46d Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 25 Apr 2017 15:02:48 +0300 Subject: SQL: CREATE VIEW and misc improvements [fixes #183] --- sql/share/errmsg-utf8.txt | 9 +++ sql/sql_base.cc | 5 +- sql/sql_lex.cc | 22 ++++++++ sql/sql_lex.h | 3 +- sql/sql_select.cc | 29 ++++++---- sql/sql_view.cc | 137 +++++++++++++++++++++++++++++++++++++--------- sql/table.h | 22 ++++++-- 7 files changed, 183 insertions(+), 44 deletions(-) (limited to 'sql') diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 0aeaa058cf9..81651071c05 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7535,5 +7535,14 @@ ER_VERS_NOT_ALLOWED ER_VERS_WRONG_QUERY_TYPE eng "%`s works only with %`s query type" +ER_VERS_VIEW_PROHIBITED + eng "Creating VIEW %`s is prohibited!" + +ER_VERS_WRONG_QUERY + eng "Wrong versioned query: %s" + +WARN_VERS_ALIAS_TOO_LONG + eng "Auto generated alias for `%s.%s` is too long; using `%s`." + ER_WRONG_TABLESPACE_NAME 42000 eng "Incorrect tablespace name `%-.192s`" diff --git a/sql/sql_base.cc b/sql/sql_base.cc index d0c723af397..8cdced1cb09 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -7582,7 +7582,6 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, if (!(item= field_iterator.create_item(thd))) DBUG_RETURN(TRUE); - /* This will be deprecated when HIDDEN feature will come to MariaDB. */ if (item->type() == Item::FIELD_ITEM) { Item_field *f= static_cast(item); @@ -7597,8 +7596,8 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, tl->vers_conditions.type == FOR_SYSTEM_TIME_UNSPECIFIED ? slex->vers_conditions.type : tl->vers_conditions.type; - if ((sys_field && vers_hide == VERS_HIDE_FULL && - thd->lex->sql_command != SQLCOM_CREATE_TABLE) || + if ((sys_field && (thd->lex->sql_command == SQLCOM_CREATE_VIEW || + vers_hide == VERS_HIDE_FULL && thd->lex->sql_command != SQLCOM_CREATE_TABLE)) || ((fl & HIDDEN_FLAG) && ( !sys_field || vers_hide == VERS_HIDE_IMPLICIT || diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 5f3c83eed71..7b61efa8fb8 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -7044,3 +7044,25 @@ bool LEX::sp_add_cfetch(THD *thd, const LEX_STRING &name) return true; return false; } + + +bool SELECT_LEX::vers_push_field(THD *thd, TABLE_LIST *table, const char* field_name) +{ + char buf[MAX_FIELD_NAME]; + Item_field *fld= new (thd->mem_root) Item_field(thd, &context, + table->db, table->alias, field_name); + if (!fld) + return true; + + item_list.push_back(fld); + + if (thd->lex->view_list.elements) + { + if (LEX_STRING *l= thd->make_lex_string(field_name, strlen(field_name))) + thd->lex->view_list.push_back(l); + else + return true; + } + + return false; +} diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 06aa248ee02..158130b5ddc 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -991,8 +991,9 @@ public: /* it is for correct printing SELECT options */ thr_lock_type lock_type; - /* System Versioning conditions */ + /* System Versioning */ vers_select_conds_t vers_conditions; + bool vers_push_field(THD *thd, TABLE_LIST *table, const char* field_name); void init_query(); void init_select(); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index d1d0d08d69a..5901d1d4d6f 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -694,6 +694,7 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, TABLE_LIST *table; int versioned_tables= 0; + int slex_conds_used= 0; Query_arena *arena= 0, backup; if (!thd->stmt_arena->is_conventional() && @@ -707,7 +708,7 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, { if (table->table && table->table->versioned()) versioned_tables++; - else if (table->vers_conditions.type != FOR_SYSTEM_TIME_UNSPECIFIED) + else if (table->vers_conditions) { my_error(ER_VERSIONING_REQUIRED, MYF(0), "`FOR SYSTEM_TIME` query"); DBUG_RETURN(-1); @@ -716,7 +717,7 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, if (versioned_tables == 0) { - if (slex->vers_conditions.type != FOR_SYSTEM_TIME_UNSPECIFIED) + if (slex->vers_conditions) { my_error(ER_VERSIONING_REQUIRED, MYF(0), "`FOR SYSTEM_TIME` query"); DBUG_RETURN(-1); @@ -775,17 +776,17 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, { if (table->table && table->table->versioned()) { - vers_select_conds_t &vers_conditions= - table->vers_conditions.type == FOR_SYSTEM_TIME_UNSPECIFIED ? - slex->vers_conditions : table->vers_conditions; + vers_select_conds_t &vers_conditions= !table->vers_conditions? + (++slex_conds_used, slex->vers_conditions) : + table->vers_conditions; - if (vers_conditions.type == FOR_SYSTEM_TIME_UNSPECIFIED) + if (!vers_conditions) { if (vers_conditions.init_from_sysvar(thd)) DBUG_RETURN(-1); } - if (vers_conditions.type != FOR_SYSTEM_TIME_UNSPECIFIED) + if (vers_conditions) { switch (slex->lock_type) { @@ -804,9 +805,9 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, break; } - if (vers_conditions.type == FOR_SYSTEM_TIME_ALL) + if (vers_conditions == FOR_SYSTEM_TIME_ALL) continue; - } + } // if (vers_conditions) COND** dst_cond= where_expr; if (table->on_expr) @@ -979,6 +980,12 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, if (arena) thd->restore_active_arena(arena, &backup); + if (!slex_conds_used && slex->vers_conditions) + { + my_error(ER_VERS_WRONG_QUERY, MYF(0), "unused `QUERY FOR SYSTEM_TIME` clause!"); + DBUG_RETURN(-1); + } + DBUG_RETURN(0); #undef newx } @@ -16985,8 +16992,8 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List &fields, sys_trx_end->flags|= VERS_SYS_END_FLAG | HIDDEN_FLAG; share->versioned= true; share->field= table->field; - share->row_start_field= field_count - 2; - share->row_end_field= field_count - 1; + share->row_start_field= sys_trx_start->field_index; + share->row_end_field= sys_trx_end->field_index; } DBUG_ASSERT(fieldnr == (uint) (reg_field - table->field)); diff --git a/sql/sql_view.cc b/sql/sql_view.cc index d542a5710f6..37703a2bc11 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -453,38 +453,125 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, goto err; } - /* Implicitly add versioning fields if needed */ - { - TABLE_LIST *tl = tables; - while (tl && tl->is_view()) - tl = tl->view->select_lex.table_list.first; - if (tl && tl->table) + { /* System Versioning begin */ + TABLE_LIST *impli_table= NULL, *expli_table= NULL; + const char *impli_start, *impli_end; + Item_field *expli_start= NULL, *expli_end= NULL; + + for (TABLE_LIST *table= tables; table; table= table->next_local) { - TABLE_SHARE *s= tl->table->s; - if (s->versioned) + DBUG_ASSERT(!table->is_view() || table->view); + + // Any versioned table in VIEW will add `FOR SYSTEM_TIME ALL` + WHERE: + // if there are at least one versioned table then VIEW will contain FOR_SYSTEM_TIME_ALL + // (because it is in fact LEX used to parse its SELECT). + if (table->is_view() && table->view->vers_conditions == FOR_SYSTEM_TIME_ALL) { - const char *start= s->vers_start_field()->field_name; - const char *end = s->vers_end_field()->field_name; + my_printf_error( + ER_VERS_VIEW_PROHIBITED, + "Creating VIEW %`s is prohibited: versioned VIEW %`s in query!", MYF(0), + view->table_name, + table->table_name); + res= true; + goto err; + } + + if (!table->table || !table->table->versioned()) + continue; - select_lex->item_list.push_back(new (thd->mem_root) Item_field( - thd, &select_lex->context, tables->db, tables->alias, start)); - select_lex->item_list.push_back(new (thd->mem_root) Item_field( - thd, &select_lex->context, tables->db, tables->alias, end)); + const char *table_start= table->table->vers_start_field()->field_name; + const char *table_end= table->table->vers_end_field()->field_name; - if (lex->view_list.elements) + if (!impli_table) + { + impli_table= table; + impli_start= table_start; + impli_end= table_end; + } + + /* Implicitly add versioning fields if needed */ + Item *item; + List_iterator_fast it(select_lex->item_list); + + DBUG_ASSERT(table->alias); + while ((item= it++)) + { + if (item->real_item()->type() != Item::FIELD_ITEM) + continue; + Item_field *fld= (Item_field*) (item->real_item()); + if (fld->table_name && 0 != my_strcasecmp(table_alias_charset, table->alias, fld->table_name)) + continue; + DBUG_ASSERT(fld->field_name); + if (0 == my_strcasecmp(system_charset_info, table_start, fld->field_name)) { - if (LEX_STRING *s= thd->make_lex_string(start, strlen(start))) - lex->view_list.push_back(s); - else + if (expli_start) + { + my_printf_error( + ER_VERS_VIEW_PROHIBITED, + "Creating VIEW %`s is prohibited: multiple start system fields `%s.%s`, `%s.%s` in query!", MYF(0), + view->table_name, + expli_table->alias, + expli_start->field_name, + table->alias, + fld->field_name); + res= true; goto err; - if (LEX_STRING *s= thd->make_lex_string(end, strlen(end))) - lex->view_list.push_back(s); + } + if (expli_table) + { + if (expli_table != table) + { +expli_table_err: + my_printf_error( + ER_VERS_VIEW_PROHIBITED, + "Creating VIEW %`s is prohibited: system fields from multiple tables %`s, %`s in query!", MYF(0), + view->table_name, + expli_table->alias, + table->alias); + res= true; + goto err; + } + } else + expli_table= table; + expli_start= fld; + impli_end= table_end; + } + else if (0 == my_strcasecmp(system_charset_info, table_end, fld->field_name)) + { + if (expli_end) + { + my_printf_error( + ER_VERS_VIEW_PROHIBITED, + "Creating VIEW %`s is prohibited: multiple end system fields `%s.%s`, `%s.%s` in query!", MYF(0), + view->table_name, + expli_table->alias, + expli_end->field_name, + table->alias, + fld->field_name); + res= true; goto err; + } + if (expli_table) + { + if (expli_table != table) + goto expli_table_err; + } + else + expli_table= table; + expli_end= fld; + impli_start= table_start; } - } - } - } + } // while ((item= it++)) + } // for (TABLE_LIST *table) + + if (expli_table) + impli_table= expli_table; + if (!expli_start && select_lex->vers_push_field(thd, impli_table, impli_start)) + goto err; + if (!expli_end && select_lex->vers_push_field(thd, impli_table, impli_end)) + goto err; + } /* System Versioning end */ view= lex->unlink_first_table(&link_to_local); @@ -2071,8 +2158,8 @@ bool insert_view_fields(THD *thd, List *list, TABLE_LIST *view) { TABLE_SHARE *s= fld->context->table_list->table->s; if (s->versioned && - (!strcmp(fld->name, s->vers_start_field()->field_name) || - !strcmp(fld->name, s->vers_end_field()->field_name))) + (!strcmp(fld->field_name, s->vers_start_field()->field_name) || + !strcmp(fld->field_name, s->vers_end_field()->field_name))) continue; list->push_back(fld, thd->mem_root); } diff --git a/sql/table.h b/sql/table.h index f6736658674..68f3a2dfe75 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1512,31 +1512,32 @@ public: bool versioned() const { + DBUG_ASSERT(s); return s->versioned; } /* Versioned by SQL layer */ bool versioned_by_sql() const { - DBUG_ASSERT(file); + DBUG_ASSERT(s && file); return s->versioned && !file->versioned(); } bool versioned_by_engine() const { - DBUG_ASSERT(file); + DBUG_ASSERT(s && file); return s->versioned && file->versioned(); } Field *vers_start_field() const { - DBUG_ASSERT(s->versioned); + DBUG_ASSERT(s && s->versioned); return field[s->row_start_field]; } Field *vers_end_field() const { - DBUG_ASSERT(s->versioned); + DBUG_ASSERT(s && s->versioned); return field[s->row_end_field]; } @@ -1874,6 +1875,19 @@ struct vers_select_conds_t } bool init_from_sysvar(THD *thd); + + bool operator== (vers_range_type_t b) + { + return type == b; + } + bool operator!= (vers_range_type_t b) + { + return type != b; + } + operator bool() + { + return type != FOR_SYSTEM_TIME_UNSPECIFIED; + } }; struct LEX; -- cgit v1.2.1 From 122ffa22112ebc78a63e39cc2fcc7b9da3675926 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Thu, 27 Apr 2017 10:24:16 +0300 Subject: SQL(misc): Query_arena_stmt RAII --- sql/sql_class.cc | 13 +++++++++++++ sql/sql_class.h | 16 ++++++++++++++++ sql/sql_derived.cc | 5 +---- sql/sql_select.cc | 10 ++-------- 4 files changed, 32 insertions(+), 12 deletions(-) (limited to 'sql') diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 1a422fd9966..052e5ae136d 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -7425,3 +7425,16 @@ bool Discrete_intervals_list::append(Discrete_interval *new_interval) } #endif /* !defined(MYSQL_CLIENT) */ + + +Query_arena_stmt::Query_arena_stmt(THD *_thd) : + thd(_thd) +{ + arena= thd->activate_stmt_arena_if_needed(&backup); +} + +Query_arena_stmt::~Query_arena_stmt() +{ + if (arena) + thd->restore_active_arena(arena, &backup); +} diff --git a/sql/sql_class.h b/sql/sql_class.h index 303992c40d8..b76b5cd6e60 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1024,6 +1024,22 @@ public: }; +class Query_arena_stmt +{ + THD *thd; + Query_arena backup; + Query_arena *arena; + +public: + Query_arena_stmt(THD *_thd); + ~Query_arena_stmt(); + bool arena_replaced() + { + return arena != NULL; + } +}; + + class Server_side_cursor; /** diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index 43302498d57..eb99544ea0b 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -723,14 +723,11 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived) TABLE_SHARE *s= tl->table->s; const char *db= tl->db; const char *alias= tl->alias; - Query_arena backup; - Query_arena *arena= thd->activate_stmt_arena_if_needed(&backup); + Query_arena_stmt on_stmt_arena(thd); sl->item_list.push_back(new (thd->mem_root) Item_field( thd, &sl->context, db, alias, s->vers_start_field()->field_name)); sl->item_list.push_back(new (thd->mem_root) Item_field( thd, &sl->context, db, alias, s->vers_end_field()->field_name)); - if (arena) - thd->restore_active_arena(arena, &backup); } } } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 5901d1d4d6f..5ebd3126a23 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -695,7 +695,6 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, TABLE_LIST *table; int versioned_tables= 0; int slex_conds_used= 0; - Query_arena *arena= 0, backup; if (!thd->stmt_arena->is_conventional() && !thd->stmt_arena->is_stmt_prepare() && !thd->stmt_arena->is_sp_execute()) @@ -727,7 +726,7 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, /* For prepared statements we create items on statement arena, because they must outlive execution phase for multiple executions. */ - arena= thd->activate_stmt_arena_if_needed(&backup); + Query_arena_stmt on_stmt_arena(thd); if (slex->saved_where) { @@ -798,8 +797,6 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, case TL_WRITE: case TL_WRITE_ONLY: my_error(ER_VERS_WRONG_PARAMS, MYF(0), "FOR SYSTEM_TIME query", "write-locking of historic rows"); - if (arena) - thd->restore_active_arena(arena, &backup); DBUG_RETURN(-1); default: break; @@ -969,7 +966,7 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, cond2, cond1)); - if (arena) + if (on_stmt_arena.arena_replaced()) *dst_cond= cond1; else thd->change_item_tree(dst_cond, cond1); @@ -977,9 +974,6 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, } // if (... table->table->versioned()) } // for (table= tables; ...) - if (arena) - thd->restore_active_arena(arena, &backup); - if (!slex_conds_used && slex->vers_conditions) { my_error(ER_VERS_WRONG_QUERY, MYF(0), "unused `QUERY FOR SYSTEM_TIME` clause!"); -- cgit v1.2.1 From 7153ff85a18a252f4e8454614de643fb32df8c49 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Fri, 28 Apr 2017 12:07:04 +0300 Subject: SQL: derived tables improvements [closes #185] --- sql/share/errmsg-utf8.txt | 3 ++ sql/sql_base.cc | 1 + sql/sql_derived.cc | 130 +++++++++++++++++++++++++++++++++++++++++----- sql/sql_lex.cc | 2 +- sql/sql_lex.h | 1 + sql/sql_select.cc | 24 ++++++++- sql/sql_view.cc | 18 +++++-- sql/table.h | 4 ++ 8 files changed, 163 insertions(+), 20 deletions(-) (limited to 'sql') diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 81651071c05..13408634738 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7538,6 +7538,9 @@ ER_VERS_WRONG_QUERY_TYPE ER_VERS_VIEW_PROHIBITED eng "Creating VIEW %`s is prohibited!" +ER_VERS_DERIVED_PROHIBITED + eng "Derived table is prohibited!" + ER_VERS_WRONG_QUERY eng "Wrong versioned query: %s" diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 8cdced1cb09..bf6f2c3931e 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -7597,6 +7597,7 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, slex->vers_conditions.type : tl->vers_conditions.type; if ((sys_field && (thd->lex->sql_command == SQLCOM_CREATE_VIEW || + slex->nest_level > 0 || vers_hide == VERS_HIDE_FULL && thd->lex->sql_command != SQLCOM_CREATE_TABLE)) || ((fl & HIDDEN_FLAG) && ( !sys_field || diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index eb99544ea0b..f159708b932 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -713,23 +713,127 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived) cursor= cursor->next_local) cursor->outer_join|= JOIN_TYPE_OUTER; } - if ((thd->stmt_arena->is_stmt_prepare() || - !thd->stmt_arena->is_stmt_execute()) && - !derived->is_view() && sl->table_list.elements > 0) + + // System Versioning begin +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat" +#pragma GCC diagnostic ignored "-Wformat-extra-args" + if ((thd->stmt_arena->is_stmt_prepare() || !thd->stmt_arena->is_stmt_execute()) + && sl->table_list.elements > 0) { - TABLE_LIST *tl= sl->table_list.first; - if (tl->table && tl->table->versioned()) + // Similar logic as in mysql_create_view() + TABLE_LIST *impli_table= NULL, *expli_table= NULL; + const char *impli_start, *impli_end; + Item_field *expli_start= NULL, *expli_end= NULL; + + for (TABLE_LIST *table= sl->table_list.first; table; table= table->next_local) + { + if (!table->table || !table->table->versioned()) + continue; + + const char *table_start= table->table->vers_start_field()->field_name; + const char *table_end= table->table->vers_end_field()->field_name; + if (!impli_table) + { + impli_table= table; + impli_start= table_start; + impli_end= table_end; + } + + /* Implicitly add versioning fields if needed */ + Item *item; + List_iterator_fast it(sl->item_list); + + DBUG_ASSERT(table->alias); + while ((item= it++)) + { + if (item->real_item()->type() != Item::FIELD_ITEM) + continue; + Item_field *fld= (Item_field*) (item->real_item()); + if (fld->table_name && 0 != my_strcasecmp(table_alias_charset, table->alias, fld->table_name)) + continue; + DBUG_ASSERT(fld->field_name); + if (0 == my_strcasecmp(system_charset_info, table_start, fld->field_name)) + { + if (expli_start) + { + my_printf_error( + ER_VERS_DERIVED_PROHIBITED, + "Derived table is prohibited: multiple start system fields `%s.%s`, `%s.%s` in query!", MYF(0), + expli_table->alias, + expli_start->field_name, + table->alias, + fld->field_name); + res= true; + goto exit; + } + if (expli_table) + { + if (expli_table != table) + { +expli_table_err: + my_printf_error( + ER_VERS_DERIVED_PROHIBITED, + "Derived table is prohibited: system fields from multiple tables %`s, %`s in query!", MYF(0), + expli_table->alias, + table->alias); + res= true; + goto exit; + } + } + else + expli_table= table; + expli_start= fld; + impli_end= table_end; + } + else if (0 == my_strcasecmp(system_charset_info, table_end, fld->field_name)) + { + if (expli_end) + { + my_printf_error( + ER_VERS_DERIVED_PROHIBITED, + "Derived table is prohibited: multiple end system fields `%s.%s`, `%s.%s` in query!", MYF(0), + expli_table->alias, + expli_end->field_name, + table->alias, + fld->field_name); + res= true; + goto exit; + } + if (expli_table) + { + if (expli_table != table) + goto expli_table_err; + } + else + expli_table= table; + expli_end= fld; + impli_start= table_start; + } + } // while ((item= it++)) + } // for (TABLE_LIST *table) + + if (expli_table) + impli_table= expli_table; + + if (impli_table) { - TABLE_SHARE *s= tl->table->s; - const char *db= tl->db; - const char *alias= tl->alias; Query_arena_stmt on_stmt_arena(thd); - sl->item_list.push_back(new (thd->mem_root) Item_field( - thd, &sl->context, db, alias, s->vers_start_field()->field_name)); - sl->item_list.push_back(new (thd->mem_root) Item_field( - thd, &sl->context, db, alias, s->vers_end_field()->field_name)); + if (!expli_start && (res= sl->vers_push_field(thd, impli_table, impli_start))) + goto exit; + if (!expli_end && (res= sl->vers_push_field(thd, impli_table, impli_end))) + goto exit; + + if (impli_table->vers_conditions) + sl->vers_derived_conds= impli_table->vers_conditions; + else if (sl->vers_conditions) + sl->vers_derived_conds= sl->vers_conditions; + else + sl->vers_conditions.import_outer= true; } - } + } // if (sl->table_list.elements > 0) +#pragma GCC diagnostic pop + // System Versioning end } unit->derived= derived; diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 7b61efa8fb8..7c334d7ce67 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -2298,6 +2298,7 @@ void st_select_lex::init_select() join= 0; lock_type= TL_READ_DEFAULT; vers_conditions.empty(); + vers_derived_conds.empty(); } /* @@ -7048,7 +7049,6 @@ bool LEX::sp_add_cfetch(THD *thd, const LEX_STRING &name) bool SELECT_LEX::vers_push_field(THD *thd, TABLE_LIST *table, const char* field_name) { - char buf[MAX_FIELD_NAME]; Item_field *fld= new (thd->mem_root) Item_field(thd, &context, table->db, table->alias, field_name); if (!fld) diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 158130b5ddc..66f943e9f17 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -993,6 +993,7 @@ public: /* System Versioning */ vers_select_conds_t vers_conditions; + vers_select_conds_t vers_derived_conds; bool vers_push_field(THD *thd, TABLE_LIST *table, const char* field_name); void init_query(); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 5ebd3126a23..9ebe9ddf31b 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -771,6 +771,28 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, } } + SELECT_LEX *outer_slex= slex->next_select_in_list(); + if (outer_slex) + { + if (slex->vers_derived_conds) + { + // Propagate derived conditions to outer SELECT_LEX: + if (!outer_slex->vers_conditions) + { + (outer_slex->vers_conditions= slex->vers_derived_conds). + from_inner= true; + } + } + else if (slex->vers_conditions.import_outer) + { + // Propagate query conditions from nearest outer SELECT_LEX: + while (outer_slex && (!outer_slex->vers_conditions || outer_slex->vers_conditions.from_inner)) + outer_slex= outer_slex->next_select_in_list(); + if (outer_slex) + slex->vers_conditions= outer_slex->vers_conditions; + } + } + for (table= tables; table; table= table->next_local) { if (table->table && table->table->versioned()) @@ -1059,7 +1081,7 @@ JOIN::prepare(TABLE_LIST *tables_init, remove_redundant_subquery_clauses(select_lex); } - /* Handle FOR SYSTEM_TIME clause. */ + /* System Versioning: handle FOR SYSTEM_TIME clause. */ if (vers_setup_select(thd, tables_list, &conds, select_lex) < 0) DBUG_RETURN(-1); diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 37703a2bc11..46dd7570874 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -454,6 +454,9 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, } { /* System Versioning begin */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat" +#pragma GCC diagnostic ignored "-Wformat-extra-args" TABLE_LIST *impli_table= NULL, *expli_table= NULL; const char *impli_start, *impli_end; Item_field *expli_start= NULL, *expli_end= NULL; @@ -498,7 +501,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, { if (item->real_item()->type() != Item::FIELD_ITEM) continue; - Item_field *fld= (Item_field*) (item->real_item()); + Item_field *fld= (Item_field*) item->real_item(); if (fld->table_name && 0 != my_strcasecmp(table_alias_charset, table->alias, fld->table_name)) continue; DBUG_ASSERT(fld->field_name); @@ -567,10 +570,15 @@ expli_table_err: if (expli_table) impli_table= expli_table; - if (!expli_start && select_lex->vers_push_field(thd, impli_table, impli_start)) - goto err; - if (!expli_end && select_lex->vers_push_field(thd, impli_table, impli_end)) - goto err; + + if (impli_table) + { + if (!expli_start && select_lex->vers_push_field(thd, impli_table, impli_start)) + goto err; + if (!expli_end && select_lex->vers_push_field(thd, impli_table, impli_end)) + goto err; + } +#pragma GCC diagnostic pop } /* System Versioning end */ view= lex->unlink_first_table(&link_to_local); diff --git a/sql/table.h b/sql/table.h index 68f3a2dfe75..e03ea37226a 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1853,12 +1853,15 @@ struct vers_select_conds_t { vers_range_type_t type; vers_range_unit_t unit; + bool import_outer:1; + bool from_inner:1; Item *start, *end; void empty() { type= FOR_SYSTEM_TIME_UNSPECIFIED; unit= UNIT_TIMESTAMP; + import_outer= from_inner= false; start= end= NULL; } @@ -1872,6 +1875,7 @@ struct vers_select_conds_t unit= u; start= s; end= e; + import_outer= from_inner= false; } bool init_from_sysvar(THD *thd); -- cgit v1.2.1 From 8a11f9b243ad049e8be810b4c898013b22057942 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Sun, 30 Apr 2017 23:12:29 +0300 Subject: SQL: VIEW fix [related to #185] --- sql/sql_derived.cc | 2 +- sql/sql_select.cc | 8 +++++--- sql/table.h | 5 +++-- 3 files changed, 9 insertions(+), 6 deletions(-) (limited to 'sql') diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index f159708b932..de4e49e56a5 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -824,7 +824,7 @@ expli_table_err: if (!expli_end && (res= sl->vers_push_field(thd, impli_table, impli_end))) goto exit; - if (impli_table->vers_conditions) + if (impli_table->vers_conditions && !derived->is_view()) sl->vers_derived_conds= impli_table->vers_conditions; else if (sl->vers_conditions) sl->vers_derived_conds= sl->vers_conditions; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 9ebe9ddf31b..4b01ca1e3f9 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -694,7 +694,6 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, TABLE_LIST *table; int versioned_tables= 0; - int slex_conds_used= 0; if (!thd->stmt_arena->is_conventional() && !thd->stmt_arena->is_stmt_prepare() && !thd->stmt_arena->is_sp_execute()) @@ -789,7 +788,10 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, while (outer_slex && (!outer_slex->vers_conditions || outer_slex->vers_conditions.from_inner)) outer_slex= outer_slex->next_select_in_list(); if (outer_slex) + { slex->vers_conditions= outer_slex->vers_conditions; + outer_slex->vers_conditions.used= true; + } } } @@ -798,7 +800,7 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, if (table->table && table->table->versioned()) { vers_select_conds_t &vers_conditions= !table->vers_conditions? - (++slex_conds_used, slex->vers_conditions) : + (slex->vers_conditions.used= true, slex->vers_conditions) : table->vers_conditions; if (!vers_conditions) @@ -996,7 +998,7 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, } // if (... table->table->versioned()) } // for (table= tables; ...) - if (!slex_conds_used && slex->vers_conditions) + if (!slex->vers_conditions.used && slex->vers_conditions) { my_error(ER_VERS_WRONG_QUERY, MYF(0), "unused `QUERY FOR SYSTEM_TIME` clause!"); DBUG_RETURN(-1); diff --git a/sql/table.h b/sql/table.h index e03ea37226a..32ad43b0ae9 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1855,13 +1855,14 @@ struct vers_select_conds_t vers_range_unit_t unit; bool import_outer:1; bool from_inner:1; + bool used:1; Item *start, *end; void empty() { type= FOR_SYSTEM_TIME_UNSPECIFIED; unit= UNIT_TIMESTAMP; - import_outer= from_inner= false; + import_outer= from_inner= used= false; start= end= NULL; } @@ -1875,7 +1876,7 @@ struct vers_select_conds_t unit= u; start= s; end= e; - import_outer= from_inner= false; + import_outer= from_inner= used= false; } bool init_from_sysvar(THD *thd); -- cgit v1.2.1 From 7e0ff13d7acf796872b7a1d8df1c2fae45beca06 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Sun, 30 Apr 2017 23:51:42 +0300 Subject: SQL: derived fixes [related to #185] --- sql/sql_derived.cc | 6 +++++- sql/sql_select.cc | 12 ++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) (limited to 'sql') diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index de4e49e56a5..d6a00827fef 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -824,8 +824,12 @@ expli_table_err: if (!expli_end && (res= sl->vers_push_field(thd, impli_table, impli_end))) goto exit; - if (impli_table->vers_conditions && !derived->is_view()) + if (impli_table->vers_conditions) + { sl->vers_derived_conds= impli_table->vers_conditions; + if (derived->is_view() && !sl->vers_conditions) + sl->vers_conditions.import_outer= true; + } else if (sl->vers_conditions) sl->vers_derived_conds= sl->vers_conditions; else diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 4b01ca1e3f9..2355552ab77 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -771,6 +771,7 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, } SELECT_LEX *outer_slex= slex->next_select_in_list(); + bool use_slex_conds= false; if (outer_slex) { if (slex->vers_derived_conds) @@ -778,11 +779,12 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, // Propagate derived conditions to outer SELECT_LEX: if (!outer_slex->vers_conditions) { - (outer_slex->vers_conditions= slex->vers_derived_conds). - from_inner= true; + outer_slex->vers_conditions= slex->vers_derived_conds; + outer_slex->vers_conditions.from_inner= true; + outer_slex->vers_conditions.used= true; } } - else if (slex->vers_conditions.import_outer) + if (slex->vers_conditions.import_outer) { // Propagate query conditions from nearest outer SELECT_LEX: while (outer_slex && (!outer_slex->vers_conditions || outer_slex->vers_conditions.from_inner)) @@ -791,6 +793,8 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, { slex->vers_conditions= outer_slex->vers_conditions; outer_slex->vers_conditions.used= true; + DBUG_ASSERT(slex->master_unit()->derived); + use_slex_conds= slex->master_unit()->derived->is_view(); } } } @@ -799,7 +803,7 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, { if (table->table && table->table->versioned()) { - vers_select_conds_t &vers_conditions= !table->vers_conditions? + vers_select_conds_t &vers_conditions= use_slex_conds || !table->vers_conditions? (slex->vers_conditions.used= true, slex->vers_conditions) : table->vers_conditions; -- cgit v1.2.1 From 44506f26696ecae268b3056edb46d247fcc6b919 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Wed, 3 May 2017 06:15:20 +0300 Subject: SQL: vers_ sysvars renamed to versioning_ --- sql/sys_vars.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'sql') diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index f1a674388fc..f1f8f56a546 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -383,17 +383,17 @@ static Sys_var_charptr Sys_basedir( IN_FS_CHARSET, DEFAULT(0)); static Sys_var_vers_asof Sys_vers_current_time( - "vers_current_time", "Default AS OF value for versioned tables", + "versioning_current_timestamp", "Default AS OF value for versioned tables", SESSION_VAR(vers_current_time), CMD_LINE(REQUIRED_ARG, OPT_VERS_CURRENT_TIME), IN_FS_CHARSET, DEFAULT("now")); static Sys_var_mybool Sys_vers_force( - "vers_force", "Force system versioning for all created tables", + "versioning_force", "Force system versioning for all created tables", SESSION_VAR(vers_force), CMD_LINE(OPT_ARG), DEFAULT(FALSE)); static const char *vers_hide_keywords[]= {"AUTO", "IMPLICIT", "FULL", "NEVER", NullS}; static Sys_var_enum Sys_vers_hide( - "vers_hide", "Hide system versioning from being displayed in table info. " + "versioning_hide", "Hide system versioning from being displayed in table info. " "AUTO: hide implicit system fields only in non-versioned and AS OF queries; " "IMPLICIT: hide implicit system fields in all queries; " "FULL: hide any system fields in all queries and hide versioning info in SHOW commands; " @@ -401,7 +401,7 @@ static Sys_var_enum Sys_vers_hide( SESSION_VAR(vers_hide), CMD_LINE(OPT_ARG), vers_hide_keywords, DEFAULT(VERS_HIDE_AUTO)); static Sys_var_mybool Sys_vers_innodb_algorithm_simple( - "vers_innodb_algorithm_simple", + "versioning_innodb_algorithm_simple", "Use simple algorithm of timestamp handling in InnoDB instead of TRX_SEES", SESSION_VAR(vers_innodb_algorithm_simple), CMD_LINE(OPT_ARG), DEFAULT(TRUE)); -- cgit v1.2.1 From f94fd4b730a35183c63c7640606fede20a60e6e4 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Wed, 3 May 2017 12:09:17 +0300 Subject: Style: warning fix --- sql/sql_base.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/sql_base.cc b/sql/sql_base.cc index bf6f2c3931e..8cdc835c0df 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -7598,7 +7598,7 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, if ((sys_field && (thd->lex->sql_command == SQLCOM_CREATE_VIEW || slex->nest_level > 0 || - vers_hide == VERS_HIDE_FULL && thd->lex->sql_command != SQLCOM_CREATE_TABLE)) || + (vers_hide == VERS_HIDE_FULL && thd->lex->sql_command != SQLCOM_CREATE_TABLE))) || ((fl & HIDDEN_FLAG) && ( !sys_field || vers_hide == VERS_HIDE_IMPLICIT || -- cgit v1.2.1 From 018587244977235d9fa2283bde7533eec455a285 Mon Sep 17 00:00:00 2001 From: kevg Date: Mon, 24 Apr 2017 14:47:44 +0300 Subject: SQL: versioning DDL part I [closes #172] --- sql/sql_class.h | 1 + sql/sql_table.cc | 169 ++++++++++++++++++++++++++++++++++++++++++++++++++----- sql/sys_vars.cc | 4 ++ 3 files changed, 159 insertions(+), 15 deletions(-) (limited to 'sql') diff --git a/sql/sql_class.h b/sql/sql_class.h index b76b5cd6e60..e9a0f192c5f 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -711,6 +711,7 @@ typedef struct system_variables my_bool vers_force; ulong vers_hide; my_bool vers_innodb_algorithm_simple; + my_bool vers_ddl_survival; } SV; /** diff --git a/sql/sql_table.cc b/sql/sql_table.cc index cb6b18eee4c..a5afcfbd9a5 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -5155,6 +5155,85 @@ static void make_unique_constraint_name(THD *thd, LEX_STRING *name, ** Alter a table definition ****************************************************************************/ +// Works as NOW(6) +static MYSQL_TIME vers_thd_get_now(THD *thd) +{ + MYSQL_TIME now; + thd->variables.time_zone->gmt_sec_to_TIME(&now, thd->query_start()); + now.second_part= thd->query_start_sec_part(); + thd->time_zone_used= 1; + return now; +} + +static void vers_table_name_date(THD *thd, const char *table_name, + char *new_name, size_t new_name_size) +{ + const MYSQL_TIME now= vers_thd_get_now(thd); + my_snprintf(new_name, new_name_size, "%s_%04d%02d%02d_%02d%02d%02d_%06d", + table_name, now.year, now.month, now.day, now.hour, now.minute, + now.second, now.second_part); +} + +bool operator!=(const MYSQL_TIME &lhs, const MYSQL_TIME &rhs) +{ + return lhs.year != rhs.year || lhs.month != rhs.month || lhs.day != rhs.day || + lhs.hour != rhs.hour || lhs.minute != rhs.minute || + lhs.second_part != rhs.second_part || lhs.neg != rhs.neg || + lhs.time_type != rhs.time_type; +} + +// Sets sys_trx_end=MAX for rows with sys_trx_end=now(6) +static bool vers_reset_alter_copy(THD *thd, TABLE *table) +{ + const MYSQL_TIME now= vers_thd_get_now(thd); + + READ_RECORD info; + int error= 0; + bool will_batch= false; + uint dup_key_found= 0; + if (init_read_record(&info, thd, table, NULL, NULL, 0, 1, true)) + goto err; + + will_batch= !table->file->start_bulk_update(); + + while (!(error= info.read_record(&info))) + { + MYSQL_TIME current; + if (table->vers_end_field()->get_date(¤t, 0)) + goto err_read_record; + if (current != now) + { + continue; + } + + store_record(table, record[1]); + table->vers_end_field()->set_max(); + if (will_batch) + error= table->file->ha_bulk_update_row(table->record[1], table->record[0], + &dup_key_found); + else + error= table->file->ha_update_row(table->record[1], table->record[0]); + if (error && table->file->is_fatal_error(error, HA_CHECK_ALL)) + { + table->file->print_error(error, MYF(ME_FATALERROR)); + goto err_read_record; + } + } + + if (will_batch && (error= table->file->exec_bulk_update(&dup_key_found))) + table->file->print_error(error, MYF(ME_FATALERROR)); + if (will_batch) + table->file->end_bulk_update(); + +err_read_record: + end_read_record(&info); + +err: + if (table->file->ha_external_lock(thd, F_UNLCK)) + return true; + + return error ? true : false; +} /** Rename a table. @@ -8602,6 +8681,26 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, bool error= open_tables(thd, &table_list, &tables_opened, 0, &alter_prelocking_strategy); thd->open_options&= ~HA_OPEN_FOR_ALTER; + bool versioned= table_list->table && table_list->table->versioned(); + if (versioned && thd->variables.vers_ddl_survival) + { + table_list->set_lock_type(thd, TL_WRITE); + if (thd->mdl_context.upgrade_shared_lock(table_list->table->mdl_ticket, + MDL_EXCLUSIVE, + thd->variables.lock_wait_timeout)) + { + DBUG_RETURN(true); + } + + if (table_list->table->versioned_by_engine() && + alter_info->requested_algorithm == + Alter_info::ALTER_TABLE_ALGORITHM_DEFAULT && + !table_list->table->s->partition_info_str) + { + // Changle default ALGORITHM to COPY for INNODB + alter_info->requested_algorithm= Alter_info::ALTER_TABLE_ALGORITHM_COPY; + } + } DEBUG_SYNC(thd, "alter_opened_table"); @@ -8914,9 +9013,11 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, Upgrade from MDL_SHARED_UPGRADABLE to MDL_SHARED_NO_WRITE. Afterwards it's safe to take the table level lock. */ - if (thd->mdl_context.upgrade_shared_lock(mdl_ticket, MDL_SHARED_NO_WRITE, - thd->variables.lock_wait_timeout) - || lock_tables(thd, table_list, alter_ctx.tables_opened, 0)) + if ((!(versioned && thd->variables.vers_ddl_survival) && + thd->mdl_context.upgrade_shared_lock( + mdl_ticket, MDL_SHARED_NO_WRITE, + thd->variables.lock_wait_timeout)) || + lock_tables(thd, table_list, alter_ctx.tables_opened, 0)) { DBUG_RETURN(true); } @@ -8981,6 +9082,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, handlerton *new_db_type= create_info->db_type; handlerton *old_db_type= table->s->db_type(); TABLE *new_table= NULL; + bool new_versioned= false; ha_rows copied=0,deleted=0; /* @@ -9315,6 +9417,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, } if (!new_table) goto err_new_table_cleanup; + new_versioned= new_table->versioned(); /* Note: In case of MERGE table, we do not attach children. We do not copy data for MERGE tables. Only the children have data. @@ -9341,7 +9444,18 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, order_num, order, &copied, &deleted, alter_info->keys_onoff, &alter_ctx)) + { + if (versioned && new_versioned && thd->variables.vers_ddl_survival) + { + if (table->versioned_by_sql()) + { + // Failure of this function may result in corruption of + // an original table. + vers_reset_alter_copy(thd, table); + } + } goto err_new_table_cleanup; + } } else { @@ -9436,9 +9550,14 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, Rename the old table to temporary name to have a backup in case anything goes wrong while renaming the new table. */ - char backup_name[32]; - my_snprintf(backup_name, sizeof(backup_name), "%s2-%lx-%lx", tmp_file_prefix, - current_pid, thd->thread_id); + char backup_name[FN_LEN]; + if (versioned && thd->variables.vers_ddl_survival) + vers_table_name_date(thd, alter_ctx.table_name, backup_name, + sizeof(backup_name)); + else + my_snprintf(backup_name, sizeof(backup_name), "%s2-%lx-%lx", + tmp_file_prefix, current_pid, thd->thread_id); + if (lower_case_table_names) my_casedn_str(files_charset_info, backup_name); if (mysql_rename_table(old_db_type, alter_ctx.db, alter_ctx.table_name, @@ -9490,7 +9609,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, } // ALTER TABLE succeeded, delete the backup of the old table. - if (quick_rm_table(thd, old_db_type, alter_ctx.db, backup_name, FN_IS_TMP)) + if (!(versioned && new_versioned && thd->variables.vers_ddl_survival) && + quick_rm_table(thd, old_db_type, alter_ctx.db, backup_name, FN_IS_TMP)) { /* The fact that deletion of the backup failed is not critical @@ -9674,7 +9794,8 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, Field **dfield_ptr= to->default_field; bool make_versioned= !from->versioned() && to->versioned(); bool make_unversioned= from->versioned() && !to->versioned(); - Field *to_sys_trx_start= NULL, *from_sys_trx_end= NULL, *to_sys_trx_end= NULL; + bool keep_versioned= from->versioned() && to->versioned(); + Field *to_sys_trx_start= NULL, *to_sys_trx_end= NULL, *from_sys_trx_end= NULL; MYSQL_TIME now; DBUG_ENTER("copy_data_between_tables"); @@ -9777,19 +9898,26 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, if (make_versioned) { - thd->variables.time_zone->gmt_sec_to_TIME(&now, thd->query_start()); - now.second_part= thd->query_start_sec_part(); - thd->time_zone_used= 1; - to_sys_trx_start= to->field[to->s->row_start_field]; - to_sys_trx_end= to->field[to->s->row_end_field]; + now= vers_thd_get_now(thd); + to_sys_trx_start= to->vers_start_field(); + to_sys_trx_end= to->vers_end_field(); } else if (make_unversioned) { - from_sys_trx_end= from->field[from->s->row_end_field]; + from_sys_trx_end= from->vers_end_field(); } - else if (from->versioned() && to->versioned()) + else if (keep_versioned) { to->file->vers_auto_decrement= 0xffffffffffffffff; + if (thd->variables.vers_ddl_survival) + { + thd->variables.time_zone->gmt_sec_to_TIME(&now, thd->query_start()); + now.second_part= thd->query_start_sec_part(); + thd->time_zone_used= 1; + + from_sys_trx_end= from->vers_end_field(); + to_sys_trx_start= to->vers_start_field(); + } } THD_STAGE_INFO(thd, stage_copy_to_tmp_table); @@ -9863,6 +9991,17 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, if (!from_sys_trx_end->is_max()) continue; } + else if (keep_versioned && thd->variables.vers_ddl_survival) + { + // Do not copy history rows. + if (!from_sys_trx_end->is_max()) + continue; + + store_record(from, record[1]); + from->vers_end_field()->store_time(&now); + from->file->ha_update_row(from->record[1], from->record[0]); + to_sys_trx_start->store_time(&now); + } prev_insert_id= to->file->next_insert_id; if (to->default_field) diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index f1f8f56a546..c618a4f4bc2 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -406,6 +406,10 @@ static Sys_var_mybool Sys_vers_innodb_algorithm_simple( SESSION_VAR(vers_innodb_algorithm_simple), CMD_LINE(OPT_ARG), DEFAULT(TRUE)); +static Sys_var_mybool Sys_vers_ddl_survival( + "versioning_ddl_survival", "Use system versioning DDL survival feature", + SESSION_VAR(vers_ddl_survival), CMD_LINE(OPT_ARG), DEFAULT(FALSE)); + static Sys_var_ulonglong Sys_binlog_cache_size( "binlog_cache_size", "The size of the transactional cache for " "updates to transactional engines for the binary log. " -- cgit v1.2.1 From f751b30884c09be2f2ff6282129f8c023f44d265 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Wed, 3 May 2017 14:28:15 +0300 Subject: Style: comment fixes --- sql/sql_table.cc | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'sql') diff --git a/sql/sql_table.cc b/sql/sql_table.cc index a5afcfbd9a5..268c8bd7ac2 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -9449,8 +9449,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, { if (table->versioned_by_sql()) { - // Failure of this function may result in corruption of - // an original table. + // Failure of this function may result in corruption of an original table. vers_reset_alter_copy(thd, table); } } @@ -9987,15 +9986,13 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, } else if (make_unversioned) { - // Drop history rows. if (!from_sys_trx_end->is_max()) - continue; + continue; // Drop history rows. } else if (keep_versioned && thd->variables.vers_ddl_survival) { - // Do not copy history rows. if (!from_sys_trx_end->is_max()) - continue; + continue; // Do not copy history rows. store_record(from, record[1]); from->vers_end_field()->store_time(&now); -- cgit v1.2.1 From ec0002e908c3f8f946a71fa07adfee7d106961b2 Mon Sep 17 00:00:00 2001 From: kevg Date: Thu, 18 May 2017 16:44:29 +0300 Subject: Parser: useful attributes for AS ROW fields --- sql/sql_yacc.yy | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'sql') diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 4abe1dbc24a..8f02a033210 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -6262,6 +6262,15 @@ opt_serial_attribute_list: | serial_attribute ; +opt_asrow_attribute: + /* empty */ {} + | opt_asrow_attribute_list {} + ; + +opt_asrow_attribute_list: + opt_asrow_attribute_list asrow_attribute {} + | asrow_attribute + ; field_def: opt_attribute @@ -6271,7 +6280,7 @@ field_def: Lex->last_field->flags&= ~NOT_NULL_FLAG; // undo automatic NOT NULL for timestamps } vcol_opt_specifier vcol_opt_attribute - | opt_generated_always AS ROW_SYM start_or_end + | opt_generated_always AS ROW_SYM start_or_end opt_asrow_attribute { LEX *lex= Lex; Vers_parse_info &info= lex->vers_get_info(); @@ -6743,7 +6752,7 @@ attribute: | serial_attribute ; -serial_attribute: +asrow_attribute: not NULL_SYM { Lex->last_field->flags|= NOT_NULL_FLAG; @@ -6768,6 +6777,10 @@ serial_attribute: lex->alter_info.flags|= Alter_info::ALTER_ADD_INDEX; } | COMMENT_SYM TEXT_STRING_sys { Lex->last_field->comment= $2; } + ; + +serial_attribute: + asrow_attribute | IDENT_sys equal TEXT_STRING_sys { if ($3.length > ENGINE_OPTION_MAX_LENGTH) -- cgit v1.2.1 From abba11e6c4bd992c6c380ad73dfde1f7f4d438d7 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Fri, 19 May 2017 01:40:11 +0300 Subject: SQL: fix fix_create_like() --- sql/handler.cc | 43 ++++++++++++++++++++++++++++++------------- sql/handler.h | 2 +- sql/sql_table.cc | 2 +- 3 files changed, 32 insertions(+), 15 deletions(-) (limited to 'sql') diff --git a/sql/handler.cc b/sql/handler.cc index b66e616dfda..1d21d499dde 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -6926,28 +6926,45 @@ bool Vers_parse_info::check_and_fix_alter(THD *thd, Alter_info *alter_info, } bool Vers_parse_info::fix_create_like(THD *thd, Alter_info *alter_info, - HA_CREATE_INFO *create_info) + HA_CREATE_INFO *create_info, TABLE_LIST *table) { List_iterator it(alter_info->create_list); - Create_field *f= NULL; + Create_field *f, *f_start=NULL, *f_end= NULL; DBUG_ASSERT(alter_info->create_list.elements > 2); - for (uint i= 0; i < alter_info->create_list.elements - 1; ++i) - f= it++; - DBUG_ASSERT(f->flags & VERS_SYS_START_FLAG); - if (create_string(thd->mem_root, &generated_as_row.start, f->field_name) || - create_string(thd->mem_root, &period_for_system_time.start, - f->field_name)) + while ((f= it++)) + { + if (f->flags & VERS_SYS_START_FLAG) + { + f_start= f; + if (f_end) + break; + } + else if (f->flags & VERS_SYS_END_FLAG) + { + f_end= f; + if (f_start) + break; + } + } + + if (!f_start || !f_end) + { + my_error(ER_VERS_WRONG_PARAMS, MYF(0), table->table_name, + "Missed one of system versioning fields from source"); return true; + } - f= it++; - DBUG_ASSERT(f->flags & VERS_SYS_END_FLAG); - if (create_string(thd->mem_root, &generated_as_row.end, f->field_name) || - create_string(thd->mem_root, &period_for_system_time.end, f->field_name)) + if (create_string(thd->mem_root, &generated_as_row.start, f_start->field_name) || + create_string(thd->mem_root, &period_for_system_time.start, f_start->field_name) || + create_string(thd->mem_root, &generated_as_row.end, f_end->field_name) || + create_string(thd->mem_root, &period_for_system_time.end, f_end->field_name)) + { + sql_print_error("Failed to allocate memory for Vers_parse_info::fix_create_like()"); return true; + } create_info->options|= HA_VERSIONED_TABLE; - return false; } diff --git a/sql/handler.h b/sql/handler.h index 76d6bae734f..c46ee422565 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -1728,7 +1728,7 @@ public: bool check_and_fix_alter(THD *thd, Alter_info *alter_info, HA_CREATE_INFO *create_info, TABLE_SHARE *share); bool fix_create_like(THD *thd, Alter_info *alter_info, - HA_CREATE_INFO *create_info); + HA_CREATE_INFO *create_info, TABLE_LIST *table); /** User has added 'WITH SYSTEM VERSIONING' to table definition */ bool declared_with_system_versioning : 1; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 268c8bd7ac2..bbfe78ac4b2 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -5459,7 +5459,7 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, if (src_table->table->versioned() && local_create_info.vers_info.fix_create_like(thd, &local_alter_info, - &local_create_info)) + &local_create_info, src_table)) { goto err; } -- cgit v1.2.1 From 84b4baef93d125d3afcf49bec96849f2eeca23ec Mon Sep 17 00:00:00 2001 From: Eugene Kosov Date: Mon, 22 May 2017 23:40:48 +0300 Subject: SQL: SHOW CREATE for GENERATED ALWAYS AS ROW Test is main.mysqldump --- sql/sql_show.cc | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'sql') diff --git a/sql/sql_show.cc b/sql/sql_show.cc index a3ce1fd19c6..f6e046a8d34 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2112,6 +2112,15 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, } else { + if (field->flags & VERS_SYS_START_FLAG) + { + packet->append(STRING_WITH_LEN(" GENERATED ALWAYS AS ROW START")); + } + else if (field->flags & VERS_SYS_END_FLAG) + { + packet->append(STRING_WITH_LEN(" GENERATED ALWAYS AS ROW END")); + } + if (flags & NOT_NULL_FLAG) packet->append(STRING_WITH_LEN(" NOT NULL")); else if (field->type() == MYSQL_TYPE_TIMESTAMP && !field->vers_sys_field()) @@ -2129,14 +2138,6 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, packet->append(STRING_WITH_LEN(" DEFAULT ")); packet->append(def_value.ptr(), def_value.length(), system_charset_info); } - else if (field->flags & VERS_SYS_START_FLAG) - { - packet->append(STRING_WITH_LEN(" GENERATED ALWAYS AS ROW START")); - } - else if (field->flags & VERS_SYS_END_FLAG) - { - packet->append(STRING_WITH_LEN(" GENERATED ALWAYS AS ROW END")); - } if (field->flags & VERS_OPTIMIZED_UPDATE_FLAG) { -- cgit v1.2.1 From 414651c80a64d93ece5a3f03bfb186fe52cca6f0 Mon Sep 17 00:00:00 2001 From: Eugene Kosov Date: Tue, 23 May 2017 11:15:44 +0300 Subject: SQL: ALTER ADD COLUMN order fix --- sql/handler.cc | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) (limited to 'sql') diff --git a/sql/handler.cc b/sql/handler.cc index 1d21d499dde..f2e74d936fd 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -6874,31 +6874,11 @@ bool Vers_parse_info::check_and_fix_alter(THD *thd, Alter_info *alter_info, if (alter_info->create_list.elements) { - DBUG_ASSERT(share->fields > 2); - const char *after_this= share->field[share->fields - 3]->field_name; - List_iterator it(alter_info->create_list); + List_iterator_fast it(alter_info->create_list); while (Create_field *f= it++) { if (f->versioning == Column_definition::WITHOUT_VERSIONING) f->flags|= VERS_OPTIMIZED_UPDATE_FLAG; - - if (f->change) - continue; - - if (f->after) - { - if (is_trx_start(f->after) || is_trx_end(f->after)) - { - my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, - "Can not put new field after system versioning field"); - return true; - } - - continue; - } - - // TODO: ALTER_COLUMN_ORDER? - f->after= after_this; } } -- cgit v1.2.1 From 397a891538eab4310b6f45319783a1c5c53977c7 Mon Sep 17 00:00:00 2001 From: Eugene Kosov Date: Thu, 25 May 2017 15:19:49 +0300 Subject: SQL: minor cleanup in mysql_alter_table() --- sql/sql_table.cc | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'sql') diff --git a/sql/sql_table.cc b/sql/sql_table.cc index bbfe78ac4b2..a1140b1c6bc 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -9445,13 +9445,11 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, alter_info->keys_onoff, &alter_ctx)) { - if (versioned && new_versioned && thd->variables.vers_ddl_survival) + if (table->versioned_by_sql() && new_versioned && + thd->variables.vers_ddl_survival) { - if (table->versioned_by_sql()) - { - // Failure of this function may result in corruption of an original table. - vers_reset_alter_copy(thd, table); - } + // Failure of this function may result in corruption of an original table. + vers_reset_alter_copy(thd, table); } goto err_new_table_cleanup; } -- cgit v1.2.1 From f915fe8eaeed8e35ee418c6692dbe1873ffb586f Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Fri, 2 Jun 2017 16:53:24 +0300 Subject: SQL: renamed vers_thd_get_now() to THD::query_start_TIME() --- sql/sql_class.cc | 9 +++++++++ sql/sql_class.h | 1 + sql/sql_table.cc | 31 +++++++++---------------------- 3 files changed, 19 insertions(+), 22 deletions(-) (limited to 'sql') diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 052e5ae136d..4226da573cb 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -6859,6 +6859,15 @@ static bool protect_against_unsafe_warning_flood(int unsafe_type) DBUG_RETURN(unsafe_warning_suppression_active[unsafe_type]); } +MYSQL_TIME THD::query_start_TIME() +{ + MYSQL_TIME res; + variables.time_zone->gmt_sec_to_TIME(&res, query_start()); + res.second_part= query_start_sec_part(); + time_zone_used= 1; + return res; +} + /** Auxiliary method used by @c binlog_query() to raise warnings. diff --git a/sql/sql_class.h b/sql/sql_class.h index e9a0f192c5f..d024a27c2cd 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -3244,6 +3244,7 @@ public: inline my_time_t query_start() { query_start_used=1; return start_time; } inline ulong query_start_sec_part() { query_start_sec_part_used=1; return start_time_sec_part; } + MYSQL_TIME query_start_TIME(); inline void set_current_time() { my_hrtime_t hrtime= my_hrtime(); diff --git a/sql/sql_table.cc b/sql/sql_table.cc index a1140b1c6bc..efb2969f548 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -5155,20 +5155,10 @@ static void make_unique_constraint_name(THD *thd, LEX_STRING *name, ** Alter a table definition ****************************************************************************/ -// Works as NOW(6) -static MYSQL_TIME vers_thd_get_now(THD *thd) -{ - MYSQL_TIME now; - thd->variables.time_zone->gmt_sec_to_TIME(&now, thd->query_start()); - now.second_part= thd->query_start_sec_part(); - thd->time_zone_used= 1; - return now; -} - static void vers_table_name_date(THD *thd, const char *table_name, char *new_name, size_t new_name_size) { - const MYSQL_TIME now= vers_thd_get_now(thd); + const MYSQL_TIME now= thd->query_start_TIME(); my_snprintf(new_name, new_name_size, "%s_%04d%02d%02d_%02d%02d%02d_%06d", table_name, now.year, now.month, now.day, now.hour, now.minute, now.second, now.second_part); @@ -5185,7 +5175,7 @@ bool operator!=(const MYSQL_TIME &lhs, const MYSQL_TIME &rhs) // Sets sys_trx_end=MAX for rows with sys_trx_end=now(6) static bool vers_reset_alter_copy(THD *thd, TABLE *table) { - const MYSQL_TIME now= vers_thd_get_now(thd); + const MYSQL_TIME query_start= thd->query_start_TIME(); READ_RECORD info; int error= 0; @@ -5201,7 +5191,7 @@ static bool vers_reset_alter_copy(THD *thd, TABLE *table) MYSQL_TIME current; if (table->vers_end_field()->get_date(¤t, 0)) goto err_read_record; - if (current != now) + if (current != query_start) { continue; } @@ -9793,7 +9783,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, bool make_unversioned= from->versioned() && !to->versioned(); bool keep_versioned= from->versioned() && to->versioned(); Field *to_sys_trx_start= NULL, *to_sys_trx_end= NULL, *from_sys_trx_end= NULL; - MYSQL_TIME now; + MYSQL_TIME query_start; DBUG_ENTER("copy_data_between_tables"); /* Two or 3 stages; Sorting, copying data and update indexes */ @@ -9895,7 +9885,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, if (make_versioned) { - now= vers_thd_get_now(thd); + query_start= thd->query_start_TIME(); to_sys_trx_start= to->vers_start_field(); to_sys_trx_end= to->vers_end_field(); } @@ -9908,10 +9898,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, to->file->vers_auto_decrement= 0xffffffffffffffff; if (thd->variables.vers_ddl_survival) { - thd->variables.time_zone->gmt_sec_to_TIME(&now, thd->query_start()); - now.second_part= thd->query_start_sec_part(); - thd->time_zone_used= 1; - + query_start= thd->query_start_TIME(); from_sys_trx_end= from->vers_end_field(); to_sys_trx_start= to->vers_start_field(); } @@ -9975,7 +9962,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, { to_sys_trx_start->set_notnull(to_sys_trx_start->null_offset()); // TODO: write directly to record bypassing the same checks on every call - to_sys_trx_start->store_time(&now); + to_sys_trx_start->store_time(&query_start); static const timeval max_tv= {0x7fffffff, 0}; static const uint dec= 6; @@ -9993,9 +9980,9 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, continue; // Do not copy history rows. store_record(from, record[1]); - from->vers_end_field()->store_time(&now); + from->vers_end_field()->store_time(&query_start); from->file->ha_update_row(from->record[1], from->record[0]); - to_sys_trx_start->store_time(&now); + to_sys_trx_start->store_time(&query_start); } prev_insert_id= to->file->next_insert_id; -- cgit v1.2.1 From efaa0d66dafc44d994054c7d6ff160cb295a0bf6 Mon Sep 17 00:00:00 2001 From: Eugene Kosov Date: Tue, 13 Jun 2017 10:07:16 +0300 Subject: Cleanup: stale sql/field.h.orig --- sql/field.h.orig | 4160 ------------------------------------------------------ 1 file changed, 4160 deletions(-) delete mode 100644 sql/field.h.orig (limited to 'sql') diff --git a/sql/field.h.orig b/sql/field.h.orig deleted file mode 100644 index 03d9ba06ac4..00000000000 --- a/sql/field.h.orig +++ /dev/null @@ -1,4160 +0,0 @@ -#ifndef FIELD_INCLUDED -#define FIELD_INCLUDED -/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. - Copyright (c) 2008, 2017, MariaDB Corporation. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - -/* - Because of the function make_new_field() all field classes that have static - variables must declare the size_of() member function. -*/ - -#ifdef USE_PRAGMA_INTERFACE -#pragma interface /* gcc class implementation */ -#endif - -#include "mysqld.h" /* system_charset_info */ -#include "table.h" /* TABLE */ -#include "sql_string.h" /* String */ -#include "my_decimal.h" /* my_decimal */ -#include "sql_error.h" /* Sql_condition */ -#include "compat56.h" -#include "sql_type.h" /* Type_std_attributes */ - -class Send_field; -class Copy_field; -class Protocol; -class Create_field; -class Relay_log_info; -class Field; -class Column_statistics; -class Column_statistics_collected; -class Item_func; -class Item_bool_func; -class Item_equal; - -enum enum_check_fields -{ - CHECK_FIELD_IGNORE, - CHECK_FIELD_WARN, - CHECK_FIELD_ERROR_FOR_NULL -}; - -/* - Common declarations for Field and Item -*/ -class Value_source -{ -protected: - - // Parameters for warning and note generation - class Warn_filter - { - bool m_want_warning_edom; - bool m_want_note_truncated_spaces; - public: - Warn_filter(bool want_warning_edom, bool want_note_truncated_spaces) : - m_want_warning_edom(want_warning_edom), - m_want_note_truncated_spaces(want_note_truncated_spaces) - { } - Warn_filter(const THD *thd); - bool want_warning_edom() const - { return m_want_warning_edom; } - bool want_note_truncated_spaces() const - { return m_want_note_truncated_spaces; } - }; - class Warn_filter_all: public Warn_filter - { - public: - Warn_filter_all() :Warn_filter(true, true) { } - }; - - class Converter_double_to_longlong - { - protected: - bool m_error; - longlong m_result; - public: - Converter_double_to_longlong(double nr, bool unsigned_flag); - longlong result() const { return m_result; } - bool error() const { return m_error; } - void push_warning(THD *thd, double nr, bool unsigned_flag); - }; - class Converter_double_to_longlong_with_warn: - public Converter_double_to_longlong - { - public: - Converter_double_to_longlong_with_warn(THD *thd, double nr, - bool unsigned_flag) - :Converter_double_to_longlong(nr, unsigned_flag) - { - if (m_error) - push_warning(thd, nr, unsigned_flag); - } - Converter_double_to_longlong_with_warn(double nr, bool unsigned_flag) - :Converter_double_to_longlong(nr, unsigned_flag) - { - if (m_error) - push_warning(current_thd, nr, unsigned_flag); - } - }; - - // String-to-number converters - class Converter_string_to_number - { - protected: - char *m_end_of_num; // Where the low-level conversion routine stopped - int m_error; // The error code returned by the low-level routine - bool m_edom; // If EDOM-alike error happened during conversion - /** - Check string-to-number conversion and produce a warning if - - could not convert any digits (EDOM-alike error) - - found garbage at the end of the string - - found extra spaces at the end (a note) - See also Field_num::check_edom_and_truncation() for a similar function. - - @param thd - the thread that will be used to generate warnings. - Can be NULL (which means current_thd will be used - if a warning is really necessary). - @param type - name of the data type - (e.g. "INTEGER", "DECIMAL", "DOUBLE") - @param cs - character set of the original string - @param str - the original string - @param end - the end of the string - @param allow_notes - tells if trailing space notes should be displayed - or suppressed. - - Unlike Field_num::check_edom_and_truncation(), this function does not - distinguish between EDOM and truncation and reports the same warning for - both cases. Perhaps we should eventually print different warnings, - to make the explicit CAST work closer to the implicit cast in - Field_xxx::store(). - */ - void check_edom_and_truncation(THD *thd, Warn_filter filter, - const char *type, - CHARSET_INFO *cs, - const char *str, - size_t length) const; - public: - int error() const { return m_error; } - }; - - class Converter_strntod: public Converter_string_to_number - { - double m_result; - public: - Converter_strntod(CHARSET_INFO *cs, const char *str, size_t length) - { - m_result= my_strntod(cs, (char *) str, length, &m_end_of_num, &m_error); - // strntod() does not set an error if the input string was empty - m_edom= m_error !=0 || str == m_end_of_num; - } - double result() const { return m_result; } - }; - - class Converter_string_to_longlong: public Converter_string_to_number - { - protected: - longlong m_result; - public: - longlong result() const { return m_result; } - }; - - class Converter_strntoll: public Converter_string_to_longlong - { - public: - Converter_strntoll(CHARSET_INFO *cs, const char *str, size_t length) - { - m_result= my_strntoll(cs, str, length, 10, &m_end_of_num, &m_error); - /* - All non-zero errors means EDOM error. - strntoll() does not set an error if the input string was empty. - Check it here. - Notice the different with the same condition in Converter_strntoll10. - */ - m_edom= m_error != 0 || str == m_end_of_num; - } - }; - - class Converter_strtoll10: public Converter_string_to_longlong - { - public: - Converter_strtoll10(CHARSET_INFO *cs, const char *str, size_t length) - { - m_end_of_num= (char *) str + length; - m_result= (*(cs->cset->strtoll10))(cs, str, &m_end_of_num, &m_error); - /* - Negative error means "good negative number". - Only a positive m_error value means a real error. - strtoll10() sets error to MY_ERRNO_EDOM in case of an empty string, - so we don't have to additionally catch empty strings here. - */ - m_edom= m_error > 0; - } - }; - - class Converter_str2my_decimal: public Converter_string_to_number - { - public: - Converter_str2my_decimal(uint mask, - CHARSET_INFO *cs, const char *str, size_t length, - my_decimal *buf) - { - m_error= str2my_decimal(mask, str, length, cs, - buf, (const char **) &m_end_of_num); - // E_DEC_TRUNCATED means a very minor truncation: '1e-100' -> 0 - m_edom= m_error && m_error != E_DEC_TRUNCATED; - } - }; - - - // String-to-number converters with automatic warning generation - class Converter_strntod_with_warn: public Converter_strntod - { - public: - Converter_strntod_with_warn(THD *thd, Warn_filter filter, - CHARSET_INFO *cs, - const char *str, size_t length) - :Converter_strntod(cs, str, length) - { - check_edom_and_truncation(thd, filter, "DOUBLE", cs, str, length); - } - }; - - class Converter_strntoll_with_warn: public Converter_strntoll - { - public: - Converter_strntoll_with_warn(THD *thd, Warn_filter filter, - CHARSET_INFO *cs, - const char *str, size_t length) - :Converter_strntoll(cs, str, length) - { - check_edom_and_truncation(thd, filter, "INTEGER", cs, str, length); - } - }; - - class Converter_strtoll10_with_warn: public Converter_strtoll10 - { - public: - Converter_strtoll10_with_warn(THD *thd, Warn_filter filter, - CHARSET_INFO *cs, - const char *str, size_t length) - :Converter_strtoll10(cs, str, length) - { - check_edom_and_truncation(thd, filter, "INTEGER", cs, str, length); - } - }; - - class Converter_str2my_decimal_with_warn: public Converter_str2my_decimal - { - public: - Converter_str2my_decimal_with_warn(THD *thd, Warn_filter filter, - uint mask, CHARSET_INFO *cs, - const char *str, size_t length, - my_decimal *buf) - :Converter_str2my_decimal(mask, cs, str, length, buf) - { - check_edom_and_truncation(thd, filter, "DECIMAL", cs, str, length); - } - }; - - - // String-to-number convertion methods for the old code compatibility - longlong longlong_from_string_with_check(CHARSET_INFO *cs, const char *cptr, - const char *end) const - { - /* - TODO: Give error if we wanted a signed integer and we got an unsigned - one - - Notice, longlong_from_string_with_check() honors thd->no_error, because - it's used to handle queries like this: - SELECT COUNT(@@basedir); - and is called when Item_func_get_system_var::update_null_value() - suppresses warnings and then calls val_int(). - The other methods {double|decimal}_from_string_with_check() ignore - thd->no_errors, because they are not used for update_null_value() - and they always allow all kind of warnings. - */ - THD *thd= current_thd; - return Converter_strtoll10_with_warn(thd, Warn_filter(thd), - cs, cptr, end - cptr).result(); - } - - double double_from_string_with_check(CHARSET_INFO *cs, const char *cptr, - const char *end) const - { - return Converter_strntod_with_warn(NULL, Warn_filter_all(), - cs, cptr, end - cptr).result(); - } - my_decimal *decimal_from_string_with_check(my_decimal *decimal_value, - CHARSET_INFO *cs, - const char *cptr, - const char *end) - { - Converter_str2my_decimal_with_warn(NULL, Warn_filter_all(), - E_DEC_FATAL_ERROR & ~E_DEC_BAD_NUM, - cs, cptr, end - cptr, decimal_value); - return decimal_value; - } - - longlong longlong_from_hex_hybrid(const char *str, uint32 length) - { - const char *end= str + length; - const char *ptr= end - MY_MIN(length, sizeof(longlong)); - ulonglong value= 0; - for ( ; ptr != end ; ptr++) - value= (value << 8) + (ulonglong) (uchar) *ptr; - return (longlong) value; - } - - longlong longlong_from_string_with_check(const String *str) const - { - return longlong_from_string_with_check(str->charset(), - str->ptr(), str->end()); - } - double double_from_string_with_check(const String *str) const - { - return double_from_string_with_check(str->charset(), - str->ptr(), str->end()); - } - my_decimal *decimal_from_string_with_check(my_decimal *decimal_value, - const String *str) - { - return decimal_from_string_with_check(decimal_value, str->charset(), - str->ptr(), str->end()); - } - // End of String-to-number conversion methods - -public: - /* - The enumeration Subst_constraint is currently used only in implementations - of the virtual function subst_argument_checker. - */ - enum Subst_constraint - { - ANY_SUBST, /* Any substitution for a field is allowed */ - IDENTITY_SUBST /* Substitution for a field is allowed if any two - different values of the field type are not equal */ - }; - /* - Item context attributes. - Comparison functions pass their attributes to propagate_equal_fields(). - For exmple, for string comparison, the collation of the comparison - operation is important inside propagate_equal_fields(). - */ - class Context - { - /* - Which type of propagation is allowed: - - ANY_SUBST (loose equality, according to the collation), or - - IDENTITY_SUBST (strict binary equality). - */ - Subst_constraint m_subst_constraint; - /* - Comparison type. - Impostant only when ANY_SUBSTS. - */ - Item_result m_compare_type; - /* - Collation of the comparison operation. - Important only when ANY_SUBST. - */ - CHARSET_INFO *m_compare_collation; - public: - Context(Subst_constraint subst, Item_result type, CHARSET_INFO *cs) - :m_subst_constraint(subst), - m_compare_type(type), - m_compare_collation(cs) { } - Subst_constraint subst_constraint() const { return m_subst_constraint; } - Item_result compare_type() const - { - DBUG_ASSERT(m_subst_constraint == ANY_SUBST); - return m_compare_type; - } - CHARSET_INFO *compare_collation() const - { - DBUG_ASSERT(m_subst_constraint == ANY_SUBST); - return m_compare_collation; - } - }; - class Context_identity: public Context - { // Use this to request only exact value, no invariants. - public: - Context_identity() - :Context(IDENTITY_SUBST, STRING_RESULT, &my_charset_bin) { } - }; - class Context_boolean: public Context - { // Use this when an item is [a part of] a boolean expression - public: - Context_boolean() :Context(ANY_SUBST, INT_RESULT, &my_charset_bin) { } - }; -}; - - -#define STORAGE_TYPE_MASK 7 -#define COLUMN_FORMAT_MASK 7 -#define COLUMN_FORMAT_SHIFT 3 - -/* The length of the header part for each virtual column in the .frm file */ -#define FRM_VCOL_OLD_HEADER_SIZE(b) (3 + MY_TEST(b)) -#define FRM_VCOL_NEW_BASE_SIZE 16 -#define FRM_VCOL_NEW_HEADER_SIZE 6 - -class Count_distinct_field; - -struct ha_field_option_struct; - -struct st_cache_field; -int field_conv(Field *to,Field *from); -int truncate_double(double *nr, uint field_length, uint dec, - bool unsigned_flag, double max_value); - -inline uint get_enum_pack_length(int elements) -{ - return elements < 256 ? 1 : 2; -} - -inline uint get_set_pack_length(int elements) -{ - uint len= (elements + 7) / 8; - return len > 4 ? 8 : len; -} - - -/** - Tests if field type is temporal and has date part, - i.e. represents DATE, DATETIME or TIMESTAMP types in SQL. - - @param type Field type, as returned by field->type(). - @retval true If field type is temporal type with date part. - @retval false If field type is not temporal type with date part. -*/ -inline bool is_temporal_type_with_date(enum_field_types type) -{ - switch (type) - { - case MYSQL_TYPE_DATE: - case MYSQL_TYPE_DATETIME: - case MYSQL_TYPE_TIMESTAMP: - return true; - case MYSQL_TYPE_DATETIME2: - case MYSQL_TYPE_TIMESTAMP2: - DBUG_ASSERT(0); // field->real_type() should not get to here. - default: - return false; - } -} - - -/** - Recognizer for concrete data type (called real_type for some reason), - returning true if it is one of the TIMESTAMP types. -*/ -inline bool is_timestamp_type(enum_field_types type) -{ - return type == MYSQL_TYPE_TIMESTAMP || type == MYSQL_TYPE_TIMESTAMP2; -} - - -/** - Convert temporal real types as retuned by field->real_type() - to field type as returned by field->type(). - - @param real_type Real type. - @retval Field type. -*/ -inline enum_field_types real_type_to_type(enum_field_types real_type) -{ - switch (real_type) - { - case MYSQL_TYPE_TIME2: - return MYSQL_TYPE_TIME; - case MYSQL_TYPE_DATETIME2: - return MYSQL_TYPE_DATETIME; - case MYSQL_TYPE_TIMESTAMP2: - return MYSQL_TYPE_TIMESTAMP; - case MYSQL_TYPE_NEWDATE: - return MYSQL_TYPE_DATE; - /* Note: NEWDECIMAL is a type, not only a real_type */ - default: return real_type; - } -} - - -static inline enum enum_mysql_timestamp_type -mysql_type_to_time_type(enum enum_field_types mysql_type) -{ - switch(mysql_type) { - case MYSQL_TYPE_TIME2: - case MYSQL_TYPE_TIME: return MYSQL_TIMESTAMP_TIME; - case MYSQL_TYPE_TIMESTAMP2: - case MYSQL_TYPE_TIMESTAMP: - case MYSQL_TYPE_DATETIME2: - case MYSQL_TYPE_DATETIME: return MYSQL_TIMESTAMP_DATETIME; - case MYSQL_TYPE_NEWDATE: - case MYSQL_TYPE_DATE: return MYSQL_TIMESTAMP_DATE; - default: return MYSQL_TIMESTAMP_ERROR; - } -} - - -/** - Tests if field type is temporal, i.e. represents - DATE, TIME, DATETIME or TIMESTAMP types in SQL. - - @param type Field type, as returned by field->type(). - @retval true If field type is temporal - @retval false If field type is not temporal -*/ -inline bool is_temporal_type(enum_field_types type) -{ - return mysql_type_to_time_type(type) != MYSQL_TIMESTAMP_ERROR; -} - - -/** - Tests if field type is temporal and has time part, - i.e. represents TIME, DATETIME or TIMESTAMP types in SQL. - - @param type Field type, as returned by field->type(). - @retval true If field type is temporal type with time part. - @retval false If field type is not temporal type with time part. -*/ -inline bool is_temporal_type_with_time(enum_field_types type) -{ - switch (type) - { - case MYSQL_TYPE_TIME: - case MYSQL_TYPE_DATETIME: - case MYSQL_TYPE_TIMESTAMP: - return true; - default: - return false; - } -} - -enum enum_vcol_info_type -{ - VCOL_GENERATED_VIRTUAL, VCOL_GENERATED_STORED, - VCOL_DEFAULT, VCOL_CHECK_FIELD, VCOL_CHECK_TABLE -}; - -static inline const char *vcol_type_name(enum_vcol_info_type type) -{ - switch (type) - { - case VCOL_GENERATED_VIRTUAL: - case VCOL_GENERATED_STORED: - return "GENERATED ALWAYS AS"; - case VCOL_DEFAULT: - return "DEFAULT"; - case VCOL_CHECK_FIELD: - case VCOL_CHECK_TABLE: - return "CHECK"; - } - return 0; -} - -/* - Flags for Virtual_column_info. If none is set, the expression must be - a constant with no side-effects, so it's calculated at CREATE TABLE time, - stored in table->record[2], and not recalculated for every statement. -*/ -#define VCOL_FIELD_REF 1 -#define VCOL_NON_DETERMINISTIC 2 -#define VCOL_SESSION_FUNC 4 /* uses session data, e.g. USER or DAYNAME */ -#define VCOL_TIME_FUNC 8 -#define VCOL_IMPOSSIBLE 16 - -#define VCOL_NOT_STRICTLY_DETERMINISTIC \ - (VCOL_NON_DETERMINISTIC | VCOL_TIME_FUNC | VCOL_SESSION_FUNC) - -/* - Virtual_column_info is the class to contain additional - characteristics that is specific for a virtual/computed - field such as: - - the defining expression that is evaluated to compute the value - of the field - - whether the field is to be stored in the database - - whether the field is used in a partitioning expression -*/ - -class Virtual_column_info: public Sql_alloc -{ -private: - /* - The following data is only updated by the parser and read - when a Create_field object is created/initialized. - */ - enum_field_types field_type; /* Real field type*/ - /* Flag indicating that the field used in a partitioning expression */ - bool in_partitioning_expr; - -public: - /* Flag indicating that the field is physically stored in the database */ - bool stored_in_db; - bool utf8; /* Already in utf8 */ - Item *expr; - LEX_STRING name; /* Name of constraint */ - uint flags; - - Virtual_column_info() - : field_type((enum enum_field_types)MYSQL_TYPE_VIRTUAL), - in_partitioning_expr(FALSE), stored_in_db(FALSE), - utf8(TRUE), expr(NULL), flags(0) - { - name.str= NULL; - name.length= 0; - }; - ~Virtual_column_info() {} - enum_field_types get_real_type() const - { - return field_type; - } - void set_field_type(enum_field_types fld_type) - { - /* Calling this function can only be done once. */ - field_type= fld_type; - } - bool is_stored() const - { - return stored_in_db; - } - void set_stored_in_db_flag(bool stored) - { - stored_in_db= stored; - } - bool is_in_partitioning_expr() const - { - return in_partitioning_expr; - } - void mark_as_in_partitioning_expr() - { - in_partitioning_expr= TRUE; - } - inline bool is_equal(const Virtual_column_info* vcol) const; - void print(String*); -}; - -class Field: public Value_source -{ - Field(const Item &); /* Prevent use of these */ - void operator=(Field &); -protected: - int save_in_field_str(Field *to) - { - StringBuffer result(charset()); - val_str(&result); - return to->store(result.ptr(), result.length(), charset()); - } - static void do_field_int(Copy_field *copy); - static void do_field_real(Copy_field *copy); - static void do_field_string(Copy_field *copy); - static void do_field_temporal(Copy_field *copy); - static void do_field_decimal(Copy_field *copy); -public: - static void *operator new(size_t size, MEM_ROOT *mem_root) throw () - { return alloc_root(mem_root, size); } - static void *operator new(size_t size) throw () - { return thd_alloc(current_thd, size); } - static void operator delete(void *ptr_arg, size_t size) { TRASH(ptr_arg, size); } - static void operator delete(void *ptr, MEM_ROOT *mem_root) - { DBUG_ASSERT(0); } - - /** - Used by System Versioning. - */ - virtual bool set_max() - { DBUG_ASSERT(0); return false; } - - /** - Used by System Versioning. - */ - virtual bool is_max() - { DBUG_ASSERT(0); return false; } - - uchar *ptr; // Position to field in record - /** - Byte where the @c NULL bit is stored inside a record. If this Field is a - @c NOT @c NULL field, this member is @c NULL. - */ - uchar *null_ptr; - /* - Note that you can use table->in_use as replacement for current_thd member - only inside of val_*() and store() members (e.g. you can't use it in cons) - */ - TABLE *table; // Pointer for table - TABLE *orig_table; // Pointer to original table - const char * const *table_name; - const char *field_name; - /** reference to the list of options or NULL */ - engine_option_value *option_list; - ha_field_option_struct *option_struct; /* structure with parsed options */ - LEX_STRING comment; - /* Field is part of the following keys */ - key_map key_start, part_of_key, part_of_key_not_clustered; - - /* - Bitmap of indexes that have records ordered by col1, ... this_field, ... - - For example, INDEX (col(prefix_n)) is not present in col.part_of_sortkey. - */ - key_map part_of_sortkey; - /* - We use three additional unireg types for TIMESTAMP to overcome limitation - of current binary format of .frm file. We'd like to be able to support - NOW() as default and on update value for such fields but unable to hold - this info anywhere except unireg_check field. This issue will be resolved - in more clean way with transition to new text based .frm format. - See also comment for Field_timestamp::Field_timestamp(). - */ - enum utype { - NONE=0, - NEXT_NUMBER=15, // AUTO_INCREMENT - TIMESTAMP_OLD_FIELD=18, // TIMESTAMP created before 4.1.3 - TIMESTAMP_DN_FIELD=21, // TIMESTAMP DEFAULT NOW() - TIMESTAMP_UN_FIELD=22, // TIMESTAMP ON UPDATE NOW() - TIMESTAMP_DNUN_FIELD=23 // TIMESTAMP DEFAULT NOW() ON UPDATE NOW() - }; - enum geometry_type - { - GEOM_GEOMETRY = 0, GEOM_POINT = 1, GEOM_LINESTRING = 2, GEOM_POLYGON = 3, - GEOM_MULTIPOINT = 4, GEOM_MULTILINESTRING = 5, GEOM_MULTIPOLYGON = 6, - GEOM_GEOMETRYCOLLECTION = 7 - }; - enum imagetype { itRAW, itMBR}; - - utype unireg_check; - uint32 field_length; // Length of field - uint32 flags; - uint16 field_index; // field number in fields array - uchar null_bit; // Bit used to test null bit - /** - If true, this field was created in create_tmp_field_from_item from a NULL - value. This means that the type of the field is just a guess, and the type - may be freely coerced to another type. - - @see create_tmp_field_from_item - @see Item_type_holder::get_real_type - - */ - bool is_created_from_null_item; - - /* TRUE in Field objects created for column min/max values */ - bool is_stat_field; - - /* - Selectivity of the range condition over this field. - When calculating this selectivity a range predicate - is taken into account only if: - - it is extracted from the WHERE clause - - it depends only on the table the field belongs to - */ - double cond_selectivity; - - /* - The next field in the class of equal fields at the top AND level - of the WHERE clause - */ - Field *next_equal_field; - - /* - This structure is used for statistical data on the column - that has been read from the statistical table column_stat - */ - Column_statistics *read_stats; - /* - This structure is used for statistical data on the column that - is collected by the function collect_statistics_for_table - */ - Column_statistics_collected *collected_stats; - - /* - This is additional data provided for any computed(virtual) field, - default function or check constraint. - In particular it includes a pointer to the item by which this field - can be computed from other fields. - */ - Virtual_column_info *vcol_info, *check_constraint, *default_value; - - Field(uchar *ptr_arg,uint32 length_arg,uchar *null_ptr_arg, - uchar null_bit_arg, utype unireg_check_arg, - const char *field_name_arg); - virtual ~Field() {} - - DTCollation dtcollation() const - { - return DTCollation(charset(), derivation(), repertoire()); - } - Type_std_attributes type_std_attributes() const - { - return Type_std_attributes(field_length, decimals(), - MY_TEST(flags & UNSIGNED_FLAG), - dtcollation()); - } - - /** - Convenience definition of a copy function returned by - Field::get_copy_func() - */ - typedef void Copy_func(Copy_field*); - virtual Copy_func *get_copy_func(const Field *from) const= 0; - /* Store functions returns 1 on overflow and -1 on fatal error */ - virtual int store_field(Field *from) { return from->save_in_field(this); } - virtual int save_in_field(Field *to)= 0; - /** - Check if it is possible just copy the value - of the field 'from' to the field 'this', e.g. for - INSERT INTO t1 (field1) SELECT field2 FROM t2; - @param from - The field to copy from - @retval true - it is possible to just copy value of 'from' to 'this' - @retval false - conversion is needed - */ - virtual bool memcpy_field_possible(const Field *from) const= 0; - virtual int store(const char *to, uint length,CHARSET_INFO *cs)=0; - virtual int store_hex_hybrid(const char *str, uint length); - virtual int store(double nr)=0; - virtual int store(longlong nr, bool unsigned_val)=0; - virtual int store_decimal(const my_decimal *d)=0; - virtual int store_time_dec(MYSQL_TIME *ltime, uint dec); - int store_time(MYSQL_TIME *ltime) - { return store_time_dec(ltime, TIME_SECOND_PART_DIGITS); } - int store(const char *to, uint length, CHARSET_INFO *cs, - enum_check_fields check_level); - int store(const LEX_STRING *ls, CHARSET_INFO *cs) - { return store(ls->str, ls->length, cs); } - virtual double val_real(void)=0; - virtual longlong val_int(void)=0; - virtual bool val_bool(void)= 0; - virtual my_decimal *val_decimal(my_decimal *); - inline String *val_str(String *str) { return val_str(str, str); } - /* - val_str(buf1, buf2) gets two buffers and should use them as follows: - if it needs a temp buffer to convert result to string - use buf1 - example Field_tiny::val_str() - if the value exists as a string already - use buf2 - example Field_string::val_str() - consequently, buf2 may be created as 'String buf;' - no memory - will be allocated for it. buf1 will be allocated to hold a - value if it's too small. Using allocated buffer for buf2 may result in - an unnecessary free (and later, may be an alloc). - This trickery is used to decrease a number of malloc calls. - */ - virtual String *val_str(String*,String *)=0; - String *val_int_as_str(String *val_buffer, bool unsigned_flag); - fast_field_copier get_fast_field_copier(const Field *from); - /* - str_needs_quotes() returns TRUE if the value returned by val_str() needs - to be quoted when used in constructing an SQL query. - */ - virtual bool str_needs_quotes() { return FALSE; } - virtual Item_result result_type () const=0; - virtual Item_result cmp_type () const { return result_type(); } - virtual const Type_handler *cast_to_int_type_handler() const - { - return Type_handler::get_handler_by_field_type(type()); - } - static bool type_can_have_key_part(enum_field_types); - static enum_field_types field_type_merge(enum_field_types, enum_field_types); - virtual bool eq(Field *field) - { - return (ptr == field->ptr && null_ptr == field->null_ptr && - null_bit == field->null_bit && field->type() == type()); - } - virtual bool eq_def(const Field *field) const; - - /* - pack_length() returns size (in bytes) used to store field data in memory - (i.e. it returns the maximum size of the field in a row of the table, - which is located in RAM). - */ - virtual uint32 pack_length() const { return (uint32) field_length; } - - /* - pack_length_in_rec() returns size (in bytes) used to store field data on - storage (i.e. it returns the maximal size of the field in a row of the - table, which is located on disk). - */ - virtual uint32 pack_length_in_rec() const { return pack_length(); } - virtual bool compatible_field_size(uint metadata, Relay_log_info *rli, - uint16 mflags, int *order); - virtual uint pack_length_from_metadata(uint field_metadata) - { - DBUG_ENTER("Field::pack_length_from_metadata"); - DBUG_RETURN(field_metadata); - } - virtual uint row_pack_length() const { return 0; } - virtual int save_field_metadata(uchar *first_byte) - { return do_save_field_metadata(first_byte); } - - /* - data_length() return the "real size" of the data in memory. - */ - virtual uint32 data_length() { return pack_length(); } - virtual uint32 sort_length() const { return pack_length(); } - - /* - Get the number bytes occupied by the value in the field. - CHAR values are stripped of trailing spaces. - Flexible values are stripped of their length. - */ - virtual uint32 value_length() - { - uint len; - if (!zero_pack() && - (type() == MYSQL_TYPE_STRING && - (len= pack_length()) >= 4 && len < 256)) - { - uchar *str, *end; - for (str= ptr, end= str+len; end > str && end[-1] == ' '; end--) {} - len=(uint) (end-str); - return len; - } - return data_length(); - } - - /** - Get the maximum size of the data in packed format. - - @return Maximum data length of the field when packed using the - Field::pack() function. - */ - virtual uint32 max_data_length() const { - return pack_length(); - }; - - virtual int reset(void) { bzero(ptr,pack_length()); return 0; } - virtual void reset_fields() {} - const uchar *ptr_in_record(const uchar *record) const - { - my_ptrdiff_t l_offset= (my_ptrdiff_t) (record - table->record[0]); - return ptr + l_offset; - } - virtual void set_default(); - - bool has_update_default_function() const - { - return flags & ON_UPDATE_NOW_FLAG; - } - bool has_default_now_unireg_check() const - { - return unireg_check == TIMESTAMP_DN_FIELD - || unireg_check == TIMESTAMP_DNUN_FIELD; - } - - /* - Mark the field as having a value supplied by the client, thus it should - not be auto-updated. - */ - void set_has_explicit_value() - { - bitmap_set_bit(&table->has_value_set, field_index); - } - bool has_explicit_value() - { - return bitmap_is_set(&table->has_value_set, field_index); - } - virtual bool set_explicit_default(Item *value); - - /** - Evaluates the @c UPDATE default function, if one exists, and stores the - result in the record buffer. If no such function exists for the column, - or the function is not valid for the column's data type, invoking this - function has no effect. - */ - virtual int evaluate_update_default_function() { return 0; } - - virtual bool binary() const { return 1; } - virtual bool zero_pack() const { return 1; } - virtual enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; } - virtual uint32 key_length() const { return pack_length(); } - virtual enum_field_types type() const =0; - virtual enum_field_types real_type() const { return type(); } - virtual enum_field_types binlog_type() const - { - /* - Binlog stores field->type() as type code by default. For example, - it puts MYSQL_TYPE_STRING in case of CHAR, VARCHAR, SET and ENUM, - with extra data type details put into metadata. - - Binlog behaviour slightly differs between various MySQL and MariaDB - versions for the temporal data types TIME, DATETIME and TIMESTAMP. - - MySQL prior to 5.6 uses MYSQL_TYPE_TIME, MYSQL_TYPE_DATETIME - and MYSQL_TYPE_TIMESTAMP type codes in binlog and stores no - additional metadata. - - MariaDB-5.3 implements new versions for TIME, DATATIME, TIMESTAMP - with fractional second precision, but uses the old format for the - types TIME(0), DATETIME(0), TIMESTAMP(0), and it still stores - MYSQL_TYPE_TIME, MYSQL_TYPE_DATETIME and MYSQL_TYPE_TIMESTAMP in binlog, - with no additional metadata. - So row-based replication between temporal data types of - different precision is not possible in MariaDB. - - MySQL-5.6 also implements a new version of TIME, DATETIME, TIMESTAMP - which support fractional second precision 0..6, and use the new - format even for the types TIME(0), DATETIME(0), TIMESTAMP(0). - For these new data types, MySQL-5.6 stores new type codes - MYSQL_TYPE_TIME2, MYSQL_TYPE_DATETIME2, MYSQL_TYPE_TIMESTAMP2 in binlog, - with fractional precision 0..6 put into metadata. - This makes it in theory possible to do row-based replication between - columns of different fractional precision (e.g. from TIME(1) on master - to TIME(6) on slave). However, it's not currently fully implemented yet. - MySQL-5.6 can only do row-based replication from the old types - TIME, DATETIME, TIMESTAMP (represented by MYSQL_TYPE_TIME, - MYSQL_TYPE_DATETIME and MYSQL_TYPE_TIMESTAMP type codes in binlog) - to the new corresponding types TIME(0), DATETIME(0), TIMESTAMP(0). - - Note: MariaDB starting from the version 10.0 understands the new - MySQL-5.6 type codes MYSQL_TYPE_TIME2, MYSQL_TYPE_DATETIME2, - MYSQL_TYPE_TIMESTAMP2. When started over MySQL-5.6 tables both on - master and on slave, MariaDB-10.0 can also do row-based replication - from the old types TIME, DATETIME, TIMESTAMP to the new MySQL-5.6 - types TIME(0), DATETIME(0), TIMESTAMP(0). - - Note: perhaps binlog should eventually be modified to store - real_type() instead of type() for all column types. - */ - return type(); - } - inline int cmp(const uchar *str) { return cmp(ptr,str); } - virtual int cmp_max(const uchar *a, const uchar *b, uint max_len) - { return cmp(a, b); } - virtual int cmp(const uchar *,const uchar *)=0; - virtual int cmp_binary(const uchar *a,const uchar *b, uint32 max_length=~0U) - { return memcmp(a,b,pack_length()); } - virtual int cmp_offset(uint row_offset) - { return cmp(ptr,ptr+row_offset); } - virtual int cmp_binary_offset(uint row_offset) - { return cmp_binary(ptr, ptr+row_offset); }; - virtual int key_cmp(const uchar *a,const uchar *b) - { return cmp(a, b); } - virtual int key_cmp(const uchar *str, uint length) - { return cmp(ptr,str); } - /* - Update the value m of the 'min_val' field with the current value v - of this field if force_update is set to TRUE or if v < m. - Return TRUE if the value has been updated. - */ - virtual bool update_min(Field *min_val, bool force_update) - { - bool update_fl= force_update || cmp(ptr, min_val->ptr) < 0; - if (update_fl) - { - min_val->set_notnull(); - memcpy(min_val->ptr, ptr, pack_length()); - } - return update_fl; - } - /* - Update the value m of the 'max_val' field with the current value v - of this field if force_update is set to TRUE or if v > m. - Return TRUE if the value has been updated. - */ - virtual bool update_max(Field *max_val, bool force_update) - { - bool update_fl= force_update || cmp(ptr, max_val->ptr) > 0; - if (update_fl) - { - max_val->set_notnull(); - memcpy(max_val->ptr, ptr, pack_length()); - } - return update_fl; - } - virtual void store_field_value(uchar *val, uint len) - { - memcpy(ptr, val, len); - } - virtual uint decimals() const { return 0; } - /* - Caller beware: sql_type can change str.Ptr, so check - ptr() to see if it changed if you are using your own buffer - in str and restore it with set() if needed - */ - virtual void sql_type(String &str) const =0; - virtual uint size_of() const =0; // For new field - inline bool is_null(my_ptrdiff_t row_offset= 0) const - { - /* - The table may have been marked as containing only NULL values - for all fields if it is a NULL-complemented row of an OUTER JOIN - or if the query is an implicitly grouped query (has aggregate - functions but no GROUP BY clause) with no qualifying rows. If - this is the case (in which TABLE::null_row is true), the field - is considered to be NULL. - - Note that if a table->null_row is set then also all null_bits are - set for the row. - - In the case of the 'result_field' for GROUP BY, table->null_row might - refer to the *next* row in the table (when the algorithm is: read the - next row, see if any of group column values have changed, send the - result - grouped - row to the client if yes). So, table->null_row might - be wrong, but such a result_field is always nullable (that's defined by - original_field->maybe_null()) and we trust its null bit. - */ - return null_ptr ? null_ptr[row_offset] & null_bit : table->null_row; - } - inline bool is_real_null(my_ptrdiff_t row_offset= 0) const - { return null_ptr && (null_ptr[row_offset] & null_bit); } - inline bool is_null_in_record(const uchar *record) const - { - if (maybe_null_in_table()) - return record[(uint) (null_ptr - table->record[0])] & null_bit; - return 0; - } - inline void set_null(my_ptrdiff_t row_offset= 0) - { if (null_ptr) null_ptr[row_offset]|= null_bit; } - inline void set_notnull(my_ptrdiff_t row_offset= 0) - { if (null_ptr) null_ptr[row_offset]&= (uchar) ~null_bit; } - inline bool maybe_null(void) const - { return null_ptr != 0 || table->maybe_null; } - - /* @return true if this field is NULL-able (even if temporarily) */ - inline bool real_maybe_null(void) const { return null_ptr != 0; } - uint null_offset(const uchar *record) const - { return (uint) (null_ptr - record); } - /* - For a NULL-able field (that can actually store a NULL value in a table) - null_ptr points to the "null bitmap" in the table->record[0] header. For - NOT NULL fields it is either 0 or points outside table->record[0] into the - table->triggers->extra_null_bitmap (so that the field can store a NULL - value temporarily, only in memory) - */ - bool maybe_null_in_table() const - { return null_ptr >= table->record[0] && null_ptr <= ptr; } - - uint null_offset() const - { return null_offset(table->record[0]); } - void set_null_ptr(uchar *p_null_ptr, uint p_null_bit) - { - null_ptr= p_null_ptr; - null_bit= p_null_bit; - } - - bool stored_in_db() const { return !vcol_info || vcol_info->stored_in_db; } - - inline THD *get_thd() const - { return likely(table) ? table->in_use : current_thd; } - - enum { - LAST_NULL_BYTE_UNDEF= 0 - }; - - /* - Find the position of the last null byte for the field. - - SYNOPSIS - last_null_byte() - - DESCRIPTION - Return a pointer to the last byte of the null bytes where the - field conceptually is placed. - - RETURN VALUE - The position of the last null byte relative to the beginning of - the record. If the field does not use any bits of the null - bytes, the value 0 (LAST_NULL_BYTE_UNDEF) is returned. - */ - size_t last_null_byte() const { - size_t bytes= do_last_null_byte(); - DBUG_PRINT("debug", ("last_null_byte() ==> %ld", (long) bytes)); - DBUG_ASSERT(bytes <= table->s->null_bytes); - return bytes; - } - - void make_sort_key(uchar *buff, uint length); - virtual void make_field(Send_field *); - virtual void sort_string(uchar *buff,uint length)=0; - virtual bool optimize_range(uint idx, uint part); - virtual void free() {} - virtual Field *make_new_field(MEM_ROOT *root, TABLE *new_table, - bool keep_type); - virtual Field *new_key_field(MEM_ROOT *root, TABLE *new_table, - uchar *new_ptr, uint32 length, - uchar *new_null_ptr, uint new_null_bit); - Field *clone(MEM_ROOT *mem_root, TABLE *new_table); - Field *clone(MEM_ROOT *mem_root, TABLE *new_table, my_ptrdiff_t diff, - bool stat_flag= FALSE); - Field *clone(MEM_ROOT *mem_root, my_ptrdiff_t diff); - inline void move_field(uchar *ptr_arg,uchar *null_ptr_arg,uchar null_bit_arg) - { - ptr=ptr_arg; null_ptr=null_ptr_arg; null_bit=null_bit_arg; - } - inline void move_field(uchar *ptr_arg) { ptr=ptr_arg; } - inline uchar *record_ptr() // record[0] or wherever the field was moved to - { - my_ptrdiff_t offset= table->s->field[field_index]->ptr - table->s->default_values; - return ptr - offset; - } - virtual void move_field_offset(my_ptrdiff_t ptr_diff) - { - ptr=ADD_TO_PTR(ptr,ptr_diff, uchar*); - if (null_ptr) - null_ptr=ADD_TO_PTR(null_ptr,ptr_diff,uchar*); - } - virtual void get_image(uchar *buff, uint length, CHARSET_INFO *cs) - { memcpy(buff,ptr,length); } - virtual void set_image(const uchar *buff,uint length, CHARSET_INFO *cs) - { memcpy(ptr,buff,length); } - - - /* - Copy a field part into an output buffer. - - SYNOPSIS - Field::get_key_image() - buff [out] output buffer - length output buffer size - type itMBR for geometry blobs, otherwise itRAW - - DESCRIPTION - This function makes a copy of field part of size equal to or - less than "length" parameter value. - For fields of string types (CHAR, VARCHAR, TEXT) the rest of buffer - is padded by zero byte. - - NOTES - For variable length character fields (i.e. UTF-8) the "length" - parameter means a number of output buffer bytes as if all field - characters have maximal possible size (mbmaxlen). In the other words, - "length" parameter is a number of characters multiplied by - field_charset->mbmaxlen. - - RETURN - Number of copied bytes (excluding padded zero bytes -- see above). - */ - - virtual uint get_key_image(uchar *buff, uint length, imagetype type_arg) - { - get_image(buff, length, &my_charset_bin); - return length; - } - virtual void set_key_image(const uchar *buff,uint length) - { set_image(buff,length, &my_charset_bin); } - inline longlong val_int_offset(uint row_offset) - { - ptr+=row_offset; - longlong tmp=val_int(); - ptr-=row_offset; - return tmp; - } - inline longlong val_int(const uchar *new_ptr) - { - uchar *old_ptr= ptr; - longlong return_value; - ptr= (uchar*) new_ptr; - return_value= val_int(); - ptr= old_ptr; - return return_value; - } - inline String *val_str(String *str, const uchar *new_ptr) - { - uchar *old_ptr= ptr; - ptr= (uchar*) new_ptr; - val_str(str); - ptr= old_ptr; - return str; - } - virtual bool send_binary(Protocol *protocol); - - virtual uchar *pack(uchar *to, const uchar *from, uint max_length); - /** - @overload Field::pack(uchar*, const uchar*, uint, bool) - */ - uchar *pack(uchar *to, const uchar *from) - { - DBUG_ENTER("Field::pack"); - uchar *result= this->pack(to, from, UINT_MAX); - DBUG_RETURN(result); - } - - virtual const uchar *unpack(uchar* to, const uchar *from, - const uchar *from_end, uint param_data=0); - - virtual uint packed_col_length(const uchar *to, uint length) - { return length;} - virtual uint max_packed_col_length(uint max_length) - { return max_length;} - - uint offset(uchar *record) const - { - return (uint) (ptr - record); - } - void copy_from_tmp(int offset); - uint fill_cache_field(struct st_cache_field *copy); - virtual bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); - bool get_time(MYSQL_TIME *ltime) { return get_date(ltime, TIME_TIME_ONLY); } - virtual CHARSET_INFO *charset(void) const { return &my_charset_bin; } - virtual CHARSET_INFO *charset_for_protocol(void) const - { return binary() ? &my_charset_bin : charset(); } - virtual CHARSET_INFO *sort_charset(void) const { return charset(); } - virtual bool has_charset(void) const { return FALSE; } - virtual enum Derivation derivation(void) const - { return DERIVATION_IMPLICIT; } - virtual uint repertoire(void) const { return MY_REPERTOIRE_UNICODE30; } - virtual void set_derivation(enum Derivation derivation_arg, - uint repertoire_arg) - { } - virtual int set_time() { return 1; } - bool set_warning(Sql_condition::enum_warning_level, unsigned int code, - int cuted_increment) const; -protected: - bool set_warning(unsigned int code, int cuted_increment) const - { - return set_warning(Sql_condition::WARN_LEVEL_WARN, code, cuted_increment); - } - bool set_note(unsigned int code, int cuted_increment) const - { - return set_warning(Sql_condition::WARN_LEVEL_NOTE, code, cuted_increment); - } - void set_datetime_warning(Sql_condition::enum_warning_level, uint code, - const ErrConv *str, timestamp_type ts_type, - int cuted_increment) const; - void set_datetime_warning(uint code, - const ErrConv *str, timestamp_type ts_type, - int cuted_increment) const - { - set_datetime_warning(Sql_condition::WARN_LEVEL_WARN, code, str, ts_type, - cuted_increment); - } - void set_warning_truncated_wrong_value(const char *type, const char *value); - inline bool check_overflow(int op_result) - { - return (op_result == E_DEC_OVERFLOW); - } - int warn_if_overflow(int op_result); - Copy_func *get_identical_copy_func() const; -public: - void set_table_name(String *alias) - { - table_name= &alias->Ptr; - } - void init(TABLE *table_arg) - { - orig_table= table= table_arg; - set_table_name(&table_arg->alias); - } - - /* maximum possible display length */ - virtual uint32 max_display_length()= 0; - - /** - Whether a field being created is compatible with a existing one. - - Used by the ALTER TABLE code to evaluate whether the new definition - of a table is compatible with the old definition so that it can - determine if data needs to be copied over (table data change). - */ - virtual uint is_equal(Create_field *new_field); - /* convert decimal to longlong with overflow check */ - longlong convert_decimal2longlong(const my_decimal *val, bool unsigned_flag, - int *err); - /* The max. number of characters */ - virtual uint32 char_length() const - { - return field_length / charset()->mbmaxlen; - } - - virtual geometry_type get_geometry_type() - { - /* shouldn't get here. */ - DBUG_ASSERT(0); - return GEOM_GEOMETRY; - } - - ha_storage_media field_storage_type() const - { - return (ha_storage_media) - ((flags >> FIELD_FLAGS_STORAGE_MEDIA) & 3); - } - - void set_storage_type(ha_storage_media storage_type_arg) - { - DBUG_ASSERT(field_storage_type() == HA_SM_DEFAULT); - flags |= static_cast(storage_type_arg) << - FIELD_FLAGS_STORAGE_MEDIA; - } - - column_format_type column_format() const - { - return (column_format_type) - ((flags >> FIELD_FLAGS_COLUMN_FORMAT) & 3); - } - - void set_column_format(column_format_type column_format_arg) - { - DBUG_ASSERT(column_format() == COLUMN_FORMAT_TYPE_DEFAULT); - flags |= static_cast(column_format_arg) << - FIELD_FLAGS_COLUMN_FORMAT; - } - - /* - System versioning support. - */ - - bool is_generated() - { - return flags & (GENERATED_ROW_START_FLAG | GENERATED_ROW_END_FLAG); - } - - bool is_generated_row_start() - { - return flags & GENERATED_ROW_START_FLAG; - } - - bool is_generated_row_end() - { - return flags & GENERATED_ROW_END_FLAG; - } - - bool is_versioning_disabled() - { - return flags & WITHOUT_SYSTEM_VERSIONING_FLAG; - } - - /* Mark a field as auto-generated row start column. */ - void set_generated_row_start() - { - //DBUG_ASSERT((flags & GENERATED_ROW_END_FLAG) == 0); - flags |= GENERATED_ROW_START_FLAG; - } - - /* Mark a field as auto-generated row start column. */ - void set_generated_row_end() - { - //DBUG_ASSERT((flags & GENERATED_ROW_START_FLAG) == 0); - flags |= GENERATED_ROW_END_FLAG; - } - - /* Disable a field versioning for a versioned table. */ - void disable_versioning() - { - flags |= WITHOUT_SYSTEM_VERSIONING_FLAG; - } - - /* Inherit a field versioning status from the table. */ - void inherit_versioning() - { - flags &= ~WITHOUT_SYSTEM_VERSIONING_FLAG; - } - - /* - Validate a non-null field value stored in the given record - according to the current thread settings, e.g. sql_mode. - @param thd - the thread - @param record - the record to check in - */ - virtual bool validate_value_in_record(THD *thd, const uchar *record) const - { return false; } - bool validate_value_in_record_with_warn(THD *thd, const uchar *record); - key_map get_possible_keys(); - - /* Hash value */ - virtual void hash(ulong *nr, ulong *nr2); - -/** - Checks whether a string field is part of write_set. - - @return - FALSE - If field is not char/varchar/.... - - If field is char/varchar/.. and is not part of write set. - TRUE - If field is char/varchar/.. and is part of write set. -*/ - virtual bool is_varchar_and_in_write_set() const { return FALSE; } - - /* Check whether the field can be used as a join attribute in hash join */ - virtual bool hash_join_is_possible() { return TRUE; } - virtual bool eq_cmp_as_binary() { return TRUE; } - - /* Position of the field value within the interval of [min, max] */ - virtual double pos_in_interval(Field *min, Field *max) - { - return (double) 0.5; - } - - /* - Check if comparison between the field and an item unambiguously - identifies a distinct field value. - - Example1: SELECT * FROM t1 WHERE int_column=10; - This example returns distinct integer value of 10. - - Example2: SELECT * FROM t1 WHERE varchar_column=DATE'2001-01-01' - This example returns non-distinct values. - Comparison as DATE will return '2001-01-01' and '2001-01-01x', - but these two values are not equal to each other as VARCHARs. - See also the function with the same name in sql_select.cc. - */ - virtual bool test_if_equality_guarantees_uniqueness(const Item *const_item) - const; - virtual bool can_be_substituted_to_equal_item(const Context &ctx, - const Item_equal *item); - virtual Item *get_equal_const_item(THD *thd, const Context &ctx, - Item *const_item) - { - return const_item; - } - virtual bool can_optimize_keypart_ref(const Item_bool_func *cond, - const Item *item) const; - virtual bool can_optimize_hash_join(const Item_bool_func *cond, - const Item *item) const - { - return can_optimize_keypart_ref(cond, item); - } - virtual bool can_optimize_group_min_max(const Item_bool_func *cond, - const Item *const_item) const; - /** - Test if Field can use range optimizer for a standard comparison operation: - <=, <, =, <=>, >, >= - Note, this method does not cover spatial operations. - */ - virtual bool can_optimize_range(const Item_bool_func *cond, - const Item *item, - bool is_eq_func) const; - - bool can_optimize_outer_join_table_elimination(const Item_bool_func *cond, - const Item *item) const - { - // Exactly the same rules with REF access - return can_optimize_keypart_ref(cond, item); - } - - bool save_in_field_default_value(bool view_eror_processing); - bool save_in_field_ignore_value(bool view_error_processing); - - /* Mark field in read map. Updates also virtual fields */ - void register_field_in_read_map(); - - friend int cre_myisam(char * name, register TABLE *form, uint options, - ulonglong auto_increment_value); - friend class Copy_field; - friend class Item_avg_field; - friend class Item_std_field; - friend class Item_sum_num; - friend class Item_sum_sum; - friend class Item_sum_count; - friend class Item_sum_avg; - friend class Item_sum_std; - friend class Item_sum_min; - friend class Item_sum_max; - friend class Item_func_group_concat; - -private: - /* - Primitive for implementing last_null_byte(). - - SYNOPSIS - do_last_null_byte() - - DESCRIPTION - Primitive for the implementation of the last_null_byte() - function. This represents the inheritance interface and can be - overridden by subclasses. - */ - virtual size_t do_last_null_byte() const; - -/** - Retrieve the field metadata for fields. - - This default implementation returns 0 and saves 0 in the metadata_ptr - value. - - @param metadata_ptr First byte of field metadata - - @returns 0 no bytes written. -*/ - virtual int do_save_field_metadata(uchar *metadata_ptr) - { return 0; } - -protected: - uchar *pack_int(uchar *to, const uchar *from, size_t size) - { - memcpy(to, from, size); - return to + size; - } - - const uchar *unpack_int(uchar* to, const uchar *from, - const uchar *from_end, size_t size) - { - if (from + size > from_end) - return 0; - memcpy(to, from, size); - return from + size; - } - - uchar *pack_int16(uchar *to, const uchar *from) - { return pack_int(to, from, 2); } - const uchar *unpack_int16(uchar* to, const uchar *from, const uchar *from_end) - { return unpack_int(to, from, from_end, 2); } - uchar *pack_int24(uchar *to, const uchar *from) - { return pack_int(to, from, 3); } - const uchar *unpack_int24(uchar* to, const uchar *from, const uchar *from_end) - { return unpack_int(to, from, from_end, 3); } - uchar *pack_int32(uchar *to, const uchar *from) - { return pack_int(to, from, 4); } - const uchar *unpack_int32(uchar* to, const uchar *from, const uchar *from_end) - { return unpack_int(to, from, from_end, 4); } - uchar *pack_int64(uchar* to, const uchar *from) - { return pack_int(to, from, 8); } - const uchar *unpack_int64(uchar* to, const uchar *from, const uchar *from_end) - { return unpack_int(to, from, from_end, 8); } - - double pos_in_interval_val_real(Field *min, Field *max); - double pos_in_interval_val_str(Field *min, Field *max, uint data_offset); -}; - - -class Field_num :public Field { -protected: - int check_edom_and_important_data_truncation(const char *type, bool edom, - CHARSET_INFO *cs, - const char *str, uint length, - const char *end_of_num); - int check_edom_and_truncation(const char *type, bool edom, - CHARSET_INFO *cs, - const char *str, uint length, - const char *end_of_num); - int check_int(CHARSET_INFO *cs, const char *str, uint length, - const char *int_end, int error) - { - return check_edom_and_truncation("integer", - error == MY_ERRNO_EDOM || str == int_end, - cs, str, length, int_end); - } - bool get_int(CHARSET_INFO *cs, const char *from, uint len, - longlong *rnd, ulonglong unsigned_max, - longlong signed_min, longlong signed_max); - void prepend_zeros(String *value) const; - Item *get_equal_zerofill_const_item(THD *thd, const Context &ctx, - Item *const_item); -public: - const uint8 dec; - bool zerofill,unsigned_flag; // Purify cannot handle bit fields - Field_num(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg, - uchar null_bit_arg, utype unireg_check_arg, - const char *field_name_arg, - uint8 dec_arg, bool zero_arg, bool unsigned_arg); - enum Item_result result_type () const { return INT_RESULT; } - enum Derivation derivation(void) const { return DERIVATION_NUMERIC; } - uint repertoire(void) const { return MY_REPERTOIRE_NUMERIC; } - CHARSET_INFO *charset(void) const { return &my_charset_numeric; } - Item *get_equal_const_item(THD *thd, const Context &ctx, Item *const_item) - { - return (flags & ZEROFILL_FLAG) ? - get_equal_zerofill_const_item(thd, ctx, const_item) : - const_item; - } - void add_zerofill_and_unsigned(String &res) const; - friend class Create_field; - void make_field(Send_field *); - uint decimals() const { return (uint) dec; } - uint size_of() const { return sizeof(*this); } - bool eq_def(const Field *field) const; - Copy_func *get_copy_func(const Field *from) const - { - return do_field_int; - } - int save_in_field(Field *to) - { - return to->store(val_int(), MY_TEST(flags & UNSIGNED_FLAG)); - } - bool memcpy_field_possible(const Field *from) const - { - return real_type() == from->real_type() && - pack_length() == from->pack_length() && - !((flags & UNSIGNED_FLAG) && !(from->flags & UNSIGNED_FLAG)) && - decimals() == from->decimals(); - } - int store_decimal(const my_decimal *); - my_decimal *val_decimal(my_decimal *); - bool val_bool() { return val_int() != 0; } - uint is_equal(Create_field *new_field); - uint row_pack_length() const { return pack_length(); } - uint32 pack_length_from_metadata(uint field_metadata) { - uint32 length= pack_length(); - DBUG_PRINT("result", ("pack_length_from_metadata(%d): %u", - field_metadata, length)); - return length; - } - int store_time_dec(MYSQL_TIME *ltime, uint dec); - double pos_in_interval(Field *min, Field *max) - { - return pos_in_interval_val_real(min, max); - } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); -}; - - -class Field_str :public Field { -protected: - // TODO-10.2: Reuse DTCollation instead of these three members - CHARSET_INFO *field_charset; - enum Derivation field_derivation; - uint field_repertoire; -public: - bool can_be_substituted_to_equal_item(const Context &ctx, - const Item_equal *item_equal); - Field_str(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg, - uchar null_bit_arg, utype unireg_check_arg, - const char *field_name_arg, CHARSET_INFO *charset); - Item_result result_type () const { return STRING_RESULT; } - uint decimals() const { return NOT_FIXED_DEC; } - int save_in_field(Field *to) { return save_in_field_str(to); } - bool memcpy_field_possible(const Field *from) const - { - return real_type() == from->real_type() && - pack_length() == from->pack_length() && - charset() == from->charset(); - } - int store(double nr); - int store(longlong nr, bool unsigned_val)=0; - int store_decimal(const my_decimal *); - int store(const char *to,uint length,CHARSET_INFO *cs)=0; - int store_hex_hybrid(const char *str, uint length) - { - return store(str, length, &my_charset_bin); - } - uint repertoire(void) const { return field_repertoire; } - CHARSET_INFO *charset(void) const { return field_charset; } - enum Derivation derivation(void) const { return field_derivation; } - void set_derivation(enum Derivation derivation_arg, - uint repertoire_arg) - { - field_derivation= derivation_arg; - field_repertoire= repertoire_arg; - } - bool binary() const { return field_charset == &my_charset_bin; } - uint32 max_display_length() { return field_length; } - friend class Create_field; - my_decimal *val_decimal(my_decimal *); - bool val_bool() { return val_real() != 0e0; } - virtual bool str_needs_quotes() { return TRUE; } - uint is_equal(Create_field *new_field); - bool eq_cmp_as_binary() { return MY_TEST(flags & BINARY_FLAG); } - virtual uint length_size() { return 0; } - double pos_in_interval(Field *min, Field *max) - { - return pos_in_interval_val_str(min, max, length_size()); - } - bool test_if_equality_guarantees_uniqueness(const Item *const_item) const; -}; - -/* base class for Field_string, Field_varstring and Field_blob */ - -class Field_longstr :public Field_str -{ -protected: - int report_if_important_data(const char *ptr, const char *end, - bool count_spaces); - bool check_string_copy_error(const String_copier *copier, - const char *end, CHARSET_INFO *cs); - int check_conversion_status(const String_copier *copier, - const char *end, CHARSET_INFO *cs, - bool count_spaces) - { - if (check_string_copy_error(copier, end, cs)) - return 2; - return report_if_important_data(copier->source_end_pos(), - end, count_spaces); - } - bool cmp_to_string_with_same_collation(const Item_bool_func *cond, - const Item *item) const; - bool cmp_to_string_with_stricter_collation(const Item_bool_func *cond, - const Item *item) const; -public: - Field_longstr(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, - uchar null_bit_arg, utype unireg_check_arg, - const char *field_name_arg, CHARSET_INFO *charset_arg) - :Field_str(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, - field_name_arg, charset_arg) - {} - - int store_decimal(const my_decimal *d); - uint32 max_data_length() const; - - bool is_varchar_and_in_write_set() const - { - DBUG_ASSERT(table && table->write_set); - return bitmap_is_set(table->write_set, field_index); - } - bool match_collation_to_optimize_range() const { return true; } - - bool can_optimize_keypart_ref(const Item_bool_func *cond, - const Item *item) const; - bool can_optimize_hash_join(const Item_bool_func *cond, - const Item *item) const; - bool can_optimize_group_min_max(const Item_bool_func *cond, - const Item *const_item) const; - bool can_optimize_range(const Item_bool_func *cond, - const Item *item, - bool is_eq_func) const; -}; - -/* base class for float and double and decimal (old one) */ -class Field_real :public Field_num { -protected: - double get_double(const char *str, uint length, CHARSET_INFO *cs, int *err); -public: - bool not_fixed; - - Field_real(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, - uchar null_bit_arg, utype unireg_check_arg, - const char *field_name_arg, - uint8 dec_arg, bool zero_arg, bool unsigned_arg) - :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, - field_name_arg, dec_arg, zero_arg, unsigned_arg), - not_fixed(dec_arg >= FLOATING_POINT_DECIMALS) - {} - Item_result result_type () const { return REAL_RESULT; } - Copy_func *get_copy_func(const Field *from) const - { - return do_field_real; - } - int save_in_field(Field *to) { return to->store(val_real()); } - bool memcpy_field_possible(const Field *from) const - { - /* - Cannot do memcpy from a longer field to a shorter field, - e.g. a DOUBLE(53,10) into a DOUBLE(10,10). - But it should be OK the other way around. - */ - return Field_num::memcpy_field_possible(from) && - field_length >= from->field_length; - } - int store_decimal(const my_decimal *); - int store_time_dec(MYSQL_TIME *ltime, uint dec); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); - my_decimal *val_decimal(my_decimal *); - bool val_bool() { return val_real() != 0e0; } - uint32 max_display_length() { return field_length; } - uint size_of() const { return sizeof(*this); } - Item *get_equal_const_item(THD *thd, const Context &ctx, Item *const_item); -}; - - -class Field_decimal :public Field_real { -public: - Field_decimal(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, - uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg, - uint8 dec_arg,bool zero_arg,bool unsigned_arg) - :Field_real(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, - unireg_check_arg, field_name_arg, - dec_arg, zero_arg, unsigned_arg) - {} - enum_field_types type() const { return MYSQL_TYPE_DECIMAL;} - enum ha_base_keytype key_type() const - { return zerofill ? HA_KEYTYPE_BINARY : HA_KEYTYPE_NUM; } - Copy_func *get_copy_func(const Field *from) const - { - return eq_def(from) ? get_identical_copy_func() : do_field_string; - } - int reset(void); - int store(const char *to,uint length,CHARSET_INFO *charset); - int store(double nr); - int store(longlong nr, bool unsigned_val); - double val_real(void); - longlong val_int(void); - String *val_str(String*,String *); - int cmp(const uchar *,const uchar *); - void sort_string(uchar *buff,uint length); - void overflow(bool negative); - bool zero_pack() const { return 0; } - void sql_type(String &str) const; - virtual uchar *pack(uchar* to, const uchar *from, uint max_length) - { - return Field::pack(to, from, max_length); - } -}; - - -/* New decimal/numeric field which use fixed point arithmetic */ -class Field_new_decimal :public Field_num { -private: - int do_save_field_metadata(uchar *first_byte); -public: - /* The maximum number of decimal digits can be stored */ - uint precision; - uint bin_size; - /* - Constructors take max_length of the field as a parameter - not the - precision as the number of decimal digits allowed. - So for example we need to count length from precision handling - CREATE TABLE ( DECIMAL(x,y)) - */ - Field_new_decimal(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, - uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg, - uint8 dec_arg, bool zero_arg, bool unsigned_arg); - Field_new_decimal(uint32 len_arg, bool maybe_null_arg, - const char *field_name_arg, uint8 dec_arg, - bool unsigned_arg); - enum_field_types type() const { return MYSQL_TYPE_NEWDECIMAL;} - enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; } - Item_result result_type () const { return DECIMAL_RESULT; } - Copy_func *get_copy_func(const Field *from) const - { - // if (from->real_type() == MYSQL_TYPE_BIT) // QQ: why? - // return do_field_int; - return do_field_decimal; - } - int save_in_field(Field *to) - { - my_decimal buff; - return to->store_decimal(val_decimal(&buff)); - } - bool memcpy_field_possible(const Field *from) const - { - return Field_num::memcpy_field_possible(from) && - field_length == from->field_length; - } - int reset(void); - bool store_value(const my_decimal *decimal_value); - bool store_value(const my_decimal *decimal_value, int *native_error); - void set_value_on_overflow(my_decimal *decimal_value, bool sign); - int store(const char *to, uint length, CHARSET_INFO *charset); - int store(double nr); - int store(longlong nr, bool unsigned_val); - int store_time_dec(MYSQL_TIME *ltime, uint dec); - int store_decimal(const my_decimal *); - double val_real(void); - longlong val_int(void); - my_decimal *val_decimal(my_decimal *); - String *val_str(String*, String *); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); - bool val_bool() - { - my_decimal decimal_value; - my_decimal *val= val_decimal(&decimal_value); - return val ? !my_decimal_is_zero(val) : 0; - } - int cmp(const uchar *, const uchar *); - void sort_string(uchar *buff, uint length); - bool zero_pack() const { return 0; } - void sql_type(String &str) const; - uint32 max_display_length() { return field_length; } - uint size_of() const { return sizeof(*this); } - uint32 pack_length() const { return (uint32) bin_size; } - uint pack_length_from_metadata(uint field_metadata); - uint row_pack_length() const { return pack_length(); } - bool compatible_field_size(uint field_metadata, Relay_log_info *rli, - uint16 mflags, int *order_var); - uint is_equal(Create_field *new_field); - virtual const uchar *unpack(uchar* to, const uchar *from, const uchar *from_end, uint param_data); - static Field *create_from_item(MEM_ROOT *root, Item *); - Item *get_equal_const_item(THD *thd, const Context &ctx, Item *const_item); -}; - - -class Field_tiny :public Field_num { -public: - Field_tiny(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, - uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg, - bool zero_arg, bool unsigned_arg) - :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, - unireg_check_arg, field_name_arg, - 0, zero_arg,unsigned_arg) - {} - enum_field_types type() const { return MYSQL_TYPE_TINY;} - enum ha_base_keytype key_type() const - { return unsigned_flag ? HA_KEYTYPE_BINARY : HA_KEYTYPE_INT8; } - int store(const char *to,uint length,CHARSET_INFO *charset); - int store(double nr); - int store(longlong nr, bool unsigned_val); - int reset(void) { ptr[0]=0; return 0; } - double val_real(void); - longlong val_int(void); - String *val_str(String*,String *); - bool send_binary(Protocol *protocol); - int cmp(const uchar *,const uchar *); - void sort_string(uchar *buff,uint length); - uint32 pack_length() const { return 1; } - void sql_type(String &str) const; - uint32 max_display_length() { return 4; } - - virtual uchar *pack(uchar* to, const uchar *from, uint max_length) - { - *to= *from; - return to + 1; - } - - virtual const uchar *unpack(uchar* to, const uchar *from, - const uchar *from_end, uint param_data) - { - if (from == from_end) - return 0; - *to= *from; - return from + 1; - } -}; - - -class Field_short :public Field_num { -public: - Field_short(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, - uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg, - bool zero_arg, bool unsigned_arg) - :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, - unireg_check_arg, field_name_arg, - 0, zero_arg,unsigned_arg) - {} - Field_short(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg, - bool unsigned_arg) - :Field_num((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0,0, - NONE, field_name_arg, 0, 0, unsigned_arg) - {} - enum_field_types type() const { return MYSQL_TYPE_SHORT;} - enum ha_base_keytype key_type() const - { return unsigned_flag ? HA_KEYTYPE_USHORT_INT : HA_KEYTYPE_SHORT_INT;} - int store(const char *to,uint length,CHARSET_INFO *charset); - int store(double nr); - int store(longlong nr, bool unsigned_val); - int reset(void) { ptr[0]=ptr[1]=0; return 0; } - double val_real(void); - longlong val_int(void); - String *val_str(String*,String *); - bool send_binary(Protocol *protocol); - int cmp(const uchar *,const uchar *); - void sort_string(uchar *buff,uint length); - uint32 pack_length() const { return 2; } - void sql_type(String &str) const; - uint32 max_display_length() { return 6; } - - virtual uchar *pack(uchar* to, const uchar *from, uint max_length) - { return pack_int16(to, from); } - - virtual const uchar *unpack(uchar* to, const uchar *from, - const uchar *from_end, uint param_data) - { return unpack_int16(to, from, from_end); } -}; - -class Field_medium :public Field_num { -public: - Field_medium(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, - uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg, - bool zero_arg, bool unsigned_arg) - :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, - unireg_check_arg, field_name_arg, - 0, zero_arg,unsigned_arg) - {} - enum_field_types type() const { return MYSQL_TYPE_INT24;} - enum ha_base_keytype key_type() const - { return unsigned_flag ? HA_KEYTYPE_UINT24 : HA_KEYTYPE_INT24; } - int store(const char *to,uint length,CHARSET_INFO *charset); - int store(double nr); - int store(longlong nr, bool unsigned_val); - int reset(void) { ptr[0]=ptr[1]=ptr[2]=0; return 0; } - double val_real(void); - longlong val_int(void); - String *val_str(String*,String *); - bool send_binary(Protocol *protocol); - int cmp(const uchar *,const uchar *); - void sort_string(uchar *buff,uint length); - uint32 pack_length() const { return 3; } - void sql_type(String &str) const; - uint32 max_display_length() { return 8; } - - virtual uchar *pack(uchar* to, const uchar *from, uint max_length) - { - return Field::pack(to, from, max_length); - } -}; - - -class Field_long :public Field_num { -public: - Field_long(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, - uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg, - bool zero_arg, bool unsigned_arg) - :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, - unireg_check_arg, field_name_arg, - 0, zero_arg,unsigned_arg) - {} - Field_long(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg, - bool unsigned_arg) - :Field_num((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0,0, - NONE, field_name_arg,0,0,unsigned_arg) - {} - enum_field_types type() const { return MYSQL_TYPE_LONG;} - enum ha_base_keytype key_type() const - { return unsigned_flag ? HA_KEYTYPE_ULONG_INT : HA_KEYTYPE_LONG_INT; } - int store(const char *to,uint length,CHARSET_INFO *charset); - int store(double nr); - int store(longlong nr, bool unsigned_val); - int reset(void) { ptr[0]=ptr[1]=ptr[2]=ptr[3]=0; return 0; } - double val_real(void); - longlong val_int(void); - bool send_binary(Protocol *protocol); - String *val_str(String*,String *); - int cmp(const uchar *,const uchar *); - void sort_string(uchar *buff,uint length); - uint32 pack_length() const { return 4; } - void sql_type(String &str) const; - uint32 max_display_length() { return MY_INT32_NUM_DECIMAL_DIGITS; } - virtual uchar *pack(uchar* to, const uchar *from, - uint max_length __attribute__((unused))) - { - return pack_int32(to, from); - } - virtual const uchar *unpack(uchar* to, const uchar *from, - const uchar *from_end, - uint param_data __attribute__((unused))) - { - return unpack_int32(to, from, from_end); - } -}; - - -class Field_longlong :public Field_num { -public: - Field_longlong(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, - uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg, - bool zero_arg, bool unsigned_arg) - :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, - unireg_check_arg, field_name_arg, - 0, zero_arg,unsigned_arg) - {} - Field_longlong(uint32 len_arg,bool maybe_null_arg, - const char *field_name_arg, - bool unsigned_arg) - :Field_num((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0,0, - NONE, field_name_arg,0,0,unsigned_arg) - {} - enum_field_types type() const { return MYSQL_TYPE_LONGLONG;} - enum ha_base_keytype key_type() const - { return unsigned_flag ? HA_KEYTYPE_ULONGLONG : HA_KEYTYPE_LONGLONG; } - int store(const char *to,uint length,CHARSET_INFO *charset); - int store(double nr); - int store(longlong nr, bool unsigned_val); - int reset(void) - { - ptr[0]=ptr[1]=ptr[2]=ptr[3]=ptr[4]=ptr[5]=ptr[6]=ptr[7]=0; - return 0; - } - double val_real(void); - longlong val_int(void); - String *val_str(String*,String *); - bool send_binary(Protocol *protocol); - int cmp(const uchar *,const uchar *); - void sort_string(uchar *buff,uint length); - uint32 pack_length() const { return 8; } - void sql_type(String &str) const; - uint32 max_display_length() { return 20; } - virtual uchar *pack(uchar* to, const uchar *from, - uint max_length __attribute__((unused))) - { - return pack_int64(to, from); - } - const uchar *unpack(uchar* to, const uchar *from, const uchar *from_end, - uint param_data __attribute__((unused))) - { - return unpack_int64(to, from, from_end); - } - - bool set_max(); - bool is_max(); -}; - - -class Field_float :public Field_real { -public: - Field_float(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, - uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg, - uint8 dec_arg,bool zero_arg,bool unsigned_arg) - :Field_real(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, - unireg_check_arg, field_name_arg, - dec_arg, zero_arg, unsigned_arg) - { - if (dec_arg >= FLOATING_POINT_DECIMALS) - dec_arg= NOT_FIXED_DEC; - } - Field_float(uint32 len_arg, bool maybe_null_arg, const char *field_name_arg, - uint8 dec_arg) - :Field_real((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0, (uint) 0, - NONE, field_name_arg, dec_arg, 0, 0) - { - if (dec_arg >= FLOATING_POINT_DECIMALS) - dec_arg= NOT_FIXED_DEC; - } - enum_field_types type() const { return MYSQL_TYPE_FLOAT;} - enum ha_base_keytype key_type() const { return HA_KEYTYPE_FLOAT; } - int store(const char *to,uint length,CHARSET_INFO *charset); - int store(double nr); - int store(longlong nr, bool unsigned_val); - int reset(void) { bzero(ptr,sizeof(float)); return 0; } - double val_real(void); - longlong val_int(void); - String *val_str(String*,String *); - bool send_binary(Protocol *protocol); - int cmp(const uchar *,const uchar *); - void sort_string(uchar *buff,uint length); - uint32 pack_length() const { return sizeof(float); } - uint row_pack_length() const { return pack_length(); } - void sql_type(String &str) const; -private: - int do_save_field_metadata(uchar *first_byte); -}; - - -class Field_double :public Field_real { -public: - Field_double(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, - uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg, - uint8 dec_arg,bool zero_arg,bool unsigned_arg) - :Field_real(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, - unireg_check_arg, field_name_arg, - dec_arg, zero_arg, unsigned_arg) - { - if (dec_arg >= FLOATING_POINT_DECIMALS) - dec_arg= NOT_FIXED_DEC; - } - Field_double(uint32 len_arg, bool maybe_null_arg, const char *field_name_arg, - uint8 dec_arg) - :Field_real((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "" : 0, (uint) 0, - NONE, field_name_arg, dec_arg, 0, 0) - { - if (dec_arg >= FLOATING_POINT_DECIMALS) - dec_arg= NOT_FIXED_DEC; - } - Field_double(uint32 len_arg, bool maybe_null_arg, const char *field_name_arg, - uint8 dec_arg, bool not_fixed_arg) - :Field_real((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "" : 0, (uint) 0, - NONE, field_name_arg, dec_arg, 0, 0) - { - not_fixed= not_fixed_arg; - if (dec_arg >= FLOATING_POINT_DECIMALS) - dec_arg= NOT_FIXED_DEC; - } - enum_field_types type() const { return MYSQL_TYPE_DOUBLE;} - enum ha_base_keytype key_type() const { return HA_KEYTYPE_DOUBLE; } - int store(const char *to,uint length,CHARSET_INFO *charset); - int store(double nr); - int store(longlong nr, bool unsigned_val); - int reset(void) { bzero(ptr,sizeof(double)); return 0; } - double val_real(void); - longlong val_int(void) - { - Converter_double_to_longlong conv(Field_double::val_real(), false); - if (conv.error()) - conv.push_warning(get_thd(), Field_double::val_real(), false); - return conv.result(); - } - String *val_str(String*,String *); - bool send_binary(Protocol *protocol); - int cmp(const uchar *,const uchar *); - void sort_string(uchar *buff,uint length); - uint32 pack_length() const { return sizeof(double); } - uint row_pack_length() const { return pack_length(); } - void sql_type(String &str) const; -private: - int do_save_field_metadata(uchar *first_byte); -}; - - -/* Everything saved in this will disappear. It will always return NULL */ - -class Field_null :public Field_str { - static uchar null[1]; -public: - Field_null(uchar *ptr_arg, uint32 len_arg, - enum utype unireg_check_arg, const char *field_name_arg, - CHARSET_INFO *cs) - :Field_str(ptr_arg, len_arg, null, 1, - unireg_check_arg, field_name_arg, cs) - {} - enum_field_types type() const { return MYSQL_TYPE_NULL;} - Copy_func *get_copy_func(const Field *from) const - { - return do_field_string; - } - int store(const char *to, uint length, CHARSET_INFO *cs) - { null[0]=1; return 0; } - int store(double nr) { null[0]=1; return 0; } - int store(longlong nr, bool unsigned_val) { null[0]=1; return 0; } - int store_decimal(const my_decimal *d) { null[0]=1; return 0; } - int reset(void) { return 0; } - double val_real(void) { return 0.0;} - longlong val_int(void) { return 0;} - bool val_bool(void) { return false; } - my_decimal *val_decimal(my_decimal *) { return 0; } - String *val_str(String *value,String *value2) - { value2->length(0); return value2;} - int cmp(const uchar *a, const uchar *b) { return 0;} - void sort_string(uchar *buff, uint length) {} - uint32 pack_length() const { return 0; } - void sql_type(String &str) const; - uint size_of() const { return sizeof(*this); } - uint32 max_display_length() { return 4; } - void move_field_offset(my_ptrdiff_t ptr_diff) {} - bool can_optimize_keypart_ref(const Item_bool_func *cond, - const Item *item) const - { - DBUG_ASSERT(0); - return false; - } - bool can_optimize_group_min_max(const Item_bool_func *cond, - const Item *const_item) const - { - DBUG_ASSERT(0); - return false; - } -}; - - -class Field_temporal: public Field { -protected: - Item *get_equal_const_item_datetime(THD *thd, const Context &ctx, - Item *const_item); -public: - Field_temporal(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg, - uchar null_bit_arg, utype unireg_check_arg, - const char *field_name_arg) - :Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, - field_name_arg) - { flags|= BINARY_FLAG; } - Item_result result_type () const { return STRING_RESULT; } - int store_hex_hybrid(const char *str, uint length) - { - return store(str, length, &my_charset_bin); - } - Copy_func *get_copy_func(const Field *from) const; - int save_in_field(Field *to) - { - MYSQL_TIME ltime; - if (get_date(<ime, 0)) - return to->reset(); - return to->store_time_dec(<ime, decimals()); - } - bool memcpy_field_possible(const Field *from) const; - uint32 max_display_length() { return field_length; } - bool str_needs_quotes() { return TRUE; } - enum Derivation derivation(void) const { return DERIVATION_NUMERIC; } - uint repertoire(void) const { return MY_REPERTOIRE_NUMERIC; } - CHARSET_INFO *charset(void) const { return &my_charset_numeric; } - CHARSET_INFO *sort_charset(void) const { return &my_charset_bin; } - bool binary() const { return true; } - enum Item_result cmp_type () const { return TIME_RESULT; } - bool val_bool() { return val_real() != 0e0; } - uint is_equal(Create_field *new_field); - bool eq_def(const Field *field) const - { - return (Field::eq_def(field) && decimals() == field->decimals()); - } - my_decimal *val_decimal(my_decimal*); - void set_warnings(Sql_condition::enum_warning_level trunc_level, - const ErrConv *str, int was_cut, timestamp_type ts_type); - double pos_in_interval(Field *min, Field *max) - { - return pos_in_interval_val_real(min, max); - } - bool can_optimize_keypart_ref(const Item_bool_func *cond, - const Item *item) const; - bool can_optimize_group_min_max(const Item_bool_func *cond, - const Item *const_item) const; - bool can_optimize_range(const Item_bool_func *cond, - const Item *item, - bool is_eq_func) const - { - return true; - } -}; - - -/** - Abstract class for: - - DATE - - DATETIME - - DATETIME(1..6) - - DATETIME(0..6) - MySQL56 version -*/ -class Field_temporal_with_date: public Field_temporal { -protected: - int store_TIME_with_warning(MYSQL_TIME *ltime, const ErrConv *str, - int was_cut, int have_smth_to_conv); - virtual void store_TIME(MYSQL_TIME *ltime) = 0; - virtual bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, - ulonglong fuzzydate) const = 0; - bool validate_MMDD(bool not_zero_date, uint month, uint day, - ulonglong fuzzydate) const - { - if (!not_zero_date) - return fuzzydate & TIME_NO_ZERO_DATE; - if (!month || !day) - return fuzzydate & TIME_NO_ZERO_IN_DATE; - return false; - } -public: - Field_temporal_with_date(uchar *ptr_arg, uint32 len_arg, - uchar *null_ptr_arg, uchar null_bit_arg, - utype unireg_check_arg, - const char *field_name_arg) - :Field_temporal(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, - unireg_check_arg, field_name_arg) - {} - int store(const char *to, uint length, CHARSET_INFO *charset); - int store(double nr); - int store(longlong nr, bool unsigned_val); - int store_time_dec(MYSQL_TIME *ltime, uint dec); - int store_decimal(const my_decimal *); - bool validate_value_in_record(THD *thd, const uchar *record) const; -}; - - -class Field_timestamp :public Field_temporal { -protected: - int store_TIME_with_warning(THD *, MYSQL_TIME *, const ErrConv *, - int warnings, bool have_smth_to_conv); -public: - Field_timestamp(uchar *ptr_arg, uint32 len_arg, - uchar *null_ptr_arg, uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg, - TABLE_SHARE *share); - enum_field_types type() const { return MYSQL_TYPE_TIMESTAMP;} - enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONG_INT; } - int store(const char *to,uint length,CHARSET_INFO *charset); - int store(double nr); - int store(longlong nr, bool unsigned_val); - int store_time_dec(MYSQL_TIME *ltime, uint dec); - int store_decimal(const my_decimal *); - double val_real(void); - longlong val_int(void); - String *val_str(String*,String *); - bool send_binary(Protocol *protocol); - int cmp(const uchar *,const uchar *); - void sort_string(uchar *buff,uint length); - uint32 pack_length() const { return 4; } - void sql_type(String &str) const; - bool zero_pack() const { return 0; } - int set_time(); - bool set_explicit_default(Item *value); - int evaluate_update_default_function() - { - int res= 0; - if (has_update_default_function()) - res= set_time(); - return res; - } - /* Get TIMESTAMP field value as seconds since begging of Unix Epoch */ - virtual my_time_t get_timestamp(const uchar *pos, ulong *sec_part) const; - my_time_t get_timestamp(ulong *sec_part) const - { - return get_timestamp(ptr, sec_part); - } - virtual void store_TIME(my_time_t timestamp, ulong sec_part) - { - int4store(ptr,timestamp); - } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); - uchar *pack(uchar *to, const uchar *from, - uint max_length __attribute__((unused))) - { - return pack_int32(to, from); - } - const uchar *unpack(uchar* to, const uchar *from, const uchar *from_end, - uint param_data __attribute__((unused))) - { - return unpack_int32(to, from, from_end); - } - bool validate_value_in_record(THD *thd, const uchar *record) const; - Item *get_equal_const_item(THD *thd, const Context &ctx, Item *const_item) - { - return get_equal_const_item_datetime(thd, ctx, const_item); - } - uint size_of() const { return sizeof(*this); } -}; - - -/** - Abstract class for: - - TIMESTAMP(1..6) - - TIMESTAMP(0..6) - MySQL56 version -*/ -class Field_timestamp_with_dec :public Field_timestamp { -protected: - uint dec; -public: - Field_timestamp_with_dec(uchar *ptr_arg, - uchar *null_ptr_arg, uchar null_bit_arg, - enum utype unireg_check_arg, - const char *field_name_arg, - TABLE_SHARE *share, uint dec_arg) : - Field_timestamp(ptr_arg, - MAX_DATETIME_WIDTH + dec_arg + MY_TEST(dec_arg), null_ptr_arg, - null_bit_arg, unireg_check_arg, field_name_arg, share), - dec(dec_arg) - { - DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS); - } - uint decimals() const { return dec; } - enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; } - uchar *pack(uchar *to, const uchar *from, uint max_length) - { return Field::pack(to, from, max_length); } - const uchar *unpack(uchar* to, const uchar *from, const uchar *from_end, - uint param_data) - { return Field::unpack(to, from, from_end, param_data); } - void make_field(Send_field *field); - void sort_string(uchar *to, uint length) - { - DBUG_ASSERT(length == pack_length()); - memcpy(to, ptr, length); - } - bool send_binary(Protocol *protocol); - double val_real(void); - my_decimal* val_decimal(my_decimal*); - int set_time(); -}; - - -class Field_timestamp_hires :public Field_timestamp_with_dec { -public: - Field_timestamp_hires(uchar *ptr_arg, - uchar *null_ptr_arg, uchar null_bit_arg, - enum utype unireg_check_arg, - const char *field_name_arg, - TABLE_SHARE *share, uint dec_arg) : - Field_timestamp_with_dec(ptr_arg, null_ptr_arg, null_bit_arg, - unireg_check_arg, field_name_arg, share, dec_arg) - { - DBUG_ASSERT(dec); - } - my_time_t get_timestamp(const uchar *pos, ulong *sec_part) const; - void store_TIME(my_time_t timestamp, ulong sec_part); - int cmp(const uchar *,const uchar *); - uint32 pack_length() const; - uint size_of() const { return sizeof(*this); } -}; - - -/** - TIMESTAMP(0..6) - MySQL56 version -*/ -class Field_timestampf :public Field_timestamp_with_dec { - int do_save_field_metadata(uchar *metadata_ptr) - { - *metadata_ptr= decimals(); - return 1; - } -public: - Field_timestampf(uchar *ptr_arg, - uchar *null_ptr_arg, uchar null_bit_arg, - enum utype unireg_check_arg, - const char *field_name_arg, - TABLE_SHARE *share, uint dec_arg) : - Field_timestamp_with_dec(ptr_arg, null_ptr_arg, null_bit_arg, - unireg_check_arg, field_name_arg, share, dec_arg) - {} - enum_field_types real_type() const { return MYSQL_TYPE_TIMESTAMP2; } - enum_field_types binlog_type() const { return MYSQL_TYPE_TIMESTAMP2; } - uint32 pack_length() const - { - return my_timestamp_binary_length(dec); - } - uint row_pack_length() const { return pack_length(); } - uint pack_length_from_metadata(uint field_metadata) - { - DBUG_ENTER("Field_timestampf::pack_length_from_metadata"); - uint tmp= my_timestamp_binary_length(field_metadata); - DBUG_RETURN(tmp); - } - int cmp(const uchar *a_ptr,const uchar *b_ptr) - { - return memcmp(a_ptr, b_ptr, pack_length()); - } - bool set_max(); - bool is_max(); - void store_TIME(my_time_t timestamp, ulong sec_part); - my_time_t get_timestamp(const uchar *pos, ulong *sec_part) const; - uint size_of() const { return sizeof(*this); } -}; - - -class Field_year :public Field_tiny { -public: - Field_year(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, - uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg) - :Field_tiny(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, - unireg_check_arg, field_name_arg, 1, 1) - {} - enum_field_types type() const { return MYSQL_TYPE_YEAR;} - Copy_func *get_copy_func(const Field *from) const - { - if (eq_def(from)) - return get_identical_copy_func(); - switch (from->cmp_type()) { - case STRING_RESULT: - return do_field_string; - case TIME_RESULT: - return do_field_temporal; - case DECIMAL_RESULT: - return do_field_decimal; - case REAL_RESULT: - return do_field_real; - case INT_RESULT: - break; - case ROW_RESULT: - default: - DBUG_ASSERT(0); - break; - } - return do_field_int; - } - int store(const char *to,uint length,CHARSET_INFO *charset); - int store(double nr); - int store(longlong nr, bool unsigned_val); - int store_time_dec(MYSQL_TIME *ltime, uint dec); - double val_real(void); - longlong val_int(void); - String *val_str(String*,String *); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); - bool send_binary(Protocol *protocol); - uint32 max_display_length() { return field_length; } - void sql_type(String &str) const; -}; - - -class Field_date :public Field_temporal_with_date { - void store_TIME(MYSQL_TIME *ltime); - bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const; -public: - Field_date(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg) - :Field_temporal_with_date(ptr_arg, MAX_DATE_WIDTH, null_ptr_arg, null_bit_arg, - unireg_check_arg, field_name_arg) {} - enum_field_types type() const { return MYSQL_TYPE_DATE;} - enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONG_INT; } - int reset(void) { ptr[0]=ptr[1]=ptr[2]=ptr[3]=0; return 0; } - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) - { return Field_date::get_TIME(ltime, ptr, fuzzydate); } - double val_real(void); - longlong val_int(void); - String *val_str(String*,String *); - bool send_binary(Protocol *protocol); - int cmp(const uchar *,const uchar *); - void sort_string(uchar *buff,uint length); - uint32 pack_length() const { return 4; } - void sql_type(String &str) const; - uchar *pack(uchar* to, const uchar *from, - uint max_length __attribute__((unused))) - { - return pack_int32(to, from); - } - const uchar *unpack(uchar* to, const uchar *from, const uchar *from_end, - uint param_data __attribute__((unused))) - { - return unpack_int32(to, from, from_end); - } - uint size_of() const { return sizeof(*this); } -}; - - -class Field_newdate :public Field_temporal_with_date { - void store_TIME(MYSQL_TIME *ltime); - bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const; -public: - Field_newdate(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg) - :Field_temporal_with_date(ptr_arg, MAX_DATE_WIDTH, null_ptr_arg, null_bit_arg, - unireg_check_arg, field_name_arg) - {} - enum_field_types type() const { return MYSQL_TYPE_DATE;} - enum_field_types real_type() const { return MYSQL_TYPE_NEWDATE; } - enum ha_base_keytype key_type() const { return HA_KEYTYPE_UINT24; } - int reset(void) { ptr[0]=ptr[1]=ptr[2]=0; return 0; } - double val_real(void); - longlong val_int(void); - String *val_str(String*,String *); - bool send_binary(Protocol *protocol); - int cmp(const uchar *,const uchar *); - void sort_string(uchar *buff,uint length); - uint32 pack_length() const { return 3; } - void sql_type(String &str) const; - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) - { return Field_newdate::get_TIME(ltime, ptr, fuzzydate); } - uint size_of() const { return sizeof(*this); } - Item *get_equal_const_item(THD *thd, const Context &ctx, Item *const_item); -}; - - -class Field_time :public Field_temporal { - /* - when this Field_time instance is used for storing values for index lookups - (see class store_key, Field::new_key_field(), etc), the following - might be set to TO_DAYS(CURDATE()). See also Field_time::store_time_dec() - */ - long curdays; -protected: - virtual void store_TIME(MYSQL_TIME *ltime); - int store_TIME_with_warning(MYSQL_TIME *ltime, const ErrConv *str, - int was_cut, int have_smth_to_conv); - bool check_zero_in_date_with_warn(ulonglong fuzzydate); - static void do_field_time(Copy_field *copy); -public: - Field_time(uchar *ptr_arg, uint length_arg, uchar *null_ptr_arg, - uchar null_bit_arg, enum utype unireg_check_arg, - const char *field_name_arg) - :Field_temporal(ptr_arg, length_arg, null_ptr_arg, null_bit_arg, - unireg_check_arg, field_name_arg), curdays(0) - {} - enum_field_types type() const { return MYSQL_TYPE_TIME;} - enum ha_base_keytype key_type() const { return HA_KEYTYPE_INT24; } - Copy_func *get_copy_func(const Field *from) const - { - return from->cmp_type() == REAL_RESULT ? do_field_string : // MDEV-9344 - from->type() == MYSQL_TYPE_YEAR ? do_field_int : - from->type() == MYSQL_TYPE_BIT ? do_field_int : - eq_def(from) ? get_identical_copy_func() : - do_field_time; - } - bool memcpy_field_possible(const Field *from) const - { - return real_type() == from->real_type() && - decimals() == from->decimals(); - } - int store_time_dec(MYSQL_TIME *ltime, uint dec); - int store(const char *to,uint length,CHARSET_INFO *charset); - int store(double nr); - int store(longlong nr, bool unsigned_val); - int store_decimal(const my_decimal *); - double val_real(void); - longlong val_int(void); - String *val_str(String*,String *); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); - bool send_binary(Protocol *protocol); - int cmp(const uchar *,const uchar *); - void sort_string(uchar *buff,uint length); - uint32 pack_length() const { return 3; } - void sql_type(String &str) const; - uint size_of() const { return sizeof(*this); } - void set_curdays(THD *thd); - Field *new_key_field(MEM_ROOT *root, TABLE *new_table, - uchar *new_ptr, uint32 length, - uchar *new_null_ptr, uint new_null_bit); - Item *get_equal_const_item(THD *thd, const Context &ctx, Item *const_item); -}; - - -/** - Abstract class for: - - TIME(1..6) - - TIME(0..6) - MySQL56 version -*/ -class Field_time_with_dec :public Field_time { -protected: - uint dec; -public: - Field_time_with_dec(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg, - uint dec_arg) - :Field_time(ptr_arg, MIN_TIME_WIDTH + dec_arg + MY_TEST(dec_arg), - null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg), - dec(dec_arg) - { - DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS); - } - uint decimals() const { return dec; } - enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; } - longlong val_int(void); - double val_real(void); - void make_field(Send_field *); -}; - - -/** - TIME(1..6) -*/ -class Field_time_hires :public Field_time_with_dec { - longlong zero_point; - void store_TIME(MYSQL_TIME *ltime); -public: - Field_time_hires(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg, - uint dec_arg) - :Field_time_with_dec(ptr_arg, null_ptr_arg, - null_bit_arg, unireg_check_arg, field_name_arg, - dec_arg) - { - DBUG_ASSERT(dec); - zero_point= sec_part_shift( - ((TIME_MAX_VALUE_SECONDS+1LL)*TIME_SECOND_PART_FACTOR), dec); - } - int reset(void); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); - int cmp(const uchar *,const uchar *); - void sort_string(uchar *buff,uint length); - uint32 pack_length() const; - uint size_of() const { return sizeof(*this); } -}; - - -/** - TIME(0..6) - MySQL56 version -*/ -class Field_timef :public Field_time_with_dec { - void store_TIME(MYSQL_TIME *ltime); - int do_save_field_metadata(uchar *metadata_ptr) - { - *metadata_ptr= decimals(); - return 1; - } -public: - Field_timef(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg, - uint dec_arg) - :Field_time_with_dec(ptr_arg, null_ptr_arg, - null_bit_arg, unireg_check_arg, field_name_arg, - dec_arg) - { - DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS); - } - enum_field_types real_type() const { return MYSQL_TYPE_TIME2; } - enum_field_types binlog_type() const { return MYSQL_TYPE_TIME2; } - uint32 pack_length() const - { - return my_time_binary_length(dec); - } - uint row_pack_length() const { return pack_length(); } - uint pack_length_from_metadata(uint field_metadata) - { - DBUG_ENTER("Field_timef::pack_length_from_metadata"); - uint tmp= my_time_binary_length(field_metadata); - DBUG_RETURN(tmp); - } - void sort_string(uchar *to, uint length) - { - DBUG_ASSERT(length == Field_timef::pack_length()); - memcpy(to, ptr, length); - } - int cmp(const uchar *a_ptr, const uchar *b_ptr) - { - return memcmp(a_ptr, b_ptr, pack_length()); - } - int reset(); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); - uint size_of() const { return sizeof(*this); } -}; - - -class Field_datetime :public Field_temporal_with_date { - void store_TIME(MYSQL_TIME *ltime); - bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const; -public: - Field_datetime(uchar *ptr_arg, uint length_arg, uchar *null_ptr_arg, - uchar null_bit_arg, enum utype unireg_check_arg, - const char *field_name_arg) - :Field_temporal_with_date(ptr_arg, length_arg, null_ptr_arg, null_bit_arg, - unireg_check_arg, field_name_arg) - { - if (unireg_check == TIMESTAMP_UN_FIELD || - unireg_check == TIMESTAMP_DNUN_FIELD) - flags|= ON_UPDATE_NOW_FLAG; - } - enum_field_types type() const { return MYSQL_TYPE_DATETIME;} - enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONGLONG; } - double val_real(void); - longlong val_int(void); - String *val_str(String*,String *); - bool send_binary(Protocol *protocol); - int cmp(const uchar *,const uchar *); - void sort_string(uchar *buff,uint length); - uint32 pack_length() const { return 8; } - void sql_type(String &str) const; - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) - { return Field_datetime::get_TIME(ltime, ptr, fuzzydate); } - int set_time(); - int evaluate_update_default_function() - { - int res= 0; - if (has_update_default_function()) - res= set_time(); - return res; - } - uchar *pack(uchar* to, const uchar *from, - uint max_length __attribute__((unused))) - { - return pack_int64(to, from); - } - const uchar *unpack(uchar* to, const uchar *from, const uchar *from_end, - uint param_data __attribute__((unused))) - { - return unpack_int64(to, from, from_end); - } - Item *get_equal_const_item(THD *thd, const Context &ctx, Item *const_item) - { - return get_equal_const_item_datetime(thd, ctx, const_item); - } - uint size_of() const { return sizeof(*this); } -}; - - -/** - Abstract class for: - - DATETIME(1..6) - - DATETIME(0..6) - MySQL56 version -*/ -class Field_datetime_with_dec :public Field_datetime { -protected: - uint dec; -public: - Field_datetime_with_dec(uchar *ptr_arg, uchar *null_ptr_arg, - uchar null_bit_arg, enum utype unireg_check_arg, - const char *field_name_arg, uint dec_arg) - :Field_datetime(ptr_arg, MAX_DATETIME_WIDTH + dec_arg + MY_TEST(dec_arg), - null_ptr_arg, null_bit_arg, unireg_check_arg, - field_name_arg), dec(dec_arg) - { - DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS); - } - uint decimals() const { return dec; } - enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; } - void make_field(Send_field *field); - bool send_binary(Protocol *protocol); - uchar *pack(uchar *to, const uchar *from, uint max_length) - { return Field::pack(to, from, max_length); } - const uchar *unpack(uchar* to, const uchar *from, const uchar *from_end, - uint param_data) - { return Field::unpack(to, from, from_end, param_data); } - void sort_string(uchar *to, uint length) - { - DBUG_ASSERT(length == pack_length()); - memcpy(to, ptr, length); - } - double val_real(void); - longlong val_int(void); - String *val_str(String*,String *); -}; - - -/** - DATETIME(1..6) -*/ -class Field_datetime_hires :public Field_datetime_with_dec { - void store_TIME(MYSQL_TIME *ltime); - bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const; -public: - Field_datetime_hires(uchar *ptr_arg, uchar *null_ptr_arg, - uchar null_bit_arg, enum utype unireg_check_arg, - const char *field_name_arg, uint dec_arg) - :Field_datetime_with_dec(ptr_arg, null_ptr_arg, null_bit_arg, - unireg_check_arg, field_name_arg, dec_arg) - { - DBUG_ASSERT(dec); - } - int cmp(const uchar *,const uchar *); - uint32 pack_length() const; - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) - { return Field_datetime_hires::get_TIME(ltime, ptr, fuzzydate); } - uint size_of() const { return sizeof(*this); } -}; - - -/** - DATETIME(0..6) - MySQL56 version -*/ -class Field_datetimef :public Field_datetime_with_dec { - void store_TIME(MYSQL_TIME *ltime); - bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const; - int do_save_field_metadata(uchar *metadata_ptr) - { - *metadata_ptr= decimals(); - return 1; - } -public: - Field_datetimef(uchar *ptr_arg, uchar *null_ptr_arg, - uchar null_bit_arg, enum utype unireg_check_arg, - const char *field_name_arg, uint dec_arg) - :Field_datetime_with_dec(ptr_arg, null_ptr_arg, null_bit_arg, - unireg_check_arg, field_name_arg, dec_arg) - {} - enum_field_types real_type() const { return MYSQL_TYPE_DATETIME2; } - enum_field_types binlog_type() const { return MYSQL_TYPE_DATETIME2; } - uint32 pack_length() const - { - return my_datetime_binary_length(dec); - } - uint row_pack_length() const { return pack_length(); } - uint pack_length_from_metadata(uint field_metadata) - { - DBUG_ENTER("Field_datetimef::pack_length_from_metadata"); - uint tmp= my_datetime_binary_length(field_metadata); - DBUG_RETURN(tmp); - } - int cmp(const uchar *a_ptr, const uchar *b_ptr) - { - return memcmp(a_ptr, b_ptr, pack_length()); - } - int reset(); - bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) - { return Field_datetimef::get_TIME(ltime, ptr, fuzzydate); } - uint size_of() const { return sizeof(*this); } -}; - - -static inline Field_timestamp * -new_Field_timestamp(MEM_ROOT *root,uchar *ptr, uchar *null_ptr, uchar null_bit, - enum Field::utype unireg_check, const char *field_name, - TABLE_SHARE *share, uint dec) -{ - if (dec==0) - return new (root) - Field_timestamp(ptr, MAX_DATETIME_WIDTH, null_ptr, - null_bit, unireg_check, field_name, share); - if (dec >= FLOATING_POINT_DECIMALS) - dec= MAX_DATETIME_PRECISION; - return new (root) - Field_timestamp_hires(ptr, null_ptr, null_bit, unireg_check, - field_name, share, dec); -} - -static inline Field_time * -new_Field_time(MEM_ROOT *root, uchar *ptr, uchar *null_ptr, uchar null_bit, - enum Field::utype unireg_check, const char *field_name, - uint dec) -{ - if (dec == 0) - return new (root) - Field_time(ptr, MIN_TIME_WIDTH, null_ptr, null_bit, unireg_check, - field_name); - if (dec >= FLOATING_POINT_DECIMALS) - dec= MAX_DATETIME_PRECISION; - return new (root) - Field_time_hires(ptr, null_ptr, null_bit, unireg_check, field_name, dec); -} - -static inline Field_datetime * -new_Field_datetime(MEM_ROOT *root, uchar *ptr, uchar *null_ptr, uchar null_bit, - enum Field::utype unireg_check, - const char *field_name, uint dec) -{ - if (dec == 0) - return new (root) - Field_datetime(ptr, MAX_DATETIME_WIDTH, null_ptr, null_bit, - unireg_check, field_name); - if (dec >= FLOATING_POINT_DECIMALS) - dec= MAX_DATETIME_PRECISION; - return new (root) - Field_datetime_hires(ptr, null_ptr, null_bit, - unireg_check, field_name, dec); -} - -class Field_string :public Field_longstr { - class Warn_filter_string: public Warn_filter - { - public: - Warn_filter_string(const THD *thd, const Field_string *field); - }; -public: - bool can_alter_field_type; - Field_string(uchar *ptr_arg, uint32 len_arg,uchar *null_ptr_arg, - uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg, - CHARSET_INFO *cs) - :Field_longstr(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, - unireg_check_arg, field_name_arg, cs), - can_alter_field_type(1) {}; - Field_string(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg, - CHARSET_INFO *cs) - :Field_longstr((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0, 0, - NONE, field_name_arg, cs), - can_alter_field_type(1) {}; - - enum_field_types type() const - { - return ((can_alter_field_type && orig_table && - orig_table->s->db_create_options & HA_OPTION_PACK_RECORD && - field_length >= 4) && - orig_table->s->frm_version < FRM_VER_TRUE_VARCHAR ? - MYSQL_TYPE_VAR_STRING : MYSQL_TYPE_STRING); - } - enum ha_base_keytype key_type() const - { return binary() ? HA_KEYTYPE_BINARY : HA_KEYTYPE_TEXT; } - bool zero_pack() const { return 0; } - Copy_func *get_copy_func(const Field *from) const; - int reset(void) - { - charset()->cset->fill(charset(),(char*) ptr, field_length, - (has_charset() ? ' ' : 0)); - return 0; - } - int store(const char *to,uint length,CHARSET_INFO *charset); - int store(longlong nr, bool unsigned_val); - int store(double nr) { return Field_str::store(nr); } /* QQ: To be deleted */ - double val_real(void); - longlong val_int(void); - String *val_str(String*,String *); - my_decimal *val_decimal(my_decimal *); - int cmp(const uchar *,const uchar *); - void sort_string(uchar *buff,uint length); - void sql_type(String &str) const; - virtual uchar *pack(uchar *to, const uchar *from, - uint max_length); - virtual const uchar *unpack(uchar* to, const uchar *from, - const uchar *from_end,uint param_data); - uint pack_length_from_metadata(uint field_metadata) - { - DBUG_PRINT("debug", ("field_metadata: 0x%04x", field_metadata)); - if (field_metadata == 0) - return row_pack_length(); - return (((field_metadata >> 4) & 0x300) ^ 0x300) + (field_metadata & 0x00ff); - } - bool compatible_field_size(uint field_metadata, Relay_log_info *rli, - uint16 mflags, int *order_var); - uint row_pack_length() const { return field_length; } - int pack_cmp(const uchar *a,const uchar *b,uint key_length, - bool insert_or_update); - int pack_cmp(const uchar *b,uint key_length,bool insert_or_update); - uint packed_col_length(const uchar *to, uint length); - uint max_packed_col_length(uint max_length); - uint size_of() const { return sizeof(*this); } - enum_field_types real_type() const { return MYSQL_TYPE_STRING; } - bool has_charset(void) const - { return charset() == &my_charset_bin ? FALSE : TRUE; } - Field *make_new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type); - virtual uint get_key_image(uchar *buff,uint length, imagetype type); -private: - int do_save_field_metadata(uchar *first_byte); -}; - - -class Field_varstring :public Field_longstr { - uchar *get_data() const - { - return ptr + length_bytes; - } - uint get_length() const - { - return length_bytes == 1 ? (uint) *ptr : uint2korr(ptr); - } -public: - /* - The maximum space available in a Field_varstring, in bytes. See - length_bytes. - */ - static const uint MAX_SIZE; - /* Store number of bytes used to store length (1 or 2) */ - uint32 length_bytes; - Field_varstring(uchar *ptr_arg, - uint32 len_arg, uint length_bytes_arg, - uchar *null_ptr_arg, uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg, - TABLE_SHARE *share, CHARSET_INFO *cs) - :Field_longstr(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, - unireg_check_arg, field_name_arg, cs), - length_bytes(length_bytes_arg) - { - share->varchar_fields++; - } - Field_varstring(uint32 len_arg,bool maybe_null_arg, - const char *field_name_arg, - TABLE_SHARE *share, CHARSET_INFO *cs) - :Field_longstr((uchar*) 0,len_arg, maybe_null_arg ? (uchar*) "": 0, 0, - NONE, field_name_arg, cs), - length_bytes(len_arg < 256 ? 1 :2) - { - share->varchar_fields++; - } - - enum_field_types type() const { return MYSQL_TYPE_VARCHAR; } - enum ha_base_keytype key_type() const; - uint row_pack_length() const { return field_length; } - bool zero_pack() const { return 0; } - int reset(void) { bzero(ptr,field_length+length_bytes); return 0; } - uint32 pack_length() const { return (uint32) field_length+length_bytes; } - uint32 key_length() const { return (uint32) field_length; } - uint32 sort_length() const - { - return (uint32) field_length + (field_charset == &my_charset_bin ? - length_bytes : 0); - } - Copy_func *get_copy_func(const Field *from) const; - bool memcpy_field_possible(const Field *from) const - { - return Field_str::memcpy_field_possible(from) && - length_bytes == ((Field_varstring*) from)->length_bytes; - } - int store(const char *to,uint length,CHARSET_INFO *charset); - int store(longlong nr, bool unsigned_val); - int store(double nr) { return Field_str::store(nr); } /* QQ: To be deleted */ - double val_real(void); - longlong val_int(void); - String *val_str(String*,String *); - my_decimal *val_decimal(my_decimal *); - int cmp_max(const uchar *, const uchar *, uint max_length); - int cmp(const uchar *a,const uchar *b) - { - return cmp_max(a, b, ~0U); - } - void sort_string(uchar *buff,uint length); - uint get_key_image(uchar *buff,uint length, imagetype type); - void set_key_image(const uchar *buff,uint length); - void sql_type(String &str) const; - virtual uchar *pack(uchar *to, const uchar *from, uint max_length); - virtual const uchar *unpack(uchar* to, const uchar *from, - const uchar *from_end, uint param_data); - int cmp_binary(const uchar *a,const uchar *b, uint32 max_length=~0U); - int key_cmp(const uchar *,const uchar*); - int key_cmp(const uchar *str, uint length); - uint packed_col_length(const uchar *to, uint length); - uint max_packed_col_length(uint max_length); - uint32 data_length(); - uint size_of() const { return sizeof(*this); } - enum_field_types real_type() const { return MYSQL_TYPE_VARCHAR; } - bool has_charset(void) const - { return charset() == &my_charset_bin ? FALSE : TRUE; } - Field *make_new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type); - Field *new_key_field(MEM_ROOT *root, TABLE *new_table, - uchar *new_ptr, uint32 length, - uchar *new_null_ptr, uint new_null_bit); - uint is_equal(Create_field *new_field); - void hash(ulong *nr, ulong *nr2); - uint length_size() { return length_bytes; } -private: - int do_save_field_metadata(uchar *first_byte); -}; - - -class Field_blob :public Field_longstr { -protected: - /** - The number of bytes used to represent the length of the blob. - */ - uint packlength; - - /** - The 'value'-object is a cache fronting the storage engine. - */ - String value; - /** - Cache for blob values when reading a row with a virtual blob - field. This is needed to not destroy the old cached value when - updating the blob with a new value when creating the new row. - */ - String read_value; - - static void do_copy_blob(Copy_field *copy); - static void do_conv_blob(Copy_field *copy); -public: - Field_blob(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg, - TABLE_SHARE *share, uint blob_pack_length, CHARSET_INFO *cs); - Field_blob(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg, - CHARSET_INFO *cs) - :Field_longstr((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0, 0, - NONE, field_name_arg, cs), - packlength(4) - { - flags|= BLOB_FLAG; - } - Field_blob(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg, - CHARSET_INFO *cs, bool set_packlength) - :Field_longstr((uchar*) 0,len_arg, maybe_null_arg ? (uchar*) "": 0, 0, - NONE, field_name_arg, cs) - { - flags|= BLOB_FLAG; - packlength= 4; - if (set_packlength) - { - packlength= len_arg <= 255 ? 1 : - len_arg <= 65535 ? 2 : - len_arg <= 16777215 ? 3 : 4; - } - } - Field_blob(uint32 packlength_arg) - :Field_longstr((uchar*) 0, 0, (uchar*) "", 0, NONE, "temp", system_charset_info), - packlength(packlength_arg) {} - /* Note that the default copy constructor is used, in clone() */ - enum_field_types type() const { return MYSQL_TYPE_BLOB;} - enum ha_base_keytype key_type() const - { return binary() ? HA_KEYTYPE_VARBINARY2 : HA_KEYTYPE_VARTEXT2; } - Copy_func *get_copy_func(const Field *from) const - { - /* - TODO: MDEV-9331 - if (from->type() == MYSQL_TYPE_BIT) - return do_field_int; - */ - if (!(from->flags & BLOB_FLAG) || from->charset() != charset()) - return do_conv_blob; - if (from->pack_length() != Field_blob::pack_length()) - return do_copy_blob; - return get_identical_copy_func(); - } - int store_field(Field *from) - { // Be sure the value is stored - from->val_str(&value); - if (table->copy_blobs || - (!value.is_alloced() && from->is_varchar_and_in_write_set())) - value.copy(); - return store(value.ptr(), value.length(), from->charset()); - } - bool memcpy_field_possible(const Field *from) const - { - return Field_str::memcpy_field_possible(from) && - !table->copy_blobs; - } - int store(const char *to,uint length,CHARSET_INFO *charset); - int store(double nr); - int store(longlong nr, bool unsigned_val); - double val_real(void); - longlong val_int(void); - String *val_str(String*,String *); - my_decimal *val_decimal(my_decimal *); - int cmp_max(const uchar *, const uchar *, uint max_length); - int cmp(const uchar *a,const uchar *b) - { return cmp_max(a, b, ~0U); } - int cmp(const uchar *a, uint32 a_length, const uchar *b, uint32 b_length); - int cmp_binary(const uchar *a,const uchar *b, uint32 max_length=~0U); - int key_cmp(const uchar *,const uchar*); - int key_cmp(const uchar *str, uint length); - /* Never update the value of min_val for a blob field */ - bool update_min(Field *min_val, bool force_update) { return FALSE; } - /* Never update the value of max_val for a blob field */ - bool update_max(Field *max_val, bool force_update) { return FALSE; } - uint32 key_length() const { return 0; } - void sort_string(uchar *buff,uint length); - uint32 pack_length() const - { return (uint32) (packlength + portable_sizeof_char_ptr); } - - /** - Return the packed length without the pointer size added. - - This is used to determine the size of the actual data in the row - buffer. - - @returns The length of the raw data itself without the pointer. - */ - uint32 pack_length_no_ptr() const - { return (uint32) (packlength); } - uint row_pack_length() const { return pack_length_no_ptr(); } - uint32 sort_length() const; - uint32 value_length() { return get_length(); } - virtual uint32 max_data_length() const - { - return (uint32) (((ulonglong) 1 << (packlength*8)) -1); - } - int reset(void) { bzero(ptr, packlength+sizeof(uchar*)); return 0; } - void reset_fields() { bzero((uchar*) &value,sizeof(value)); bzero((uchar*) &read_value,sizeof(read_value)); } - uint32 get_field_buffer_size(void) { return value.alloced_length(); } - void store_length(uchar *i_ptr, uint i_packlength, uint32 i_number); - inline void store_length(uint32 number) - { - store_length(ptr, packlength, number); - } - inline uint32 get_length(uint row_offset= 0) const - { return get_length(ptr+row_offset, this->packlength); } - uint32 get_length(const uchar *ptr, uint packlength) const; - uint32 get_length(const uchar *ptr_arg) const - { return get_length(ptr_arg, this->packlength); } - inline uchar *get_ptr() const { return get_ptr(0); } - inline uchar *get_ptr(my_ptrdiff_t row_offset) const - { - uchar *s; - memcpy(&s, ptr + packlength + row_offset, sizeof(uchar*)); - return s; - } - inline void set_ptr(uchar *length, uchar *data) - { - memcpy(ptr,length,packlength); - memcpy(ptr+packlength, &data,sizeof(char*)); - } - void set_ptr_offset(my_ptrdiff_t ptr_diff, uint32 length, uchar *data) - { - uchar *ptr_ofs= ADD_TO_PTR(ptr,ptr_diff,uchar*); - store_length(ptr_ofs, packlength, length); - memcpy(ptr_ofs+packlength, &data, sizeof(char*)); - } - inline void set_ptr(uint32 length, uchar *data) - { - set_ptr_offset(0, length, data); - } - int copy_value(Field_blob *from); - uint get_key_image(uchar *buff,uint length, imagetype type); - void set_key_image(const uchar *buff,uint length); - Field *new_key_field(MEM_ROOT *root, TABLE *new_table, - uchar *new_ptr, uint32 length, - uchar *new_null_ptr, uint new_null_bit); - void sql_type(String &str) const; - inline bool copy() - { - uchar *tmp= get_ptr(); - if (value.copy((char*) tmp, get_length(), charset())) - { - Field_blob::reset(); - return 1; - } - tmp=(uchar*) value.ptr(); - memcpy(ptr+packlength, &tmp, sizeof(char*)); - return 0; - } - /* store value for the duration of the current read record */ - inline void swap_value_and_read_value() - { - read_value.swap(value); - } - inline void set_value(uchar *data) - { - /* Set value pointer. Lengths are not important */ - value.reset((char*) data, 1, 1, &my_charset_bin); - } - virtual uchar *pack(uchar *to, const uchar *from, uint max_length); - virtual const uchar *unpack(uchar *to, const uchar *from, - const uchar *from_end, uint param_data); - uint packed_col_length(const uchar *col_ptr, uint length); - uint max_packed_col_length(uint max_length); - void free() - { - value.free(); - read_value.free(); - } - inline void clear_temporary() - { - uchar *tmp= get_ptr(); - if (likely(value.ptr() == (char*) tmp)) - bzero((uchar*) &value, sizeof(value)); - else - { - /* - Currently read_value should never point to tmp, the following code - is mainly here to make things future proof. - */ - if (unlikely(read_value.ptr() == (char*) tmp)) - bzero((uchar*) &read_value, sizeof(read_value)); - } - } - uint size_of() const { return sizeof(*this); } - bool has_charset(void) const - { return charset() == &my_charset_bin ? FALSE : TRUE; } - uint32 max_display_length(); - uint32 char_length() const; - uint is_equal(Create_field *new_field); -private: - int do_save_field_metadata(uchar *first_byte); -}; - - -#ifdef HAVE_SPATIAL -class Field_geom :public Field_blob { -public: - enum geometry_type geom_type; - uint srid; - uint precision; - enum storage_type { GEOM_STORAGE_WKB= 0, GEOM_STORAGE_BINARY= 1}; - enum storage_type storage; - - Field_geom(uchar *ptr_arg, uchar *null_ptr_arg, uint null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg, - TABLE_SHARE *share, uint blob_pack_length, - enum geometry_type geom_type_arg, uint field_srid) - :Field_blob(ptr_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, - field_name_arg, share, blob_pack_length, &my_charset_bin) - { geom_type= geom_type_arg; srid= field_srid; } - Field_geom(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg, - TABLE_SHARE *share, enum geometry_type geom_type_arg) - :Field_blob(len_arg, maybe_null_arg, field_name_arg, &my_charset_bin) - { geom_type= geom_type_arg; srid= 0; } - enum ha_base_keytype key_type() const { return HA_KEYTYPE_VARBINARY2; } - enum_field_types type() const { return MYSQL_TYPE_GEOMETRY; } - bool can_optimize_range(const Item_bool_func *cond, - const Item *item, - bool is_eq_func) const; - void sql_type(String &str) const; - uint is_equal(Create_field *new_field); - int store(const char *to, uint length, CHARSET_INFO *charset); - int store(double nr); - int store(longlong nr, bool unsigned_val); - int store_decimal(const my_decimal *); - uint size_of() const { return sizeof(*this); } - /** - Key length is provided only to support hash joins. (compared byte for byte) - Ex: SELECT .. FROM t1,t2 WHERE t1.field_geom1=t2.field_geom2. - - The comparison is not very relevant, as identical geometry might be - represented differently, but we need to support it either way. - */ - uint32 key_length() const { return packlength; } - - /** - Non-nullable GEOMETRY types cannot have defaults, - but the underlying blob must still be reset. - */ - int reset(void) { return Field_blob::reset() || !maybe_null(); } - - geometry_type get_geometry_type() { return geom_type; }; - static geometry_type geometry_type_merge(geometry_type, geometry_type); - uint get_srid() { return srid; } -}; - -uint gis_field_options_image(uchar *buff, List &create_fields); -uint gis_field_options_read(const uchar *buf, uint buf_len, - Field_geom::storage_type *st_type,uint *precision, uint *scale, uint *srid); - -#endif /*HAVE_SPATIAL*/ - - -class Field_enum :public Field_str { - static void do_field_enum(Copy_field *copy_field); -protected: - uint packlength; -public: - TYPELIB *typelib; - Field_enum(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, - uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg, - uint packlength_arg, - TYPELIB *typelib_arg, - CHARSET_INFO *charset_arg) - :Field_str(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, - unireg_check_arg, field_name_arg, charset_arg), - packlength(packlength_arg),typelib(typelib_arg) - { - flags|=ENUM_FLAG; - } - Field *make_new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type); - enum_field_types type() const { return MYSQL_TYPE_STRING; } - enum Item_result cmp_type () const { return INT_RESULT; } - const Type_handler *cast_to_int_type_handler() const - { - return &type_handler_longlong; - } - enum ha_base_keytype key_type() const; - Copy_func *get_copy_func(const Field *from) const - { - if (eq_def(from)) - return get_identical_copy_func(); - if (real_type() == MYSQL_TYPE_ENUM && - from->real_type() == MYSQL_TYPE_ENUM) - return do_field_enum; - if (from->result_type() == STRING_RESULT) - return do_field_string; - return do_field_int; - } - int store_field(Field *from) - { - if (from->real_type() == MYSQL_TYPE_ENUM && from->val_int() == 0) - { - store_type(0); - return 0; - } - return from->save_in_field(this); - } - int save_in_field(Field *to) - { - if (to->result_type() != STRING_RESULT) - return to->store(val_int(), 0); - return save_in_field_str(to); - } - bool memcpy_field_possible(const Field *from) const { return false; } - int store(const char *to,uint length,CHARSET_INFO *charset); - int store(double nr); - int store(longlong nr, bool unsigned_val); - double val_real(void); - longlong val_int(void); - String *val_str(String*,String *); - int cmp(const uchar *,const uchar *); - void sort_string(uchar *buff,uint length); - uint32 pack_length() const { return (uint32) packlength; } - void store_type(ulonglong value); - void sql_type(String &str) const; - uint size_of() const { return sizeof(*this); } - enum_field_types real_type() const { return MYSQL_TYPE_ENUM; } - uint pack_length_from_metadata(uint field_metadata) - { return (field_metadata & 0x00ff); } - uint row_pack_length() const { return pack_length(); } - virtual bool zero_pack() const { return 0; } - bool optimize_range(uint idx, uint part) { return 0; } - bool eq_def(const Field *field) const; - bool has_charset(void) const { return TRUE; } - /* enum and set are sorted as integers */ - CHARSET_INFO *sort_charset(void) const { return &my_charset_bin; } - uint decimals() const { return 0; } - - virtual uchar *pack(uchar *to, const uchar *from, uint max_length); - virtual const uchar *unpack(uchar *to, const uchar *from, - const uchar *from_end, uint param_data); - - bool can_optimize_keypart_ref(const Item_bool_func *cond, - const Item *item) const; - bool can_optimize_group_min_max(const Item_bool_func *cond, - const Item *const_item) const - { - /* - Can't use GROUP_MIN_MAX optimization for ENUM and SET, - because the values are stored as numbers in index, - while MIN() and MAX() work as strings. - It would return the records with min and max enum numeric indexes. - "Bug#45300 MAX() and ENUM type" should be fixed first. - */ - return false; - } -private: - int do_save_field_metadata(uchar *first_byte); - uint is_equal(Create_field *new_field); -}; - - -class Field_set :public Field_enum { -public: - Field_set(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, - uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg, - uint32 packlength_arg, - TYPELIB *typelib_arg, CHARSET_INFO *charset_arg) - :Field_enum(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, - unireg_check_arg, field_name_arg, - packlength_arg, - typelib_arg,charset_arg), - empty_set_string("", 0, charset_arg) - { - flags=(flags & ~ENUM_FLAG) | SET_FLAG; - } - int store_field(Field *from) { return from->save_in_field(this); } - int store(const char *to,uint length,CHARSET_INFO *charset); - int store(double nr) { return Field_set::store((longlong) nr, FALSE); } - int store(longlong nr, bool unsigned_val); - - virtual bool zero_pack() const { return 1; } - String *val_str(String*,String *); - void sql_type(String &str) const; - uint size_of() const { return sizeof(*this); } - enum_field_types real_type() const { return MYSQL_TYPE_SET; } - bool has_charset(void) const { return TRUE; } -private: - const String empty_set_string; -}; - - -/* - Note: - To use Field_bit::cmp_binary() you need to copy the bits stored in - the beginning of the record (the NULL bytes) to each memory you - want to compare (where the arguments point). - - This is the reason: - - Field_bit::cmp_binary() is only implemented in the base class - (Field::cmp_binary()). - - Field::cmp_binary() currenly use pack_length() to calculate how - long the data is. - - pack_length() includes size of the bits stored in the NULL bytes - of the record. -*/ -class Field_bit :public Field { -public: - uchar *bit_ptr; // position in record where 'uneven' bits store - uchar bit_ofs; // offset to 'uneven' high bits - uint bit_len; // number of 'uneven' high bits - uint bytes_in_rec; - Field_bit(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, - uchar null_bit_arg, uchar *bit_ptr_arg, uchar bit_ofs_arg, - enum utype unireg_check_arg, const char *field_name_arg); - enum_field_types type() const { return MYSQL_TYPE_BIT; } - enum ha_base_keytype key_type() const { return HA_KEYTYPE_BIT; } - uint32 key_length() const { return (uint32) (field_length + 7) / 8; } - uint32 max_data_length() const { return (field_length + 7) / 8; } - uint32 max_display_length() { return field_length; } - uint size_of() const { return sizeof(*this); } - Item_result result_type () const { return INT_RESULT; } - int reset(void) { - bzero(ptr, bytes_in_rec); - if (bit_ptr && (bit_len > 0)) // reset odd bits among null bits - clr_rec_bits(bit_ptr, bit_ofs, bit_len); - return 0; - } - Copy_func *get_copy_func(const Field *from) const - { - return do_field_int; - } - int save_in_field(Field *to) { return to->store(val_int(), true); } - bool memcpy_field_possible(const Field *from) const { return false; } - int store(const char *to, uint length, CHARSET_INFO *charset); - int store(double nr); - int store(longlong nr, bool unsigned_val); - int store_decimal(const my_decimal *); - double val_real(void); - longlong val_int(void); - String *val_str(String*, String *); - virtual bool str_needs_quotes() { return TRUE; } - my_decimal *val_decimal(my_decimal *); - bool val_bool() { return val_int() != 0; } - int cmp(const uchar *a, const uchar *b) - { - DBUG_ASSERT(ptr == a || ptr == b); - if (ptr == a) - return Field_bit::key_cmp(b, bytes_in_rec + MY_TEST(bit_len)); - else - return Field_bit::key_cmp(a, bytes_in_rec + MY_TEST(bit_len)) * -1; - } - int cmp_binary_offset(uint row_offset) - { return cmp_offset(row_offset); } - int cmp_max(const uchar *a, const uchar *b, uint max_length); - int key_cmp(const uchar *a, const uchar *b) - { return cmp_binary((uchar *) a, (uchar *) b); } - int key_cmp(const uchar *str, uint length); - int cmp_offset(uint row_offset); - bool update_min(Field *min_val, bool force_update) - { - longlong val= val_int(); - bool update_fl= force_update || val < min_val->val_int(); - if (update_fl) - { - min_val->set_notnull(); - min_val->store(val, FALSE); - } - return update_fl; - } - bool update_max(Field *max_val, bool force_update) - { - longlong val= val_int(); - bool update_fl= force_update || val > max_val->val_int(); - if (update_fl) - { - max_val->set_notnull(); - max_val->store(val, FALSE); - } - return update_fl; - } - void store_field_value(uchar *val, uint len) - { - store(*((longlong *)val), TRUE); - } - double pos_in_interval(Field *min, Field *max) - { - return pos_in_interval_val_real(min, max); - } - void get_image(uchar *buff, uint length, CHARSET_INFO *cs) - { get_key_image(buff, length, itRAW); } - void set_image(const uchar *buff,uint length, CHARSET_INFO *cs) - { Field_bit::store((char *) buff, length, cs); } - uint get_key_image(uchar *buff, uint length, imagetype type); - void set_key_image(const uchar *buff, uint length) - { Field_bit::store((char*) buff, length, &my_charset_bin); } - void sort_string(uchar *buff, uint length) - { get_key_image(buff, length, itRAW); } - uint32 pack_length() const { return (uint32) (field_length + 7) / 8; } - uint32 pack_length_in_rec() const { return bytes_in_rec; } - uint pack_length_from_metadata(uint field_metadata); - uint row_pack_length() const - { return (bytes_in_rec + ((bit_len > 0) ? 1 : 0)); } - bool compatible_field_size(uint metadata, Relay_log_info *rli, - uint16 mflags, int *order_var); - void sql_type(String &str) const; - virtual uchar *pack(uchar *to, const uchar *from, uint max_length); - virtual const uchar *unpack(uchar *to, const uchar *from, - const uchar *from_end, uint param_data); - virtual void set_default(); - - Field *new_key_field(MEM_ROOT *root, TABLE *new_table, - uchar *new_ptr, uint32 length, - uchar *new_null_ptr, uint new_null_bit); - void set_bit_ptr(uchar *bit_ptr_arg, uchar bit_ofs_arg) - { - bit_ptr= bit_ptr_arg; - bit_ofs= bit_ofs_arg; - } - bool eq(Field *field) - { - return (Field::eq(field) && - bit_ptr == ((Field_bit *)field)->bit_ptr && - bit_ofs == ((Field_bit *)field)->bit_ofs); - } - uint is_equal(Create_field *new_field); - void move_field_offset(my_ptrdiff_t ptr_diff) - { - Field::move_field_offset(ptr_diff); - bit_ptr= ADD_TO_PTR(bit_ptr, ptr_diff, uchar*); - } - void hash(ulong *nr, ulong *nr2); - -private: - virtual size_t do_last_null_byte() const; - int do_save_field_metadata(uchar *first_byte); -}; - - -/** - BIT field represented as chars for non-MyISAM tables. - - @todo The inheritance relationship is backwards since Field_bit is - an extended version of Field_bit_as_char and not the other way - around. Hence, we should refactor it to fix the hierarchy order. - */ -class Field_bit_as_char: public Field_bit { -public: - Field_bit_as_char(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, - uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg); - enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; } - uint size_of() const { return sizeof(*this); } - int store(const char *to, uint length, CHARSET_INFO *charset); - int store(double nr) { return Field_bit::store(nr); } - int store(longlong nr, bool unsigned_val) - { return Field_bit::store(nr, unsigned_val); } - void sql_type(String &str) const; -}; - - -extern const LEX_STRING null_lex_str; - - -Field *make_field(TABLE_SHARE *share, MEM_ROOT *mem_root, - uchar *ptr, uint32 field_length, - uchar *null_pos, uchar null_bit, - uint pack_flag, enum_field_types field_type, - CHARSET_INFO *cs, - Field::geometry_type geom_type, uint srid, - Field::utype unireg_check, - TYPELIB *interval, const char *field_name); - -/* - Create field class for CREATE TABLE -*/ -class Column_definition: public Sql_alloc -{ - /** - Create "interval" from "interval_list". - @param mem_root - memory root to create the TYPELIB - instance and its values on - @param reuse_interval_list_values - determines if TYPELIB can reuse strings - from interval_list, or should always - allocate a copy on mem_root, even if - character set conversion is not needed - @retval false on success - @retval true on error (bad values, or EOM) - */ - bool create_interval_from_interval_list(MEM_ROOT *mem_root, - bool reuse_interval_list_values); - - /* - Calculate TYPELIB (set or enum) max and total lengths - - @param cs charset+collation pair of the interval - @param max_length length of the longest item - @param tot_length sum of the item lengths - - After this method call: - - ENUM uses max_length - - SET uses tot_length. - */ - void calculate_interval_lengths(uint32 *max_length, uint32 *tot_length) - { - const char **pos; - uint *len; - *max_length= *tot_length= 0; - for (pos= interval->type_names, len= interval->type_lengths; - *pos ; pos++, len++) - { - size_t length= charset->cset->numchars(charset, *pos, *pos + *len); - *tot_length+= length; - set_if_bigger(*max_length, (uint32)length); - } - } -public: - enum enum_column_versioning - { - VERSIONING_NOT_SET, - WITH_VERSIONING, - WITHOUT_VERSIONING - }; - - const char *field_name; - LEX_STRING comment; // Comment for field - Item *on_update; // ON UPDATE NOW() - enum enum_field_types sql_type; - /* - At various stages in execution this can be length of field in bytes or - max number of characters. - */ - ulonglong length; - /* - The value of `length' as set by parser: is the number of characters - for most of the types, or of bytes for BLOBs or numeric types. - */ - uint32 char_length; - uint decimals, flags, pack_length, key_length; - Field::utype unireg_check; - TYPELIB *interval; // Which interval to use - List interval_list; - CHARSET_INFO *charset; - uint32 srid; - Field::geometry_type geom_type; - engine_option_value *option_list; - - uint pack_flag; - - /* - This is additinal data provided for any computed(virtual) field. - In particular it includes a pointer to the item by which this field - can be computed from other fields. - */ - Virtual_column_info - *vcol_info, // Virtual field - *default_value, // Default value - *check_constraint; // Check constraint - - enum_column_versioning versioning; - - Column_definition(): - comment(null_lex_str), - on_update(NULL), sql_type(MYSQL_TYPE_NULL), length(0), decimals(0), - flags(0), pack_length(0), key_length(0), unireg_check(Field::NONE), - interval(0), charset(&my_charset_bin), - srid(0), geom_type(Field::GEOM_GEOMETRY), - option_list(NULL), - vcol_info(0), default_value(0), check_constraint(0), - versioning(VERSIONING_NOT_SET) - { - interval_list.empty(); - } - - Column_definition(THD *thd, Field *field, Field *orig_field); - void set_attributes(const Lex_field_type_st &type, CHARSET_INFO *cs); - void create_length_to_internal_length(void); - - /** - Prepare a SET/ENUM field. - Create "interval" from "interval_list" if needed, and adjust "length". - @param mem_root - Memory root to allocate TYPELIB and - its values on - @param reuse_interval_list_values - determines if TYPELIB can reuse value - buffers from interval_list, or should - always allocate a copy on mem_root, - even if character set conversion - is not needed - */ - bool prepare_interval_field(MEM_ROOT *mem_root, - bool reuse_interval_list_values); - - void prepare_interval_field_calc_length() - { - uint32 field_length, dummy; - if (sql_type == MYSQL_TYPE_SET) - { - calculate_interval_lengths(&dummy, &field_length); - length= field_length + (interval->count - 1); - } - else /* MYSQL_TYPE_ENUM */ - { - calculate_interval_lengths(&field_length, &dummy); - length= field_length; - } - set_if_smaller(length, MAX_FIELD_WIDTH - 1); - } - - bool prepare_blob_field(THD *thd); - - bool sp_prepare_create_field(THD *thd, MEM_ROOT *mem_root); - - bool prepare_create_field(uint *blob_columns, ulonglong table_flags); - - bool check(THD *thd); - - bool stored_in_db() const { return !vcol_info || vcol_info->stored_in_db; } - - ha_storage_media field_storage_type() const - { - return (ha_storage_media) - ((flags >> FIELD_FLAGS_STORAGE_MEDIA) & 3); - } - - column_format_type column_format() const - { - return (column_format_type) - ((flags >> FIELD_FLAGS_COLUMN_FORMAT) & 3); - } - - bool has_default_function() const - { - return unireg_check != Field::NONE; - } - - Field *make_field(TABLE_SHARE *share, MEM_ROOT *mem_root, - uchar *ptr, uchar *null_pos, uchar null_bit, - const char *field_name_arg) const - { - return ::make_field(share, mem_root, ptr, - (uint32)length, null_pos, null_bit, - pack_flag, sql_type, charset, - geom_type, srid, unireg_check, interval, - field_name_arg); - } - Field *make_field(TABLE_SHARE *share, MEM_ROOT *mem_root, - const char *field_name_arg) - { - return make_field(share, mem_root, (uchar *) 0, (uchar *) "", 0, - field_name_arg); - } - /* Return true if default is an expression that must be saved explicitely */ - bool has_default_expression(); - - bool has_default_now_unireg_check() const - { - return unireg_check == Field::TIMESTAMP_DN_FIELD - || unireg_check == Field::TIMESTAMP_DNUN_FIELD; - } -}; - - -class Create_field :public Column_definition -{ -public: - const char *change; // If done with alter table - const char *after; // Put column after this one - Field *field; // For alter table - TYPELIB *save_interval; // Temporary copy for the above - // Used only for UCS2 intervals - - /** structure with parsed options (for comparing fields in ALTER TABLE) */ - ha_field_option_struct *option_struct; - uint offset; - uint8 interval_id; // For rea_create_table - bool create_if_not_exists; // Used in ALTER TABLE IF NOT EXISTS - - Create_field(): - Column_definition(), change(0), after(0), - field(0), option_struct(NULL), - create_if_not_exists(false) - { } - Create_field(THD *thd, Field *old_field, Field *orig_field): - Column_definition(thd, old_field, orig_field), - change(old_field->field_name), after(0), - field(old_field), option_struct(old_field->option_struct), - create_if_not_exists(false) - { } - /* Used to make a clone of this object for ALTER/CREATE TABLE */ - Create_field *clone(MEM_ROOT *mem_root) const; -}; - - -/* - A class for sending info to the client -*/ - -class Send_field :public Sql_alloc { - public: - const char *db_name; - const char *table_name,*org_table_name; - const char *col_name,*org_col_name; - ulong length; - uint flags, decimals; - enum_field_types type; - Send_field() {} -}; - - -/* - A class for quick copying data to fields -*/ - -class Copy_field :public Sql_alloc { -public: - uchar *from_ptr,*to_ptr; - uchar *from_null_ptr,*to_null_ptr; - bool *null_row; - uint from_bit,to_bit; - /** - Number of bytes in the fields pointed to by 'from_ptr' and - 'to_ptr'. Usually this is the number of bytes that are copied from - 'from_ptr' to 'to_ptr'. - - For variable-length fields (VARCHAR), the first byte(s) describe - the actual length of the text. For VARCHARs with length - < 256 there is 1 length byte - >= 256 there is 2 length bytes - Thus, if from_field is VARCHAR(10), from_length (and in most cases - to_length) is 11. For VARCHAR(1024), the length is 1026. @see - Field_varstring::length_bytes - - Note that for VARCHARs, do_copy() will be do_varstring*() which - only copies the length-bytes (1 or 2) + the actual length of the - text instead of from/to_length bytes. - */ - uint from_length,to_length; - Field *from_field,*to_field; - String tmp; // For items - - Copy_field() {} - ~Copy_field() {} - void set(Field *to,Field *from,bool save); // Field to field - void set(uchar *to,Field *from); // Field to string - void (*do_copy)(Copy_field *); - void (*do_copy2)(Copy_field *); // Used to handle null values -}; - - -uint pack_length_to_packflag(uint type); -enum_field_types get_blob_type_from_length(ulong length); -uint32 calc_pack_length(enum_field_types type,uint32 length); -int set_field_to_null(Field *field); -int set_field_to_null_with_conversions(Field *field, bool no_conversions); -int convert_null_to_field_value_or_error(Field *field); -bool check_expression(Virtual_column_info *vcol, const char *name, - enum_vcol_info_type type); - -/* - The following are for the interface with the .frm file -*/ - -#define FIELDFLAG_DECIMAL 1U -#define FIELDFLAG_BINARY 1U // Shares same flag -#define FIELDFLAG_NUMBER 2U -#define FIELDFLAG_ZEROFILL 4U -#define FIELDFLAG_PACK 120U // Bits used for packing -#define FIELDFLAG_INTERVAL 256U // mangled with decimals! -#define FIELDFLAG_BITFIELD 512U // mangled with decimals! -#define FIELDFLAG_BLOB 1024U // mangled with decimals! -#define FIELDFLAG_GEOM 2048U // mangled with decimals! - -#define FIELDFLAG_TREAT_BIT_AS_CHAR 4096U /* use Field_bit_as_char */ -#define FIELDFLAG_LONG_DECIMAL 8192U -#define FIELDFLAG_WITHOUT_SYSTEM_VERSIONING 8192U -#define FIELDFLAG_NO_DEFAULT 16384U /* sql */ -#define FIELDFLAG_MAYBE_NULL 32768U // sql -#define FIELDFLAG_HEX_ESCAPE 0x10000U -#define FIELDFLAG_PACK_SHIFT 3 -#define FIELDFLAG_DEC_SHIFT 8 -#define FIELDFLAG_MAX_DEC 63U - -#define MTYP_TYPENR(type) (type & 127U) /* Remove bits from type */ - -#define f_is_dec(x) ((x) & FIELDFLAG_DECIMAL) -#define f_is_num(x) ((x) & FIELDFLAG_NUMBER) -#define f_is_zerofill(x) ((x) & FIELDFLAG_ZEROFILL) -#define f_is_packed(x) ((x) & FIELDFLAG_PACK) -#define f_packtype(x) (((x) >> FIELDFLAG_PACK_SHIFT) & 15) -#define f_decimals(x) ((uint8) (((x) >> FIELDFLAG_DEC_SHIFT) & FIELDFLAG_MAX_DEC)) -#define f_is_alpha(x) (!f_is_num(x)) -#define f_is_binary(x) ((x) & FIELDFLAG_BINARY) // 4.0- compatibility -#define f_is_enum(x) (((x) & (FIELDFLAG_INTERVAL | FIELDFLAG_NUMBER)) == FIELDFLAG_INTERVAL) -#define f_is_bitfield(x) (((x) & (FIELDFLAG_BITFIELD | FIELDFLAG_NUMBER)) == FIELDFLAG_BITFIELD) -#define f_is_blob(x) (((x) & (FIELDFLAG_BLOB | FIELDFLAG_NUMBER)) == FIELDFLAG_BLOB) -#define f_is_geom(x) (((x) & (FIELDFLAG_GEOM | FIELDFLAG_NUMBER)) == FIELDFLAG_GEOM) -#define f_settype(x) (((uint) (x)) << FIELDFLAG_PACK_SHIFT) -#define f_maybe_null(x) ((x) & FIELDFLAG_MAYBE_NULL) -#define f_no_default(x) ((x) & FIELDFLAG_NO_DEFAULT) -#define f_bit_as_char(x) ((x) & FIELDFLAG_TREAT_BIT_AS_CHAR) -#define f_is_hex_escape(x) ((x) & FIELDFLAG_HEX_ESCAPE) -#define f_without_system_versioning(x) ((x) & FIELDFLAG_WITHOUT_SYSTEM_VERSIONING) -#define f_hidden(x) ((x) & FIELDFLAG_HIDDEN) - -#endif /* FIELD_INCLUDED */ -- cgit v1.2.1 From 448374a228aee3cd867d89f1a1eae9884f5bf434 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Thu, 15 Jun 2017 16:02:32 +0300 Subject: SQL, IB: (0.10) VTMD tracking [closes #124] IB: Fixes in logic when to do versioned or usual row updates. Now it is able to do unversioned updates for versioned tables just by disabling `TABLE_SHARE::versioned` flag. SQL: DDL tracking for: * RENAME TABLE, ALTER TABLE .. RENAME TO; * DROP TABLE; * data-modifying operations (f.ex. ALTER TABLE .. ADD/DROP COLUMN). --- sql/CMakeLists.txt | 1 + sql/handler.h | 6 + sql/share/errmsg-utf8.txt | 3 + sql/sql_alter.h | 16 ++ sql/sql_error.h | 2 +- sql/sql_lex.cc | 13 ++ sql/sql_lex.h | 12 ++ sql/sql_rename.cc | 22 ++- sql/sql_table.cc | 115 ++++++++--- sql/table.cc | 33 +++- sql/table.h | 2 + sql/unireg.cc | 12 ++ sql/unireg.h | 1 + sql/vers_utils.h | 165 ++++++++++++++++ sql/vtmd.cc | 495 ++++++++++++++++++++++++++++++++++++++++++++++ sql/vtmd.h | 175 ++++++++++++++++ 16 files changed, 1036 insertions(+), 37 deletions(-) create mode 100644 sql/vers_utils.h create mode 100644 sql/vtmd.cc create mode 100644 sql/vtmd.h (limited to 'sql') diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index ee34891d0c9..bb9e844a120 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -152,6 +152,7 @@ SET (SQL_SOURCE ${GEN_SOURCES} ${GEN_DIGEST_SOURCES} ${MYSYS_LIBWRAP_SOURCE} + vtmd.cc ) IF (CMAKE_SYSTEM_NAME MATCHES "Linux" OR diff --git a/sql/handler.h b/sql/handler.h index c46ee422565..807ae234fef 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -403,6 +403,7 @@ enum enum_alter_inplace_result { #define HA_CREATE_TMP_ALTER 8U #define HA_LEX_CREATE_SEQUENCE 16U #define HA_VERSIONED_TABLE 32U +#define HA_VTMD 64U #define HA_MAX_REC_LENGTH 65535 @@ -1840,6 +1841,11 @@ struct Table_scope_and_contents_source_st { return options & HA_VERSIONED_TABLE; } + + bool vtmd() const + { + return options & HA_VTMD; + } }; diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 13408634738..17bd8a983a7 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7547,5 +7547,8 @@ ER_VERS_WRONG_QUERY WARN_VERS_ALIAS_TOO_LONG eng "Auto generated alias for `%s.%s` is too long; using `%s`." +ER_VERS_VTMD_ERROR + eng "VTMD error: %s" + ER_WRONG_TABLESPACE_NAME 42000 eng "Incorrect tablespace name `%-.192s`" diff --git a/sql/sql_alter.h b/sql/sql_alter.h index 5668a0f52be..a1c2d87440b 100644 --- a/sql/sql_alter.h +++ b/sql/sql_alter.h @@ -126,6 +126,22 @@ public: enum enum_enable_or_disable { LEAVE_AS_IS, ENABLE, DISABLE }; + bool vers_data_modifying() const + { + return flags & ( + ALTER_ADD_COLUMN | + ALTER_DROP_COLUMN | + ALTER_CHANGE_COLUMN | + ALTER_DROP_PARTITION | + ALTER_COALESCE_PARTITION | + ALTER_REORGANIZE_PARTITION | + ALTER_TABLE_REORG | + ALTER_REMOVE_PARTITIONING | + ALTER_EXCHANGE_PARTITION | + ALTER_TRUNCATE_PARTITION | + ALTER_COLUMN_ORDER); + } + /** The different values of the ALGORITHM clause. Describes which algorithm to use when altering the table. diff --git a/sql/sql_error.h b/sql/sql_error.h index bbe97333210..0d8aa48a11a 100644 --- a/sql/sql_error.h +++ b/sql/sql_error.h @@ -1171,7 +1171,7 @@ public: void copy_non_errors_from_wi(THD *thd, const Warning_info *src_wi); -private: +protected: Warning_info *get_warning_info() { return m_wi_stack.front(); } const Warning_info *get_warning_info() const { return m_wi_stack.front(); } diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 7c334d7ce67..067b78e3a05 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -7029,6 +7029,19 @@ int set_statement_var_if_exists(THD *thd, const char *var_name, } +Query_tables_backup::Query_tables_backup(THD* _thd) : + thd(_thd) +{ + thd->lex->reset_n_backup_query_tables_list(&backup); +} + + +Query_tables_backup::~Query_tables_backup() +{ + thd->lex->restore_backup_query_tables_list(&backup); +} + + bool LEX::sp_add_cfetch(THD *thd, const LEX_STRING &name) { uint offset; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 66f943e9f17..5f6232c2c50 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -1914,6 +1914,18 @@ private: }; +class Query_tables_backup +{ + THD *thd; + Query_tables_list backup; + +public: + Query_tables_backup(THD *_thd); + ~Query_tables_backup(); + const Query_tables_list& get() const { return backup; } +}; + + /* st_parsing_options contains the flags for constructions that are allowed in the current statement. diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc index 1588644f0e1..9b80ef78c63 100644 --- a/sql/sql_rename.cc +++ b/sql/sql_rename.cc @@ -30,6 +30,7 @@ #include "sql_base.h" // tdc_remove_table, lock_table_names, #include "sql_handler.h" // mysql_ha_rm_tables #include "sql_statistics.h" +#include "vtmd.h" static TABLE_LIST *rename_tables(THD *thd, TABLE_LIST *table_list, bool skip_error); @@ -298,12 +299,23 @@ do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db, char *new_table_name, LEX_STRING new_db_name= { (char*)new_db, strlen(new_db)}; (void) rename_table_in_stat_tables(thd, &db_name, &table_name, &new_db_name, &new_table); - if ((rc= Table_triggers_list::change_table_name(thd, ren_table->db, - old_alias, - ren_table->table_name, - new_db, - new_alias))) + VTMD_rename vtmd(*ren_table); + if (thd->variables.vers_ddl_survival) { + rc= vtmd.try_rename(thd, new_db_name, new_table); + if (rc) + goto revert_table_name; + } + rc= Table_triggers_list::change_table_name(thd, ren_table->db, + old_alias, + ren_table->table_name, + new_db, + new_alias); + if (rc) + { + if (thd->variables.vers_ddl_survival) + vtmd.revert_rename(thd, new_db_name); +revert_table_name: /* We've succeeded in renaming table's .frm and in updating corresponding handler data, but have failed to update table's diff --git a/sql/sql_table.cc b/sql/sql_table.cc index efb2969f548..7aeb1854e24 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -56,6 +56,7 @@ #include "sql_audit.h" #include "sql_sequence.h" #include "tztime.h" +#include "vtmd.h" // System Versioning #ifdef __WIN__ @@ -2276,6 +2277,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, char *db=table->db; size_t db_length= table->db_length; handlerton *table_type= 0; + VTMD_drop vtmd(*table); DBUG_PRINT("table", ("table_l: '%s'.'%s' table: 0x%lx s: 0x%lx", table->db, table->table_name, (long) table->table, @@ -2474,8 +2476,24 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, // Remove extension for delete *(end= path + path_length - reg_ext_length)= '\0'; - error= ha_delete_table(thd, table_type, path, db, table->table_name, - !dont_log_query); + if (thd->lex->sql_command == SQLCOM_DROP_TABLE && + thd->variables.vers_ddl_survival && + table_type && table_type != view_pseudo_hton) + { + error= vtmd.check_exists(thd); + if (error) + goto non_tmp_err; + if (!vtmd.exists) + goto drop_table; + error= mysql_rename_table(table_type, table->db, table->table_name, + table->db, vtmd.archive_name(thd), NO_FK_CHECKS); + } + else + { + drop_table: + error= ha_delete_table(thd, table_type, path, db, table->table_name, + !dont_log_query); + } if (!error) { @@ -2510,8 +2528,18 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, else if (frm_delete_error && if_exists) thd->clear_error(); } + non_tmp_err: non_tmp_error|= MY_TEST(error); } + + if (!error && vtmd.exists) + { + error= vtmd.update(thd); + if (error) + mysql_rename_table(table_type, table->db, vtmd.archive_name(), + table->db, table->table_name, NO_FK_CHECKS); + } + if (error) { if (wrong_tables.length()) @@ -5040,6 +5068,7 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table, { thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0); result= 1; + goto err; } else { @@ -5048,6 +5077,16 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table, } } + if (create_info->versioned() && thd->variables.vers_ddl_survival) + { + VTMD_table vtmd(*create_table); + if (vtmd.update(thd)) + { + result= 1; + goto err; + } + } + err: /* In RBR we don't need to log CREATE TEMPORARY TABLE */ if (thd->is_current_stmt_binlog_format_row() && create_info->tmp_table()) @@ -5155,15 +5194,6 @@ static void make_unique_constraint_name(THD *thd, LEX_STRING *name, ** Alter a table definition ****************************************************************************/ -static void vers_table_name_date(THD *thd, const char *table_name, - char *new_name, size_t new_name_size) -{ - const MYSQL_TIME now= thd->query_start_TIME(); - my_snprintf(new_name, new_name_size, "%s_%04d%02d%02d_%02d%02d%02d_%06d", - table_name, now.year, now.month, now.day, now.hour, now.minute, - now.second, now.second_part); -} - bool operator!=(const MYSQL_TIME &lhs, const MYSQL_TIME &rhs) { return lhs.year != rhs.year || lhs.month != rhs.month || lhs.day != rhs.day || @@ -5466,6 +5496,7 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, /* Replace type of source table with one specified in the statement. */ local_create_info.options&= ~HA_LEX_CREATE_TMP_TABLE; local_create_info.options|= create_info->tmp_table(); + local_create_info.options|= create_info->options; /* Reset auto-increment counter for the new table. */ local_create_info.auto_increment_value= 0; /* @@ -8529,18 +8560,27 @@ simple_rename_or_index_change(THD *thd, TABLE_LIST *table_list, if (mysql_rename_table(old_db_type, alter_ctx->db, alter_ctx->table_name, alter_ctx->new_db, alter_ctx->new_alias, 0)) error= -1; - else if (Table_triggers_list::change_table_name(thd, - alter_ctx->db, - alter_ctx->alias, - alter_ctx->table_name, - alter_ctx->new_db, - alter_ctx->new_alias)) - { - (void) mysql_rename_table(old_db_type, - alter_ctx->new_db, alter_ctx->new_alias, - alter_ctx->db, alter_ctx->table_name, - NO_FK_CHECKS); - error= -1; + else + { + VTMD_rename vtmd(*table_list); + if (thd->variables.vers_ddl_survival && vtmd.try_rename(thd, new_db_name, new_table_name)) + goto revert_table_name; + else if (Table_triggers_list::change_table_name(thd, + alter_ctx->db, + alter_ctx->alias, + alter_ctx->table_name, + alter_ctx->new_db, + alter_ctx->new_alias)) + { + if (thd->variables.vers_ddl_survival) + vtmd.revert_rename(thd, new_db_name); +revert_table_name: + (void) mysql_rename_table(old_db_type, + alter_ctx->new_db, alter_ctx->new_alias, + alter_ctx->db, alter_ctx->table_name, + NO_FK_CHECKS); + error= -1; + } } } @@ -8672,7 +8712,11 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, &alter_prelocking_strategy); thd->open_options&= ~HA_OPEN_FOR_ALTER; bool versioned= table_list->table && table_list->table->versioned(); - if (versioned && thd->variables.vers_ddl_survival) + bool vers_data_mod= versioned && + thd->variables.vers_ddl_survival && + alter_info->vers_data_modifying(); + + if (vers_data_mod) { table_list->set_lock_type(thd, TL_WRITE); if (thd->mdl_context.upgrade_shared_lock(table_list->table->mdl_ticket, @@ -9003,7 +9047,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, Upgrade from MDL_SHARED_UPGRADABLE to MDL_SHARED_NO_WRITE. Afterwards it's safe to take the table level lock. */ - if ((!(versioned && thd->variables.vers_ddl_survival) && + if ((!vers_data_mod && thd->mdl_context.upgrade_shared_lock( mdl_ticket, MDL_SHARED_NO_WRITE, thd->variables.lock_wait_timeout)) || @@ -9435,8 +9479,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, alter_info->keys_onoff, &alter_ctx)) { - if (table->versioned_by_sql() && new_versioned && - thd->variables.vers_ddl_survival) + if (vers_data_mod && new_versioned && table->versioned_by_sql()) { // Failure of this function may result in corruption of an original table. vers_reset_alter_copy(thd, table); @@ -9538,8 +9581,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, anything goes wrong while renaming the new table. */ char backup_name[FN_LEN]; - if (versioned && thd->variables.vers_ddl_survival) - vers_table_name_date(thd, alter_ctx.table_name, backup_name, + if (vers_data_mod) + VTMD_table::archive_name(thd, alter_ctx.table_name, backup_name, sizeof(backup_name)); else my_snprintf(backup_name, sizeof(backup_name), "%s2-%lx-%lx", @@ -9572,6 +9615,17 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, goto err_with_mdl; } + if (vers_data_mod && new_versioned) + { + DBUG_ASSERT(alter_info && table_list); + VTMD_rename vtmd(*table_list); + bool rc= alter_info->flags & Alter_info::ALTER_RENAME ? + vtmd.try_rename(thd, alter_ctx.new_db, alter_ctx.new_alias, backup_name) : + vtmd.update(thd, backup_name); + if (rc) + goto err_after_rename; + } + // Check if we renamed the table and if so update trigger files. if (alter_ctx.is_table_renamed()) { @@ -9582,6 +9636,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, alter_ctx.new_db, alter_ctx.new_alias)) { +err_after_rename: // Rename succeeded, delete the new table. (void) quick_rm_table(thd, new_db_type, alter_ctx.new_db, alter_ctx.new_alias, 0); @@ -9596,7 +9651,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, } // ALTER TABLE succeeded, delete the backup of the old table. - if (!(versioned && new_versioned && thd->variables.vers_ddl_survival) && + if (!(vers_data_mod && new_versioned) && quick_rm_table(thd, old_db_type, alter_ctx.db, backup_name, FN_IS_TMP)) { /* diff --git a/sql/table.cc b/sql/table.cc index c4f3a05f5c5..b2a511d89f4 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -1201,6 +1201,8 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, uint ext_key_parts= 0; plugin_ref se_plugin= 0; const uchar *system_period= 0; + bool vtmd_used= false; + share->vtmd= false; const uchar *extra2_field_flags= 0; size_t extra2_field_flags_length= 0; @@ -1239,7 +1241,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, if (*extra2 != '/') // old frm had '/' there { const uchar *e2end= extra2 + len; - while (extra2 + 3 < e2end) + while (extra2 + 3 <= e2end) { uchar type= *extra2++; size_t length= *extra2++; @@ -1308,6 +1310,14 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, extra2_field_flags= extra2; extra2_field_flags_length= length; break; + case EXTRA2_VTMD: + if (vtmd_used) + goto err; + share->vtmd= *extra2; + if (share->vtmd) + share->table_category= TABLE_CATEGORY_LOG; + vtmd_used= true; + break; default: /* abort frm parsing if it's an unknown but important extra2 value */ if (type >= EXTRA2_ENGINE_IMPORTANT) @@ -7642,6 +7652,27 @@ void TABLE::vers_update_fields() DBUG_VOID_RETURN; } + +bool TABLE_LIST::vers_vtmd_name(String& out) const +{ + static const char *vtmd_suffix= "_vtmd"; + static const size_t vtmd_suffix_len= strlen(vtmd_suffix); + if (table_name_length > NAME_CHAR_LEN - vtmd_suffix_len) + { + my_printf_error(ER_VERS_VTMD_ERROR, "Table name is longer than %d characters", MYF(0), int(NAME_CHAR_LEN - vtmd_suffix_len)); + return true; + } + out.set(table_name, table_name_length, table_alias_charset); + if (out.append(vtmd_suffix, vtmd_suffix_len + 1)) + { + my_message(ER_VERS_VTMD_ERROR, "Failed allocate VTMD name", MYF(0)); + return true; + } + out.length(out.length() - 1); + return false; +} + + /** Reset markers that fields are being updated */ diff --git a/sql/table.h b/sql/table.h index 32ad43b0ae9..ae04c58c407 100644 --- a/sql/table.h +++ b/sql/table.h @@ -751,6 +751,7 @@ struct TABLE_SHARE */ bool versioned; + bool vtmd; uint16 row_start_field; uint16 row_end_field; uint32 hist_part_id; @@ -2354,6 +2355,7 @@ struct TABLE_LIST /* System Versioning */ vers_select_conds_t vers_conditions; + bool vers_vtmd_name(String &out) const; /** @brief diff --git a/sql/unireg.cc b/sql/unireg.cc index 9b4ee324aa3..cffc98d2e35 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -268,6 +268,11 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table, extra2_size+= 1 + 1 + 2 * sizeof(uint16); } + if (create_info->vtmd()) + { + extra2_size+= 1 + 1 + 1; + } + bool has_extra2_field_flags_= has_extra2_field_flags(create_fields); if (has_extra2_field_flags_) { @@ -340,6 +345,13 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table, pos+= sizeof(uint16); } + if (create_info->vtmd()) + { + *pos++= EXTRA2_VTMD; + *pos++= 1; + *pos++= 1; + } + if (has_extra2_field_flags_) { *pos++= EXTRA2_FIELD_FLAGS; diff --git a/sql/unireg.h b/sql/unireg.h index a47114054e1..bf6f0b0209c 100644 --- a/sql/unireg.h +++ b/sql/unireg.h @@ -174,6 +174,7 @@ enum extra2_frm_value_type { EXTRA2_GIS=2, EXTRA2_PERIOD_FOR_SYSTEM_TIME=4, EXTRA2_FIELD_FLAGS=8, + EXTRA2_VTMD=16, #define EXTRA2_ENGINE_IMPORTANT 128 diff --git a/sql/vers_utils.h b/sql/vers_utils.h new file mode 100644 index 00000000000..ee08fcbb2bc --- /dev/null +++ b/sql/vers_utils.h @@ -0,0 +1,165 @@ +#ifndef VERS_UTILS_INCLUDED +#define VERS_UTILS_INCLUDED + +#include "table.h" +#include "sql_class.h" + +class MDL_auto_lock +{ + THD *thd; + TABLE_LIST &table; + bool error; + +public: + MDL_auto_lock(THD *_thd, TABLE_LIST &_table) : + thd(_thd), table(_table) + { + DBUG_ASSERT(thd); + table.mdl_request.init(MDL_key::TABLE, table.db, table.table_name, MDL_EXCLUSIVE, MDL_EXPLICIT); + error= thd->mdl_context.acquire_lock(&table.mdl_request, thd->variables.lock_wait_timeout); + } + ~MDL_auto_lock() + { + if (!error) + { + DBUG_ASSERT(table.mdl_request.ticket); + thd->mdl_context.release_lock(table.mdl_request.ticket); + table.mdl_request.ticket= NULL; + } + } + bool acquire_error() const { return error; } +}; + +struct Compare_strncmp +{ + int operator()(const LEX_STRING& a, const LEX_STRING& b) const + { + return strncmp(a.str, b.str, a.length); + } + static CHARSET_INFO* charset() + { + return system_charset_info; + } +}; + +template +struct Compare_my_strcasecmp +{ + int operator()(const LEX_STRING& a, const LEX_STRING& b) const + { + DBUG_ASSERT(a.str[a.length] == 0 && b.str[b.length] == 0); + return my_strcasecmp(CS, a.str, b.str); + } + static CHARSET_INFO* charset() + { + return CS; + } +}; + +typedef Compare_my_strcasecmp Compare_fs; +typedef Compare_my_strcasecmp Compare_t; + +struct LEX_STRING_u : public LEX_STRING +{ + LEX_STRING_u() + { + str= NULL; + LEX_STRING::length= 0; + } + LEX_STRING_u(const char *_str, uint32 _len, CHARSET_INFO *) + { + str= const_cast(_str); + LEX_STRING::length= _len; + } + uint32 length() const + { + return LEX_STRING::length; + } + const char *ptr() const + { + return LEX_STRING::str; + } + const LEX_STRING& lex_string() const + { + return *this; + } +}; + +template +struct XString : public Storage +{ +public: + XString() {} + XString(char *_str, size_t _len) : + Storage(_str, _len, Compare::charset()) + { + } + XString(LEX_STRING& src) : + Storage(src.str, src.length, Compare::charset()) + { + } + XString(char *_str) : + Storage(_str, strlen(_str), Compare::charset()) + { + } + bool operator== (const XString& b) const + { + return Storage::length() == b.length() && 0 == Compare()(this->lex_string(), b.lex_string()); + } + bool operator!= (const XString& b) const + { + return !(*this == b); + } + operator const char* () const + { + return Storage::ptr(); + } +}; + +typedef XString<> LString; +typedef XString LString_fs; + +typedef XString SString; +typedef XString SString_fs; +typedef XString SString_t; + + +#define XSTRING_WITH_LEN(X) (X).ptr(), (X).length() +#define DB_WITH_LEN(X) (X).db, (X).db_length +#define TABLE_NAME_WITH_LEN(X) (X).table_name, (X).table_name_length + + +class Local_da : public Diagnostics_area +{ + THD *thd; + uint sql_error; + Diagnostics_area *saved_da; + +public: + Local_da(THD *_thd, uint _sql_error= 0) : + Diagnostics_area(_thd->query_id, false, true), + thd(_thd), + sql_error(_sql_error), + saved_da(_thd->get_stmt_da()) + { + thd->set_stmt_da(this); + } + ~Local_da() + { + if (saved_da) + finish(); + } + void finish() + { + DBUG_ASSERT(saved_da && thd); + thd->set_stmt_da(saved_da); + if (is_error()) + my_error(sql_error ? sql_error : sql_errno(), MYF(0), message()); + if (warn_count() > error_count()) + saved_da->copy_non_errors_from_wi(thd, get_warning_info()); + saved_da= NULL; + } +}; + + +#endif // VERS_UTILS_INCLUDED diff --git a/sql/vtmd.cc b/sql/vtmd.cc new file mode 100644 index 00000000000..552a3cd7078 --- /dev/null +++ b/sql/vtmd.cc @@ -0,0 +1,495 @@ +#include "vtmd.h" +#include "sql_base.h" +#include "sql_class.h" +#include "sql_handler.h" // mysql_ha_rm_tables() +#include "sql_table.h" +#include "table_cache.h" // tdc_remove_table() +#include "key.h" + +LString VERS_VTMD_TEMPLATE(C_STRING_WITH_LEN("vtmd_template")); + +bool +VTMD_table::create(THD *thd) +{ + Table_specification_st create_info; + TABLE_LIST src_table, table; + create_info.init(DDL_options_st::OPT_LIKE); + create_info.options|= HA_VTMD; + create_info.alias= vtmd_name; + table.init_one_table( + DB_WITH_LEN(about), + XSTRING_WITH_LEN(vtmd_name), + vtmd_name, + TL_READ); + src_table.init_one_table( + LEX_STRING_WITH_LEN(MYSQL_SCHEMA_NAME), + XSTRING_WITH_LEN(VERS_VTMD_TEMPLATE), + VERS_VTMD_TEMPLATE, + TL_READ); + + Query_tables_backup backup(thd); + thd->lex->sql_command= backup.get().sql_command; + thd->lex->add_to_query_tables(&src_table); + + MDL_auto_lock mdl_lock(thd, table); + if (mdl_lock.acquire_error()) + return true; + + Reprepare_observer *reprepare_observer= thd->m_reprepare_observer; + partition_info *work_part_info= thd->work_part_info; + thd->m_reprepare_observer= NULL; + thd->work_part_info= NULL; + bool rc= mysql_create_like_table(thd, &table, &src_table, &create_info); + thd->m_reprepare_observer= reprepare_observer; + thd->work_part_info= work_part_info; + return rc; +} + +bool +VTMD_table::find_record(ulonglong sys_trx_end, bool &found) +{ + int error; + key_buf_t key; + found= false; + + DBUG_ASSERT(vtmd); + + if (key.allocate(vtmd->s->max_unique_length)) + return true; + + DBUG_ASSERT(sys_trx_end); + vtmd->vers_end_field()->set_notnull(); + vtmd->vers_end_field()->store(sys_trx_end, true); + key_copy(key, vtmd->record[0], vtmd->key_info + IDX_TRX_END, 0); + + error= vtmd->file->ha_index_read_idx_map(vtmd->record[1], IDX_TRX_END, + key, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT); + if (error) + { + if (error == HA_ERR_RECORD_DELETED || error == HA_ERR_KEY_NOT_FOUND) + return false; + vtmd->file->print_error(error, MYF(0)); + return true; + } + + restore_record(vtmd, record[1]); + + found= true; + return false; +} + +bool +VTMD_table::update(THD *thd, const char* archive_name) +{ + TABLE_LIST vtmd_tl; + bool result= true; + bool close_log= false; + bool found= false; + bool created= false; + int error; + size_t an_len= 0; + Open_tables_backup open_tables_backup; + ulonglong save_thd_options; + { + Local_da local_da(thd, ER_VERS_VTMD_ERROR); + + save_thd_options= thd->variables.option_bits; + thd->variables.option_bits&= ~OPTION_BIN_LOG; + + if (about.vers_vtmd_name(vtmd_name)) + goto quit; + + while (true) // max 2 iterations + { + vtmd_tl.init_one_table( + DB_WITH_LEN(about), + XSTRING_WITH_LEN(vtmd_name), + vtmd_name, + TL_WRITE_CONCURRENT_INSERT); + + vtmd= open_log_table(thd, &vtmd_tl, &open_tables_backup); + if (vtmd) + break; + + if (!created && local_da.is_error() && local_da.sql_errno() == ER_NO_SUCH_TABLE) + { + local_da.reset_diagnostics_area(); + if (create(thd)) + goto quit; + created= true; + continue; + } + goto quit; + } + close_log= true; + + if (!vtmd->versioned()) + { + my_message(ER_VERS_VTMD_ERROR, "VTMD is not versioned", MYF(0)); + goto quit; + } + + if (!created && find_record(ULONGLONG_MAX, found)) + goto quit; + + if ((error= vtmd->file->extra(HA_EXTRA_MARK_AS_LOG_TABLE))) + { + vtmd->file->print_error(error, MYF(0)); + goto quit; + } + + /* Honor next number columns if present */ + vtmd->next_number_field= vtmd->found_next_number_field; + + if (vtmd->s->fields != FIELD_COUNT) + { + my_printf_error(ER_VERS_VTMD_ERROR, "`%s.%s` unexpected fields count: %d", MYF(0), + vtmd->s->db.str, vtmd->s->table_name.str, vtmd->s->fields); + goto quit; + } + + if (archive_name) + { + an_len= strlen(archive_name); + vtmd->field[FLD_ARCHIVE_NAME]->store(archive_name, an_len, table_alias_charset); + vtmd->field[FLD_ARCHIVE_NAME]->set_notnull(); + } + else + { + vtmd->field[FLD_ARCHIVE_NAME]->set_null(); + } + vtmd->field[FLD_COL_RENAMES]->set_null(); + + if (found) + { + if (thd->lex->sql_command == SQLCOM_CREATE_TABLE) + { + my_printf_error(ER_VERS_VTMD_ERROR, "`%s.%s` exists and not empty!", MYF(0), + vtmd->s->db.str, vtmd->s->table_name.str); + goto quit; + } + vtmd->mark_columns_needed_for_update(); // not needed? + if (archive_name) + { + vtmd->s->versioned= false; + error= vtmd->file->ha_update_row(vtmd->record[1], vtmd->record[0]); + vtmd->s->versioned= true; + + if (!error) + { + if (thd->lex->sql_command == SQLCOM_DROP_TABLE) + { + error= vtmd->file->ha_delete_row(vtmd->record[0]); + } + else + { + DBUG_ASSERT(thd->lex->sql_command == SQLCOM_ALTER_TABLE); + ulonglong sys_trx_end= (ulonglong) vtmd->vers_start_field()->val_int(); + store_record(vtmd, record[1]); + vtmd->field[FLD_NAME]->store(TABLE_NAME_WITH_LEN(about), system_charset_info); + vtmd->field[FLD_NAME]->set_notnull(); + vtmd->field[FLD_ARCHIVE_NAME]->set_null(); + error= vtmd->file->ha_update_row(vtmd->record[1], vtmd->record[0]); + if (error) + goto err; + + DBUG_ASSERT(an_len); + while (true) + { // fill archive_name of last sequential renames + bool found; + if (find_record(sys_trx_end, found)) + goto quit; + if (!found || !vtmd->field[FLD_ARCHIVE_NAME]->is_null()) + break; + + store_record(vtmd, record[1]); + vtmd->field[FLD_ARCHIVE_NAME]->store(archive_name, an_len, table_alias_charset); + vtmd->field[FLD_ARCHIVE_NAME]->set_notnull(); + vtmd->s->versioned= false; + error= vtmd->file->ha_update_row(vtmd->record[1], vtmd->record[0]); + vtmd->s->versioned= true; + if (error) + goto err; + sys_trx_end= (ulonglong) vtmd->vers_start_field()->val_int(); + } // while (true) + } // else (thd->lex->sql_command != SQLCOM_DROP_TABLE) + } // if (!error) + } // if (archive_name) + else + { + vtmd->field[FLD_NAME]->store(TABLE_NAME_WITH_LEN(about), system_charset_info); + vtmd->field[FLD_NAME]->set_notnull(); + error= vtmd->file->ha_update_row(vtmd->record[1], vtmd->record[0]); + } + } // if (found) + else + { + vtmd->field[FLD_NAME]->store(TABLE_NAME_WITH_LEN(about), system_charset_info); + vtmd->field[FLD_NAME]->set_notnull(); + vtmd->mark_columns_needed_for_insert(); // not needed? + error= vtmd->file->ha_write_row(vtmd->record[0]); + } + + if (error) + { +err: + vtmd->file->print_error(error, MYF(0)); + goto quit; + } + + result= false; + } + +quit: + if (close_log) + close_log_table(thd, &open_tables_backup); + + thd->variables.option_bits= save_thd_options; + return result; +} + + +bool +VTMD_rename::move_archives(THD *thd, LString &new_db) +{ + TABLE_LIST vtmd_tl; + vtmd_tl.init_one_table( + DB_WITH_LEN(about), + XSTRING_WITH_LEN(vtmd_name), + vtmd_name, + TL_READ); + int error; + bool rc= false; + SString_fs archive; + bool end_keyread= false; + bool index_end= false; + Open_tables_backup open_tables_backup; + key_buf_t key; + + vtmd= open_log_table(thd, &vtmd_tl, &open_tables_backup); + if (!vtmd) + return true; + + if (key.allocate(vtmd->key_info[IDX_ARCHIVE_NAME].key_length)) + { + close_log_table(thd, &open_tables_backup); + return true; + } + + if ((error= vtmd->file->ha_start_keyread(IDX_ARCHIVE_NAME))) + goto err; + end_keyread= true; + + if ((error= vtmd->file->ha_index_init(IDX_ARCHIVE_NAME, true))) + goto err; + index_end= true; + + error= vtmd->file->ha_index_first(vtmd->record[0]); + while (!error) + { + if (!vtmd->field[FLD_ARCHIVE_NAME]->is_null()) + { + vtmd->field[FLD_ARCHIVE_NAME]->val_str(&archive); + key_copy(key, + vtmd->record[0], + &vtmd->key_info[IDX_ARCHIVE_NAME], + vtmd->key_info[IDX_ARCHIVE_NAME].key_length, + false); + error= vtmd->file->ha_index_read_map( + vtmd->record[0], + key, + vtmd->key_info[IDX_ARCHIVE_NAME].ext_key_part_map, + HA_READ_PREFIX_LAST); + if (!error) + { + if ((rc= move_table(thd, archive, new_db))) + break; + + error= vtmd->file->ha_index_next(vtmd->record[0]); + } + } + else + { + archive.length(0); + error= vtmd->file->ha_index_next(vtmd->record[0]); + } + } + + if (error && error != HA_ERR_END_OF_FILE) + { +err: + vtmd->file->print_error(error, MYF(0)); + rc= true; + } + + if (index_end) + vtmd->file->ha_index_end(); + if (end_keyread) + vtmd->file->ha_end_keyread(); + + close_log_table(thd, &open_tables_backup); + return rc; +} + +bool +VTMD_rename::move_table(THD *thd, SString_fs &table_name, LString &new_db) +{ + handlerton *table_hton= NULL; + if (!ha_table_exists(thd, about.db, table_name, &table_hton) || !table_hton) + { + push_warning_printf( + thd, Sql_condition::WARN_LEVEL_WARN, + ER_VERS_VTMD_ERROR, + "`%s.%s` archive doesn't exist", + about.db, table_name.ptr()); + return false; + } + + if (ha_table_exists(thd, new_db, table_name)) + { + my_printf_error(ER_VERS_VTMD_ERROR, "`%s.%s` archive already exists!", MYF(0), + new_db.ptr(), table_name.ptr()); + return true; + } + + TABLE_LIST tl; + tl.init_one_table( + DB_WITH_LEN(about), + XSTRING_WITH_LEN(table_name), + table_name, + TL_WRITE_ONLY); + tl.mdl_request.set_type(MDL_EXCLUSIVE); + + mysql_ha_rm_tables(thd, &tl); + if (lock_table_names(thd, &tl, 0, thd->variables.lock_wait_timeout, 0)) + return true; + tdc_remove_table(thd, TDC_RT_REMOVE_ALL, about.db, table_name, false); + + bool rc= mysql_rename_table( + table_hton, + about.db, table_name, + new_db, table_name, + NO_FK_CHECKS); + if (!rc) + query_cache_invalidate3(thd, &tl, 0); + + return rc; +} + +bool +VTMD_rename::try_rename(THD *thd, LString new_db, LString new_alias, const char *archive_name) +{ + Local_da local_da(thd, ER_VERS_VTMD_ERROR); + TABLE_LIST new_table; + + if (check_exists(thd)) + return true; + + new_table.init_one_table( + XSTRING_WITH_LEN(new_db), + XSTRING_WITH_LEN(new_alias), + new_alias, TL_READ); + + if (new_table.vers_vtmd_name(vtmd_new_name)) + return true; + + if (ha_table_exists(thd, new_db, vtmd_new_name)) + { + if (exists) + { + my_printf_error(ER_VERS_VTMD_ERROR, "`%s.%s` table already exists!", MYF(0), + new_db.ptr(), vtmd_new_name.ptr()); + return true; + } + push_warning_printf( + thd, Sql_condition::WARN_LEVEL_WARN, + ER_VERS_VTMD_ERROR, + "`%s.%s` table already exists!", + new_db.ptr(), vtmd_new_name.ptr()); + return false; + } + + if (!exists) + return false; + + bool same_db= true; + if (LString_fs(DB_WITH_LEN(about)) != new_db) + { + // Move archives before VTMD so if the operation is interrupted, it could be continued. + if (move_archives(thd, new_db)) + return true; + same_db= false; + } + + TABLE_LIST vtmd_tl; + vtmd_tl.init_one_table( + DB_WITH_LEN(about), + XSTRING_WITH_LEN(vtmd_name), + vtmd_name, + TL_WRITE_ONLY); + vtmd_tl.mdl_request.set_type(MDL_EXCLUSIVE); + + mysql_ha_rm_tables(thd, &vtmd_tl); + if (lock_table_names(thd, &vtmd_tl, 0, thd->variables.lock_wait_timeout, 0)) + return true; + tdc_remove_table(thd, TDC_RT_REMOVE_ALL, about.db, vtmd_name, false); + bool rc= mysql_rename_table(hton, + about.db, vtmd_name, + new_db, vtmd_new_name, + NO_FK_CHECKS); + if (!rc) + { + query_cache_invalidate3(thd, &vtmd_tl, 0); + if (same_db || archive_name || new_alias != LString(TABLE_NAME_WITH_LEN(about))) + { + local_da.finish(); + VTMD_table new_vtmd(new_table); + rc= new_vtmd.update(thd, archive_name); + } + } + return rc; +} + +bool +VTMD_rename::revert_rename(THD *thd, LString new_db) +{ + DBUG_ASSERT(hton); + Local_da local_da(thd, ER_VERS_VTMD_ERROR); + + TABLE_LIST vtmd_tl; + vtmd_tl.init_one_table( + DB_WITH_LEN(about), + XSTRING_WITH_LEN(vtmd_new_name), + vtmd_new_name, + TL_WRITE_ONLY); + vtmd_tl.mdl_request.set_type(MDL_EXCLUSIVE); + mysql_ha_rm_tables(thd, &vtmd_tl); + if (lock_table_names(thd, &vtmd_tl, 0, thd->variables.lock_wait_timeout, 0)) + return true; + tdc_remove_table(thd, TDC_RT_REMOVE_ALL, new_db, vtmd_new_name, false); + + bool rc= mysql_rename_table( + hton, + new_db, vtmd_new_name, + new_db, vtmd_name, + NO_FK_CHECKS); + + if (!rc) + query_cache_invalidate3(thd, &vtmd_tl, 0); + + return rc; +} + +void VTMD_table::archive_name( + THD* thd, + const char* table_name, + char* new_name, + size_t new_name_size) +{ + const MYSQL_TIME now= thd->query_start_TIME(); + my_snprintf(new_name, new_name_size, "%s_%04d%02d%02d_%02d%02d%02d_%06d", + table_name, now.year, now.month, now.day, now.hour, now.minute, + now.second, now.second_part); +} diff --git a/sql/vtmd.h b/sql/vtmd.h new file mode 100644 index 00000000000..94a1a422313 --- /dev/null +++ b/sql/vtmd.h @@ -0,0 +1,175 @@ +#ifndef VTMD_INCLUDED +#define VTMD_INCLUDED + +#include "table.h" +#include "unireg.h" +#include +#include "my_sys.h" + +#include "vers_utils.h" + +class key_buf_t +{ + uchar* buf; + + key_buf_t(const key_buf_t&); // disabled + key_buf_t& operator= (const key_buf_t&); // disabled + +public: + key_buf_t() : buf(NULL) + {} + + ~key_buf_t() + { + if (buf) + my_free(buf); + } + + bool allocate(size_t alloc_size) + { + DBUG_ASSERT(!buf); + buf= static_cast(my_malloc(alloc_size, MYF(0))); + if (!buf) + { + my_message(ER_VERS_VTMD_ERROR, "failed to allocate key buffer", MYF(0)); + return true; + } + return false; + } + + operator uchar* () + { + DBUG_ASSERT(buf); + return reinterpret_cast(buf); + } +}; + +class THD; + +class VTMD_table +{ +protected: + TABLE *vtmd; + const TABLE_LIST &about; + SString_t vtmd_name; + +private: + VTMD_table(const VTMD_table&); // prohibit copying references + +public: + enum { + FLD_START= 0, + FLD_END, + FLD_NAME, + FLD_ARCHIVE_NAME, + FLD_COL_RENAMES, + FIELD_COUNT + }; + + enum { + IDX_TRX_END= 0, + IDX_ARCHIVE_NAME + }; + + VTMD_table(TABLE_LIST &_about) : + vtmd(NULL), + about(_about) + {} + + bool create(THD *thd); + bool find_record(ulonglong sys_trx_end, bool &found); + bool update(THD *thd, const char* archive_name= NULL); + + static void archive_name(THD *thd, const char *table_name, char *new_name, size_t new_name_size); + void archive_name(THD *thd, char *new_name, size_t new_name_size) + { + archive_name(thd, about.table_name, new_name, new_name_size); + } + +}; + +class VTMD_exists : public VTMD_table +{ +protected: + handlerton *hton; + +public: + bool exists; + +public: + VTMD_exists(TABLE_LIST &_about) : + VTMD_table(_about), + hton(NULL), + exists(false) + {} + + bool check_exists(THD *thd); // returns error status +}; + +class VTMD_rename : public VTMD_exists +{ + SString_t vtmd_new_name; + +public: + VTMD_rename(TABLE_LIST &_about) : + VTMD_exists(_about) + {} + + bool try_rename(THD *thd, LString new_db, LString new_alias, const char* archive_name= NULL); + bool revert_rename(THD *thd, LString new_db); + +private: + bool move_archives(THD *thd, LString &new_db); + bool move_table(THD *thd, SString_fs &table_name, LString &new_db); +}; + +class VTMD_drop : public VTMD_exists +{ + char archive_name_[NAME_CHAR_LEN]; + +public: + VTMD_drop(TABLE_LIST &_about) : + VTMD_exists(_about) + { + *archive_name_= 0; + } + + const char* archive_name(THD *thd) + { + VTMD_table::archive_name(thd, archive_name_, sizeof(archive_name_)); + return archive_name_; + } + + const char* archive_name() const + { + DBUG_ASSERT(*archive_name_); + return archive_name_; + } + + bool update(THD *thd) + { + DBUG_ASSERT(*archive_name_); + return VTMD_exists::update(thd, archive_name_); + } +}; + + +inline +bool +VTMD_exists::check_exists(THD *thd) +{ + if (about.vers_vtmd_name(vtmd_name)) + return true; + + exists= ha_table_exists(thd, about.db, vtmd_name, &hton); + + if (exists && !hton) + { + my_printf_error(ER_VERS_VTMD_ERROR, "`%s.%s` handlerton empty!", MYF(0), + about.db, vtmd_name.ptr()); + return true; + } + return false; +} + +#endif // VTMD_INCLUDED -- cgit v1.2.1 From ac5eb9771e4c07dc0442a44d863da6dafdf127e0 Mon Sep 17 00:00:00 2001 From: Eugene Kosov Date: Sat, 17 Jun 2017 17:59:22 +0300 Subject: SQL: Versioned SHOW CREATE TABLE [closes #125] --- sql/sql_show.cc | 33 ++++++++++++++++++++++-- sql/sql_yacc.yy | 12 +++++++-- sql/table.h | 3 ++- sql/vtmd.cc | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ sql/vtmd.h | 1 + 5 files changed, 123 insertions(+), 5 deletions(-) (limited to 'sql') diff --git a/sql/sql_show.cc b/sql/sql_show.cc index f6e046a8d34..d36d222fb07 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -62,6 +62,8 @@ #ifdef WITH_PARTITION_STORAGE_ENGINE #include "ha_partition.h" #endif +#include "vtmd.h" +#include "transaction.h" enum enum_i_s_events_fields { @@ -1267,6 +1269,24 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) */ MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint(); + TABLE_LIST tl; + bool versioned_query= + table_list->vers_conditions.type != FOR_SYSTEM_TIME_UNSPECIFIED; + String archive_name; + if (versioned_query) + { + DBUG_ASSERT(table_list->vers_conditions.type == FOR_SYSTEM_TIME_AS_OF); + VTMD_table vtmd(*table_list); + if (vtmd.find_archive_name(thd, archive_name)) + goto exit; + + tl.init_one_table(table_list->db, table_list->db_length, archive_name.ptr(), + archive_name.length(), archive_name.ptr(), TL_READ); + + tl.alias= table_list->table_name; + tl.vers_force_alias= true; + table_list= &tl; + } if (mysqld_show_create_get_fields(thd, table_list, &field_list, &buffer)) goto exit; @@ -1281,7 +1301,9 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) protocol->store(table_list->view_name.str, system_charset_info); else { - if (table_list->schema_table) + if (versioned_query) + protocol->store(tl.alias, system_charset_info); + else if (table_list->schema_table) protocol->store(table_list->schema_table->table_name, system_charset_info); else @@ -1309,6 +1331,13 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) my_eof(thd); exit: + if (versioned_query) + { + /* If commit fails, we should be able to reset the OK status. */ + thd->get_stmt_da()->set_overwrite_status(true); + trans_commit_stmt(thd); + thd->get_stmt_da()->set_overwrite_status(false); + } close_thread_tables(thd); /* Release any metadata locks taken during SHOW CREATE. */ thd->mdl_context.rollback_to_savepoint(mdl_savepoint); @@ -2026,7 +2055,7 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, alias= table_list->schema_table->table_name; else { - if (lower_case_table_names == 2) + if (lower_case_table_names == 2 || table_list->vers_force_alias) alias= table->alias.c_ptr(); else { diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 8f02a033210..ec09803a46e 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1905,7 +1905,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); parse_vcol_expr vcol_opt_specifier vcol_opt_attribute vcol_opt_attribute_list vcol_attribute opt_serial_attribute opt_serial_attribute_list serial_attribute - explainable_command opt_lock_wait_timeout + explainable_command opt_lock_wait_timeout asrow_attribute END_OF_INPUT %type call sp_proc_stmts sp_proc_stmts1 sp_proc_stmt @@ -13330,13 +13330,21 @@ show_param: Lex->set_command(SQLCOM_SHOW_CREATE_DB, $3); Lex->name= $4; } - | CREATE TABLE_SYM table_ident + | CREATE TABLE_SYM table_ident opt_for_system_time_clause { LEX *lex= Lex; lex->sql_command = SQLCOM_SHOW_CREATE; if (!lex->select_lex.add_table_to_list(thd, $3, NULL,0)) MYSQL_YYABORT; lex->create_info.storage_media= HA_SM_DEFAULT; + + if (lex->vers_conditions.type != FOR_SYSTEM_TIME_UNSPECIFIED && + lex->vers_conditions.type != FOR_SYSTEM_TIME_AS_OF) { + my_yyabort_error((ER_VERS_WRONG_PARAMS, MYF(0), "FOR SYSTEM_TIME", + "only AS OF allowed here")); + } + if ($4) + Lex->last_table()->vers_conditions= Lex->vers_conditions; } | CREATE VIEW_SYM table_ident { diff --git a/sql/table.h b/sql/table.h index ae04c58c407..8bcdb001d59 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1890,7 +1890,7 @@ struct vers_select_conds_t { return type != b; } - operator bool() + operator bool() const { return type != FOR_SYSTEM_TIME_UNSPECIFIED; } @@ -2356,6 +2356,7 @@ struct TABLE_LIST /* System Versioning */ vers_select_conds_t vers_conditions; bool vers_vtmd_name(String &out) const; + bool vers_force_alias; /** @brief diff --git a/sql/vtmd.cc b/sql/vtmd.cc index 552a3cd7078..65620bd2cb0 100644 --- a/sql/vtmd.cc +++ b/sql/vtmd.cc @@ -3,6 +3,7 @@ #include "sql_class.h" #include "sql_handler.h" // mysql_ha_rm_tables() #include "sql_table.h" +#include "sql_select.h" #include "table_cache.h" // tdc_remove_table() #include "key.h" @@ -493,3 +494,81 @@ void VTMD_table::archive_name( table_name, now.year, now.month, now.day, now.hour, now.minute, now.second, now.second_part); } + +bool VTMD_table::find_archive_name(THD *thd, String &out) +{ + String vtmd_name; + if (about.vers_vtmd_name(vtmd_name)) + return true; + + READ_RECORD info; + int error= 0; + SQL_SELECT *select= NULL; + COND *conds= NULL; + List dummy; + SELECT_LEX &select_lex= thd->lex->select_lex; + + TABLE_LIST tl; + tl.init_one_table(about.db, about.db_length, vtmd_name.ptr(), + vtmd_name.length(), vtmd_name.ptr(), TL_READ); + + Open_tables_backup open_tables_backup; + if (!(vtmd= open_log_table(thd, &tl, &open_tables_backup))) + { + my_error(ER_VERS_VTMD_ERROR, MYF(0), "failed to open VTMD table"); + return true; + } + + Name_resolution_context &ctx= thd->lex->select_lex.context; + TABLE_LIST *table_list= ctx.table_list; + TABLE_LIST *first_name_resolution_table= ctx.first_name_resolution_table; + table_map map = tl.table->map; + ctx.table_list= &tl; + ctx.first_name_resolution_table= &tl; + tl.table->map= 1; + + tl.vers_conditions= about.vers_conditions; + if ((error= vers_setup_select(thd, &tl, &conds, &select_lex)) || + (error= setup_conds(thd, &tl, dummy, &conds))) + { + my_error(ER_VERS_VTMD_ERROR, MYF(0), + "failed to setup conditions for querying VTMD table"); + goto err; + } + + select= make_select(tl.table, 0, 0, conds, NULL, 0, &error); + if (error) + goto loc_err; + if ((error= + init_read_record(&info, thd, tl.table, select, NULL, 1, 1, false))) + goto loc_err; + + while (!(error= info.read_record(&info)) && !thd->killed && !thd->is_error()) + { + if (select->skip_record(thd) > 0) + { + tl.table->field[FLD_ARCHIVE_NAME]->val_str(&out); + + if (out.length() == 0) + { + // Handle AS OF NOW or just RENAMEd case + out.set(about.table_name, about.table_name_length, + system_charset_info); + } + break; + } + } + +loc_err: + if (error) + my_error(ER_VERS_VTMD_ERROR, MYF(0), "failed to query VTMD table"); + + end_read_record(&info); +err: + delete select; + ctx.table_list= table_list; + ctx.first_name_resolution_table= first_name_resolution_table; + tl.table->map= map; + close_log_table(thd, &open_tables_backup); + return error ? true : false; +} diff --git a/sql/vtmd.h b/sql/vtmd.h index 94a1a422313..8588462f83f 100644 --- a/sql/vtmd.h +++ b/sql/vtmd.h @@ -86,6 +86,7 @@ public: archive_name(thd, about.table_name, new_name, new_name_size); } + bool find_archive_name(THD *thd, String &out); }; class VTMD_exists : public VTMD_table -- cgit v1.2.1 From 670b7f5fd4a4cdf82493fec56ddc6fa5de80ba90 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Thu, 22 Jun 2017 20:15:03 +0300 Subject: Style: API renames part_recs_slow() -> part_records(); HTON_SUPPORTS_SYS_VERSIONING -> HTON_NATIVE_SYS_VERSIONING. --- sql/ha_partition.h | 2 +- sql/handler.cc | 4 ++-- sql/handler.h | 6 +++--- sql/item_vers.cc | 2 +- sql/partition_info.h | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) (limited to 'sql') diff --git a/sql/ha_partition.h b/sql/ha_partition.h index 861ba47b94e..98e0c1baafe 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -1360,7 +1360,7 @@ public: return m_innodb; } - virtual ha_rows part_recs_slow(void *_part_elem) + virtual ha_rows part_records(void *_part_elem) { partition_element *part_elem= reinterpret_cast(_part_elem); DBUG_ASSERT(m_part_info); diff --git a/sql/handler.cc b/sql/handler.cc index f2e74d936fd..8ec9feab7c5 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -6674,7 +6674,7 @@ bool Vers_parse_info::check_and_fix_implicit( const char* table_name) { bool integer_fields= - create_info->db_type->flags & HTON_SUPPORTS_SYS_VERSIONING; + create_info->db_type->flags & HTON_NATIVE_SYS_VERSIONING; SELECT_LEX &slex= thd->lex->select_lex; int vers_tables= 0; @@ -6826,7 +6826,7 @@ bool Vers_parse_info::check_and_fix_alter(THD *thd, Alter_info *alter_info, TABLE_SHARE *share) { bool integer_fields= - create_info->db_type->flags & HTON_SUPPORTS_SYS_VERSIONING; + create_info->db_type->flags & HTON_NATIVE_SYS_VERSIONING; const char *table_name= share->table_name.str; if (!need_check() && !share->versioned) diff --git a/sql/handler.h b/sql/handler.h index 807ae234fef..6f65853256c 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -1442,7 +1442,7 @@ handlerton *ha_default_tmp_handlerton(THD *thd); */ #define HTON_NO_BINLOG_ROW_OPT (1 << 9) #define HTON_SUPPORTS_EXTENDED_KEYS (1 <<10) //supports extended keys -#define HTON_SUPPORTS_SYS_VERSIONING (1 << 11) //Engine supports System Versioning +#define HTON_NATIVE_SYS_VERSIONING (1 << 11) //Engine supports System Versioning // MySQL compatibility. Unused. #define HTON_SUPPORTS_FOREIGN_KEYS (1 << 0) //Foreign key constraint supported. @@ -4368,8 +4368,8 @@ public: { return -1; /*unsupported */} virtual bool versioned() const - { DBUG_ASSERT(ht); return partition_ht()->flags & HTON_SUPPORTS_SYS_VERSIONING; } - virtual ha_rows part_recs_slow(void *_part_elem) + { DBUG_ASSERT(ht); return partition_ht()->flags & HTON_NATIVE_SYS_VERSIONING; } + virtual ha_rows part_records(void *_part_elem) { DBUG_ASSERT(0); return false; } virtual handler* part_handler(uint32 part_id) { DBUG_ASSERT(0); return NULL; } diff --git a/sql/item_vers.cc b/sql/item_vers.cc index 81cc3808651..eff4c15db6b 100644 --- a/sql/item_vers.cc +++ b/sql/item_vers.cc @@ -71,7 +71,7 @@ VTQ_common::init_hton() hton= plugin_hton(plugin_int_to_ref(innodb_plugin)); DBUG_ASSERT(hton); } - if (hton && !(hton->flags & HTON_SUPPORTS_SYS_VERSIONING)) + if (hton && !(hton->flags & HTON_NATIVE_SYS_VERSIONING)) { my_error(ER_VERS_ENGINE_UNSUPPORTED, MYF(0), Item::name ? Item::name : this->func_name()); hton= NULL; diff --git a/sql/partition_info.h b/sql/partition_info.h index ef20564837c..9dff7a41a4a 100644 --- a/sql/partition_info.h +++ b/sql/partition_info.h @@ -476,7 +476,7 @@ public: part= vers_hist_part(); } // TODO: cache thread-shared part_recs and increment on INSERT - return table->file->part_recs_slow(part) >= vers_info->limit; + return table->file->part_records(part) >= vers_info->limit; } Vers_field_stats& vers_stat_trx(stat_trx_field fld, uint32 part_element_id) { -- cgit v1.2.1 From 07ff0e1202fbc521c476080eb2cd9fed544dc959 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Thu, 22 Jun 2017 23:09:34 +0300 Subject: SQL: start_end_t members as LEX_CSTRING --- sql/handler.cc | 59 ++++++++---------------- sql/handler.h | 23 +++++----- sql/sql_table.cc | 8 ++-- sql/sql_yacc.yy | 18 +++----- sql/unireg.cc | 4 +- sql/vers_string.h | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ sql/vers_utils.h | 99 +---------------------------------------- sql/vtmd.cc | 2 +- 8 files changed, 176 insertions(+), 168 deletions(-) create mode 100644 sql/vers_string.h (limited to 'sql') diff --git a/sql/handler.cc b/sql/handler.cc index 8ec9feab7c5..32d5c5fd2db 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -6584,13 +6584,12 @@ int del_global_index_stat(THD *thd, TABLE* table, KEY* key_info) bool Vers_parse_info::is_trx_start(const char *name) const { DBUG_ASSERT(name); - return generated_as_row.start && - !strcmp(generated_as_row.start->c_ptr(), name); + return generated_as_row.start && generated_as_row.start == LString_i(name); } bool Vers_parse_info::is_trx_end(const char *name) const { DBUG_ASSERT(name); - return generated_as_row.end && !strcmp(generated_as_row.end->c_ptr(), name); + return generated_as_row.end && generated_as_row.end == LString_i(name); } bool Vers_parse_info::is_trx_start(const Create_field &f) const { @@ -6601,14 +6600,9 @@ bool Vers_parse_info::is_trx_end(const Create_field &f) const return f.flags & VERS_SYS_END_FLAG; } -static bool create_string(MEM_ROOT *mem_root, String **s, const char *value) -{ - *s= new (mem_root) String(value, system_charset_info); - return *s == NULL; -} static bool vers_create_sys_field(THD *thd, const char *field_name, - Alter_info *alter_info, String **s, + Alter_info *alter_info, int flags, bool integer_fields) { @@ -6635,9 +6629,6 @@ static bool vers_create_sys_field(THD *thd, const char *field_name, if (f->check(thd)) return true; - if (create_string(thd->mem_root, s, field_name)) - return true; - alter_info->create_list.push_back(f); return false; } @@ -6652,19 +6643,18 @@ bool Vers_parse_info::fix_implicit(THD *thd, Alter_info *alter_info, alter_info->flags|= Alter_info::ALTER_ADD_COLUMN; - static const char * sys_trx_start= "sys_trx_start"; - static const char * sys_trx_end= "sys_trx_end"; + static const LString sys_trx_start= "sys_trx_start"; + static const LString sys_trx_end= "sys_trx_end"; + + period_for_system_time= start_end_t(sys_trx_start, sys_trx_end); + generated_as_row= period_for_system_time; return vers_create_sys_field(thd, sys_trx_start, alter_info, - &generated_as_row.start, VERS_SYS_START_FLAG, + VERS_SYS_START_FLAG, integer_fields) || vers_create_sys_field(thd, sys_trx_end, alter_info, - &generated_as_row.end, VERS_SYS_END_FLAG, - integer_fields) || - create_string(thd->mem_root, &period_for_system_time.start, - sys_trx_start) || - create_string(thd->mem_root, &period_for_system_time.end, - sys_trx_end); + VERS_SYS_END_FLAG, + integer_fields); } bool Vers_parse_info::check_and_fix_implicit( @@ -6731,7 +6721,7 @@ bool Vers_parse_info::check_and_fix_implicit( return true; } orig_table= f->field->orig_table; - generated_as_row.start= new (thd->mem_root) String(f->field_name, system_charset_info); + generated_as_row.start= f->field_name; period_for_system_time.start= generated_as_row.start; } continue; @@ -6746,7 +6736,7 @@ bool Vers_parse_info::check_and_fix_implicit( goto err_different_tables; } orig_table= f->field->orig_table; - generated_as_row.end= new (thd->mem_root) String(f->field_name, system_charset_info); + generated_as_row.end= f->field_name; period_for_system_time.end= generated_as_row.end; } continue; @@ -6866,11 +6856,8 @@ bool Vers_parse_info::check_and_fix_alter(THD *thd, Alter_info *alter_info, const char *end= share->vers_end_field()->field_name; DBUG_ASSERT(start && end); - if (create_string(thd->mem_root, &generated_as_row.start, start) || - create_string(thd->mem_root, &generated_as_row.end, end) || - create_string(thd->mem_root, &period_for_system_time.start, start) || - create_string(thd->mem_root, &period_for_system_time.end, end)) - return true; + generated_as_row= start_end_t(start, end); + period_for_system_time= generated_as_row; if (alter_info->create_list.elements) { @@ -6935,14 +6922,8 @@ bool Vers_parse_info::fix_create_like(THD *thd, Alter_info *alter_info, return true; } - if (create_string(thd->mem_root, &generated_as_row.start, f_start->field_name) || - create_string(thd->mem_root, &period_for_system_time.start, f_start->field_name) || - create_string(thd->mem_root, &generated_as_row.end, f_end->field_name) || - create_string(thd->mem_root, &period_for_system_time.end, f_end->field_name)) - { - sql_print_error("Failed to allocate memory for Vers_parse_info::fix_create_like()"); - return true; - } + generated_as_row= start_end_t(f_start->field_name, f_end->field_name); + period_for_system_time= generated_as_row; create_info->options|= HA_VERSIONED_TABLE; return false; @@ -6972,16 +6953,14 @@ bool Vers_parse_info::check_with_conditions(const char *table_name) const return true; } - if (my_strcasecmp(system_charset_info, generated_as_row.start->c_ptr(), - period_for_system_time.start->c_ptr())) + if (generated_as_row.start != period_for_system_time.start) { my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, "'PERIOD FOR SYSTEM_TIME' and 'GENERATED AS ROW START' mismatch"); return true; } - if (my_strcasecmp(system_charset_info, generated_as_row.end->c_ptr(), - period_for_system_time.end->c_ptr())) + if (generated_as_row.end != period_for_system_time.end) { my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, "'PERIOD FOR SYSTEM_TIME' and 'GENERATED AS ROW END' mismatch"); diff --git a/sql/handler.h b/sql/handler.h index 6f65853256c..9c2b1bcf209 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -36,6 +36,7 @@ #include "sql_array.h" /* Dynamic_array<> */ #include "mdl.h" #include "vtq.h" +#include "vers_string.h" #include "sql_analyze_stmt.h" // for Exec_time_tracker @@ -1684,17 +1685,19 @@ struct Vers_parse_info struct start_end_t { - start_end_t() : - start(NULL), - end(NULL) {} - String *start; - String *end; + start_end_t() + {} + start_end_t(const char* _start, const char* _end) : + start(_start), + end(_end) {} + LString_i start; + LString_i end; }; start_end_t period_for_system_time; start_end_t generated_as_row; - void set_period_for_system_time(String *start, String *end) + void set_period_for_system_time(LString start, LString end) { period_for_system_time.start = start; period_for_system_time.end = end; @@ -1713,10 +1716,10 @@ private: has_unversioned_fields || declared_with_system_versioning || declared_without_system_versioning || - period_for_system_time.start || - period_for_system_time.end || - generated_as_row.start || - generated_as_row.end; + period_for_system_time.start.str || + period_for_system_time.end.str || + generated_as_row.start.str || + generated_as_row.end.str; } bool check_with_conditions(const char *table_name) const; bool check_generated_type(const char *table_name, Alter_info *alter_info, diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 7aeb1854e24..4c1ab495651 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -4326,9 +4326,9 @@ vers_prepare_keys(THD *thd, { DBUG_ASSERT(create_info->versioned()); - const char *row_start_field= create_info->vers_info.generated_as_row.start->c_ptr(); + const char *row_start_field= create_info->vers_info.generated_as_row.start; DBUG_ASSERT(row_start_field); - const char *row_end_field= create_info->vers_info.generated_as_row.end->c_ptr(); + const char *row_end_field= create_info->vers_info.generated_as_row.end; DBUG_ASSERT(row_end_field); List_iterator key_it(alter_info->key_list); @@ -4354,10 +4354,8 @@ vers_prepare_keys(THD *thd, if (key_part) continue; // Key already contains Sys_start or Sys_end - const LEX_STRING &lex_sys_end= - create_info->vers_info.generated_as_row.end->lex_string(); Key_part_spec *key_part_sys_end_col= - new(thd->mem_root) Key_part_spec(lex_sys_end, 0); + new(thd->mem_root) Key_part_spec(create_info->vers_info.generated_as_row.end, 0); key->columns.push_back(key_part_sys_end_col); } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index ec09803a46e..73860aadca5 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1622,6 +1622,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); opt_constraint constraint opt_ident sp_decl_ident sp_block_label + period_for_system_time_column_id %type TEXT_STRING @@ -1651,7 +1652,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type text_string hex_or_bin_String opt_gconcat_separator - period_for_system_time_column_id %type int_type real_type @@ -6167,7 +6167,7 @@ period_for_system_time: PERIOD_SYM FOR_SYSTEM_TIME_SYM '(' period_for_system_time_column_id ',' period_for_system_time_column_id ')' { Vers_parse_info &info= Lex->vers_get_info(); - if (!my_strcasecmp(system_charset_info, $4->c_ptr(), $6->c_ptr())) + if (!my_strcasecmp(system_charset_info, $4.str, $6.str)) { my_yyabort_error((ER_VERS_WRONG_PARAMS, MYF(0), Lex->create_last_non_select_table->table_name, @@ -6284,14 +6284,10 @@ field_def: { LEX *lex= Lex; Vers_parse_info &info= lex->vers_get_info(); - String *field_name= new (thd->mem_root) - String((const char*)lex->last_field->field_name, system_charset_info); - if (!field_name) - MYSQL_YYABORT; - + const char *field_name= lex->last_field->field_name; const char *table_name= lex->create_last_non_select_table->table_name; - String **p= NULL; + LString_i *p; const char* err; switch ($4) { @@ -6310,6 +6306,7 @@ field_def: MYSQL_YYABORT; break; } + DBUG_ASSERT(p); if (*p) { my_yyabort_error((ER_VERS_WRONG_PARAMS, MYF(0), table_name, err)); @@ -16237,10 +16234,7 @@ column_list: period_for_system_time_column_id: ident { - String *new_str= new (thd->mem_root) String((const char*) $1.str,$1.length,system_charset_info); - if (new_str == NULL) - MYSQL_YYABORT; - $$= new_str; + $$= $1; } ; diff --git a/sql/unireg.cc b/sql/unireg.cc index cffc98d2e35..7a5aadb1c22 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -101,8 +101,8 @@ vers_get_field(HA_CREATE_INFO *create_info, List &create_fields, b const char *row_field = row_start ? - create_info->vers_info.generated_as_row.start->c_ptr() : - create_info->vers_info.generated_as_row.end->c_ptr(); + create_info->vers_info.generated_as_row.start : + create_info->vers_info.generated_as_row.end; DBUG_ASSERT(row_field); for (unsigned field_no = 0; (sql_field = it++); ++field_no) diff --git a/sql/vers_string.h b/sql/vers_string.h new file mode 100644 index 00000000000..ddd691276db --- /dev/null +++ b/sql/vers_string.h @@ -0,0 +1,131 @@ +#ifndef VERS_STRING_INCLUDED +#define VERS_STRING_INCLUDED + +struct Compare_strncmp +{ + int operator()(const LEX_CSTRING& a, const LEX_CSTRING& b) const + { + return strncmp(a.str, b.str, a.length); + } + static CHARSET_INFO* charset() + { + return system_charset_info; + } +}; + +template +struct Compare_my_strcasecmp +{ + int operator()(const LEX_CSTRING& a, const LEX_CSTRING& b) const + { + DBUG_ASSERT(a.str[a.length] == 0 && b.str[b.length] == 0); + return my_strcasecmp(CS, a.str, b.str); + } + static CHARSET_INFO* charset() + { + return CS; + } +}; + +typedef Compare_my_strcasecmp Compare_fs; +typedef Compare_my_strcasecmp Compare_t; + +template +struct LEX_STRING_u : public Storage +{ + LEX_STRING_u() + { + Storage::str= NULL; + Storage::length= 0; + } + LEX_STRING_u(const char *_str, uint32 _len, CHARSET_INFO *) + { + Storage::str= _str; + Storage::length= _len; + } + uint32 length() const + { + return Storage::length; + } + const char *ptr() const + { + return Storage::str; + } + void set(const char *_str, uint32 _len, CHARSET_INFO *) + { + Storage::str= _str; + Storage::length= _len; + } + const LEX_CSTRING& lex_cstring() const + { + return *this; + } +}; + +template > +struct XString : public Storage +{ +public: + XString() {} + XString(const char *_str, size_t _len) : + Storage(_str, _len, Compare::charset()) + { + } + XString(LEX_STRING& src) : + Storage(src.str, src.length, Compare::charset()) + { + } + XString(const char *_str) : + Storage(_str, strlen(_str), Compare::charset()) + { + } + XString& operator= (const char *_str) + { + DBUG_ASSERT(_str); + Storage::set(_str, strlen(_str), Compare::charset()); + return *this; + } + bool operator== (const XString& b) const + { + return Storage::length() == b.length() && 0 == Compare()(this->lex_cstring(), b.lex_cstring()); + } + bool operator!= (const XString& b) const + { + return !(*this == b); + } + operator const char* () const + { + return Storage::ptr(); + } + operator LEX_CSTRING& () const + { + return this->lex_cstring(); + } + operator LEX_STRING () const + { + LEX_STRING res; + res.str= const_cast(this->ptr()); + res.length= this->length(); + return res; + } + operator bool () const + { + return Storage::ptr() != NULL; + } +}; + +typedef XString<> LString; +typedef XString LString_fs; +typedef XString > LString_i; + +typedef XString SString; +typedef XString SString_fs; +typedef XString SString_t; + + +#define XSTRING_WITH_LEN(X) (X).ptr(), (X).length() +#define DB_WITH_LEN(X) (X).db, (X).db_length +#define TABLE_NAME_WITH_LEN(X) (X).table_name, (X).table_name_length + + +#endif // VERS_STRING_INCLUDED diff --git a/sql/vers_utils.h b/sql/vers_utils.h index ee08fcbb2bc..948139bfa9b 100644 --- a/sql/vers_utils.h +++ b/sql/vers_utils.h @@ -3,6 +3,7 @@ #include "table.h" #include "sql_class.h" +#include "vers_string.h" class MDL_auto_lock { @@ -30,104 +31,6 @@ public: bool acquire_error() const { return error; } }; -struct Compare_strncmp -{ - int operator()(const LEX_STRING& a, const LEX_STRING& b) const - { - return strncmp(a.str, b.str, a.length); - } - static CHARSET_INFO* charset() - { - return system_charset_info; - } -}; - -template -struct Compare_my_strcasecmp -{ - int operator()(const LEX_STRING& a, const LEX_STRING& b) const - { - DBUG_ASSERT(a.str[a.length] == 0 && b.str[b.length] == 0); - return my_strcasecmp(CS, a.str, b.str); - } - static CHARSET_INFO* charset() - { - return CS; - } -}; - -typedef Compare_my_strcasecmp Compare_fs; -typedef Compare_my_strcasecmp Compare_t; - -struct LEX_STRING_u : public LEX_STRING -{ - LEX_STRING_u() - { - str= NULL; - LEX_STRING::length= 0; - } - LEX_STRING_u(const char *_str, uint32 _len, CHARSET_INFO *) - { - str= const_cast(_str); - LEX_STRING::length= _len; - } - uint32 length() const - { - return LEX_STRING::length; - } - const char *ptr() const - { - return LEX_STRING::str; - } - const LEX_STRING& lex_string() const - { - return *this; - } -}; - -template -struct XString : public Storage -{ -public: - XString() {} - XString(char *_str, size_t _len) : - Storage(_str, _len, Compare::charset()) - { - } - XString(LEX_STRING& src) : - Storage(src.str, src.length, Compare::charset()) - { - } - XString(char *_str) : - Storage(_str, strlen(_str), Compare::charset()) - { - } - bool operator== (const XString& b) const - { - return Storage::length() == b.length() && 0 == Compare()(this->lex_string(), b.lex_string()); - } - bool operator!= (const XString& b) const - { - return !(*this == b); - } - operator const char* () const - { - return Storage::ptr(); - } -}; - -typedef XString<> LString; -typedef XString LString_fs; - -typedef XString SString; -typedef XString SString_fs; -typedef XString SString_t; - - -#define XSTRING_WITH_LEN(X) (X).ptr(), (X).length() -#define DB_WITH_LEN(X) (X).db, (X).db_length -#define TABLE_NAME_WITH_LEN(X) (X).table_name, (X).table_name_length - class Local_da : public Diagnostics_area { diff --git a/sql/vtmd.cc b/sql/vtmd.cc index 65620bd2cb0..83a6fb512ef 100644 --- a/sql/vtmd.cc +++ b/sql/vtmd.cc @@ -416,7 +416,7 @@ VTMD_rename::try_rename(THD *thd, LString new_db, LString new_alias, const char return false; bool same_db= true; - if (LString_fs(DB_WITH_LEN(about)) != new_db) + if (LString_fs(DB_WITH_LEN(about)) != LString_fs(new_db)) { // Move archives before VTMD so if the operation is interrupted, it could be continued. if (move_archives(thd, new_db)) -- cgit v1.2.1 From b44425c6be217d34a6de2d6f9de85d256d68dd48 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Thu, 22 Jun 2017 23:36:50 +0300 Subject: Misc, SQL: checks cleanup in Item_field::set_field() --- sql/item.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'sql') diff --git a/sql/item.cc b/sql/item.cc index 80a38e4d1d2..d27ab95aaa4 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -2763,9 +2763,8 @@ void Item_field::set_field(Field *field_par) field->force_null= false; if (field->flags & VERS_OPTIMIZED_UPDATE_FLAG && context && ((field->table->pos_in_table_list && - field->table->pos_in_table_list->vers_conditions.type != FOR_SYSTEM_TIME_UNSPECIFIED) || - (context->select_lex && - context->select_lex->vers_conditions.type != FOR_SYSTEM_TIME_UNSPECIFIED))) + field->table->pos_in_table_list->vers_conditions) || + (context->select_lex && context->select_lex->vers_conditions))) { field->force_null= true; push_warning_printf( -- cgit v1.2.1 From faab918ecd1ddb8640dda247ebec6010efc9ec69 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Fri, 23 Jun 2017 09:33:25 +0300 Subject: Style: comment about VTQ --- sql/vtq.h | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'sql') diff --git a/sql/vtq.h b/sql/vtq.h index 68c01990b04..dcea8734b57 100755 --- a/sql/vtq.h +++ b/sql/vtq.h @@ -1,5 +1,6 @@ #ifndef VTQ_INCLUDED #define VTQ_INCLUDED + /* Copyright (c) 2016, MariaDB Corporation. This program is free software; you can redistribute it and/or modify @@ -15,6 +16,15 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ + +/** + VTQ stands for 'versioning transaction query': InnoDB system table that holds + transaction IDs, their corresponding times and other transaction-related + data which is used for transaction order resolution. When versioned table + marks its records lifetime with transaction IDs, VTQ is used to get their + actual timestamps. */ + + enum vtq_field_t { VTQ_ALL = 0, -- cgit v1.2.1 From 46d572dde4f43a206f49574d5eca1d99b58b165a Mon Sep 17 00:00:00 2001 From: Eugene Kosov Date: Tue, 27 Jun 2017 12:22:52 +0300 Subject: SQL: default engine fix in create from versioned [fixes #206] --- sql/handler.cc | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'sql') diff --git a/sql/handler.cc b/sql/handler.cc index 32d5c5fd2db..4f31c20c8e7 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -6663,9 +6663,6 @@ bool Vers_parse_info::check_and_fix_implicit( HA_CREATE_INFO *create_info, const char* table_name) { - bool integer_fields= - create_info->db_type->flags & HTON_NATIVE_SYS_VERSIONING; - SELECT_LEX &slex= thd->lex->select_lex; int vers_tables= 0; bool from_select= slex.item_list.elements ? true : false; @@ -6677,6 +6674,21 @@ bool Vers_parse_info::check_and_fix_implicit( if (table->table && table->table->versioned()) vers_tables++; } + + // Possibly override default storage engine to match + // one used in source table. + if (!(create_info->used_fields & HA_CREATE_USED_ENGINE)) + { + List_iterator_fast it(alter_info->create_list); + while (Create_field *f= it++) + { + if (is_trx_start(*f) || is_trx_end(*f)) + { + create_info->db_type= f->field->orig_table->file->ht; + break; + } + } + } } // CREATE ... SELECT: if at least one table in SELECT is versioned, @@ -6750,6 +6762,8 @@ bool Vers_parse_info::check_and_fix_implicit( } } + bool integer_fields= create_info->db_type->flags & HTON_NATIVE_SYS_VERSIONING; + if (vers_tables > 0) { if (!generated_as_row.start && !generated_as_row.end) -- cgit v1.2.1 From 229c528110ecb38f379a6328b0eb62f6bbc74fad Mon Sep 17 00:00:00 2001 From: Eugene Kosov Date: Tue, 27 Jun 2017 20:59:27 +0300 Subject: SQL: hide system fields instead of drop [closes #210] --- sql/handler.cc | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) (limited to 'sql') diff --git a/sql/handler.cc b/sql/handler.cc index 4f31c20c8e7..9a4485412c2 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -6630,6 +6630,7 @@ static bool vers_create_sys_field(THD *thd, const char *field_name, return true; alter_info->create_list.push_back(f); + alter_info->flags|= Alter_info::ALTER_ADD_COLUMN; return false; } @@ -6885,15 +6886,41 @@ bool Vers_parse_info::check_and_fix_alter(THD *thd, Alter_info *alter_info, if (alter_info->drop_list.elements) { + bool done_start= false; + bool done_end= false; List_iterator it(alter_info->drop_list); - while (Alter_drop* d= it++) + while (Alter_drop *d= it++) { - if (is_trx_start(d->name) || is_trx_end(d->name)) + const char *name= d->name; + Field *f= NULL; + if (!done_start && is_trx_start(name)) { - my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, - "Can not drop system versioning field"); + f= share->vers_start_field(); + done_start= true; + } + else if (!done_end && is_trx_end(name)) + { + f= share->vers_end_field(); + done_end= true; + } + else + continue; + if (f->flags & HIDDEN_FLAG) + { + my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0), d->type_name(), name); return true; } + + if (vers_create_sys_field(thd, name, alter_info, + f->flags & + (VERS_SYS_START_FLAG | VERS_SYS_END_FLAG), + integer_fields)) + { + return true; + } + + if (done_start && done_end) + break; } } -- cgit v1.2.1 From 4b0f1284eeb633f091f2e93220661fc6888b1abc Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Thu, 29 Jun 2017 19:35:08 +0300 Subject: SQL: revisit error messages [closes #217] --- sql/handler.cc | 70 +++++++++++-------------- sql/handler.h | 24 ++++----- sql/partition_info.cc | 30 +++-------- sql/share/errmsg-utf8.txt | 51 +++++++++++++++--- sql/sql_partition.cc | 6 ++- sql/sql_select.cc | 4 +- sql/sql_yacc.yy | 128 +++++++++++++++++++++++++--------------------- 7 files changed, 171 insertions(+), 142 deletions(-) (limited to 'sql') diff --git a/sql/handler.cc b/sql/handler.cc index 9a4485412c2..3589c3aee19 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -6696,24 +6696,24 @@ bool Vers_parse_info::check_and_fix_implicit( // then created table will be versioned. if (thd->variables.vers_force || vers_tables > 0) { - declared_with_system_versioning= true; + with_system_versioning= true; create_info->options|= HA_VERSIONED_TABLE; } if (!need_check()) return false; - if (declared_without_system_versioning) + if (without_system_versioning) { - my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, - "'WITHOUT SYSTEM VERSIONING' is not allowed"); + my_error_as(ER_VERS_WRONG_PARAMS, ER_NOT_ALLOWED, MYF(0), table_name, + "WITHOUT SYSTEM VERSIONING"); return true; } - if (!declared_with_system_versioning && !has_versioned_fields) + if (!with_system_versioning && !versioned_fields) { - my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, - "'WITH SYSTEM VERSIONING' missing"); + my_error_as(ER_VERS_WRONG_PARAMS, ER_MISSING, MYF(0), table_name, + "WITH SYSTEM VERSIONING"); return true; } @@ -6729,8 +6729,7 @@ bool Vers_parse_info::check_and_fix_implicit( if (orig_table && orig_table != f->field->orig_table) { err_different_tables: - my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, - "system fields selected from different tables"); + my_error_as(ER_VERS_WRONG_PARAMS, ER_VERS_DIFFERENT_TABLES, MYF(0), table_name); return true; } orig_table= f->field->orig_table; @@ -6756,7 +6755,7 @@ bool Vers_parse_info::check_and_fix_implicit( } if ((f->versioning == Column_definition::VERSIONING_NOT_SET && - !declared_with_system_versioning) || + !with_system_versioning) || f->versioning == Column_definition::WITHOUT_VERSIONING) { f->flags|= VERS_OPTIMIZED_UPDATE_FLAG; @@ -6769,14 +6768,14 @@ bool Vers_parse_info::check_and_fix_implicit( { if (!generated_as_row.start && !generated_as_row.end) { - declared_with_system_versioning= false; + with_system_versioning= false; create_info->options&= ~HA_VERSIONED_TABLE; return false; } if (!generated_as_row.start || !generated_as_row.end) { - my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, - "both ROW START and ROW END system fields required in SELECT resultset"); + my_error_as(ER_VERS_WRONG_PARAMS, ER_MISSING, MYF(0), table_name, + generated_as_row.start ? "AS ROW END" : "AS ROW START"); return true; } } @@ -6807,8 +6806,8 @@ bool Vers_parse_info::check_and_fix_implicit( vers_cols == 0 && (plain_cols == 0 || !table_with_system_versioning)) { - my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, - "no columns defined with system versioning!"); + my_error_as(ER_VERS_WRONG_PARAMS, ER_VERS_NO_COLS_DEFINED, MYF(0), + table_name, "WITH SYSTEM VERSIONING"); return true; } @@ -6837,12 +6836,11 @@ bool Vers_parse_info::check_and_fix_alter(THD *thd, Alter_info *alter_info, if (!need_check() && !share->versioned) return false; - if (declared_without_system_versioning) + if (without_system_versioning) { if (!share->versioned) { - my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, - "table is not versioned"); + my_error_as(ER_VERS_WRONG_PARAMS, ER_VERS_NOT_VERSIONED, MYF(0), table_name); return true; } @@ -6854,10 +6852,9 @@ bool Vers_parse_info::check_and_fix_alter(THD *thd, Alter_info *alter_info, return false; } - if ((has_versioned_fields || has_unversioned_fields) && !share->versioned) + if ((versioned_fields || unversioned_fields) && !share->versioned) { - my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, - "Can not change fields versioning mode in a non-versioned table"); + my_error_as(ER_VERS_WRONG_PARAMS, ER_VERS_NOT_VERSIONED, MYF(0), table_name); return true; } @@ -6928,7 +6925,7 @@ bool Vers_parse_info::check_and_fix_alter(THD *thd, Alter_info *alter_info, } return fix_implicit(thd, alter_info, integer_fields) || - (declared_with_system_versioning && + (with_system_versioning && (check_with_conditions(table_name) || check_generated_type(table_name, alter_info, integer_fields))); } @@ -6958,8 +6955,8 @@ bool Vers_parse_info::fix_create_like(THD *thd, Alter_info *alter_info, if (!f_start || !f_end) { - my_error(ER_VERS_WRONG_PARAMS, MYF(0), table->table_name, - "Missed one of system versioning fields from source"); + my_error_as(ER_VERS_WRONG_PARAMS, ER_MISSING, MYF(0), table->table_name, + f_start ? "AS ROW END" : "AS ROW START"); return true; } @@ -6973,38 +6970,31 @@ bool Vers_parse_info::fix_create_like(THD *thd, Alter_info *alter_info, bool Vers_parse_info::check_with_conditions(const char *table_name) const { - if (!generated_as_row.start) + if (!generated_as_row.start || !generated_as_row.end) { - my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, - "'GENERATED AS ROW START' column missing"); - return true; - } - - if (!generated_as_row.end) - { - my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, - "'GENERATED AS ROW END' column missing"); + my_error_as(ER_VERS_WRONG_PARAMS, ER_MISSING, MYF(0), table_name, + generated_as_row.start ? "AS ROW END" : "AS ROW START"); return true; } if (!period_for_system_time.start || !period_for_system_time.end) { - my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, - "'PERIOD FOR SYSTEM_TIME' missing"); + my_error_as(ER_VERS_WRONG_PARAMS, ER_MISSING, MYF(0), table_name, + "PERIOD FOR SYSTEM_TIME"); return true; } if (generated_as_row.start != period_for_system_time.start) { - my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, - "'PERIOD FOR SYSTEM_TIME' and 'GENERATED AS ROW START' mismatch"); + my_error_as(ER_VERS_WRONG_PARAMS, ER_MISMATCH, MYF(0), table_name, + "PERIOD FOR SYSTEM_TIME", "AS ROW START"); return true; } if (generated_as_row.end != period_for_system_time.end) { - my_error(ER_VERS_WRONG_PARAMS, MYF(0), table_name, - "'PERIOD FOR SYSTEM_TIME' and 'GENERATED AS ROW END' mismatch"); + my_error_as(ER_VERS_WRONG_PARAMS, ER_MISMATCH, MYF(0), table_name, + "PERIOD FOR SYSTEM_TIME", "AS ROW END"); return true; } diff --git a/sql/handler.h b/sql/handler.h index 9c2b1bcf209..5462b33a3aa 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -1677,10 +1677,10 @@ class Create_field; struct Vers_parse_info { Vers_parse_info() : - declared_with_system_versioning(false), - declared_without_system_versioning(false), - has_versioned_fields(false), - has_unversioned_fields(false) + with_system_versioning(false), + without_system_versioning(false), + versioned_fields(false), + unversioned_fields(false) {} struct start_end_t @@ -1712,10 +1712,10 @@ private: bool need_check() const { return - has_versioned_fields || - has_unversioned_fields || - declared_with_system_versioning || - declared_without_system_versioning || + versioned_fields || + unversioned_fields || + with_system_versioning || + without_system_versioning || period_for_system_time.start.str || period_for_system_time.end.str || generated_as_row.start.str || @@ -1735,22 +1735,22 @@ public: HA_CREATE_INFO *create_info, TABLE_LIST *table); /** User has added 'WITH SYSTEM VERSIONING' to table definition */ - bool declared_with_system_versioning : 1; + bool with_system_versioning : 1; /** Use has added 'WITHOUT SYSTEM VERSIONING' to ALTER TABLE */ - bool declared_without_system_versioning : 1; + bool without_system_versioning : 1; /** At least one field was specified 'WITH SYSTEM VERSIONING'. Useful for error handling. */ - bool has_versioned_fields : 1; + bool versioned_fields : 1; /** At least one field was specified 'WITHOUT SYSTEM VERSIONING'. Useful for error handling. */ - bool has_unversioned_fields : 1; + bool unversioned_fields : 1; }; /** diff --git a/sql/partition_info.cc b/sql/partition_info.cc index 2b1bc41931d..dd4ec54673a 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -851,16 +851,8 @@ bool partition_info::vers_init_info(THD * thd) bool partition_info::vers_set_interval(const INTERVAL & i) { - if (i.neg) - { - my_error(ER_VERS_WRONG_PARAMS, MYF(0), "BY SYSTEM_TIME", "negative INTERVAL"); - return true; - } - if (i.second_part) - { - my_error(ER_VERS_WRONG_PARAMS, MYF(0), "BY SYSTEM_TIME", "second fractions in INTERVAL"); + if (i.neg || i.second_part) return true; - } DBUG_ASSERT(vers_info); @@ -874,20 +866,16 @@ bool partition_info::vers_set_interval(const INTERVAL & i) i.year * 365 * 30 * 24 * 60 * 60; if (vers_info->interval == 0) - { - my_error(ER_VERS_WRONG_PARAMS, MYF(0), "BY SYSTEM_TIME", "zero INTERVAL"); return true; - } + return false; } bool partition_info::vers_set_limit(ulonglong limit) { if (limit < 1) - { - my_error(ER_VERS_WRONG_PARAMS, MYF(0), "BY SYSTEM_TIME", "non-positive LIMIT"); return true; - } + DBUG_ASSERT(vers_info); vers_info->limit= limit; @@ -1978,15 +1966,11 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type, if (part_type == VERSIONING_PARTITION) { - if (num_parts < 2) - { - my_error(ER_VERS_WRONG_PARAMS, MYF(0), "BY SYSTEM_TIME", "unexpected number of partitions (expected > 1)"); - goto end; - } DBUG_ASSERT(vers_info); - if (!vers_info->now_part) + if (num_parts < 2 || !vers_info->now_part) { - my_error(ER_VERS_WRONG_PARAMS, MYF(0), "BY SYSTEM_TIME", "no `AS OF NOW` partition defined"); + DBUG_ASSERT(info && info->alias); + my_error(ER_VERS_WRONG_PARTS, MYF(0), info->alias); goto end; } DBUG_ASSERT(vers_info->initialized(false)); @@ -2135,7 +2119,7 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type, } if (now_parts > 1) { - my_error(ER_VERS_WRONG_PARAMS, MYF(0), "BY SYSTEM_TIME", "multiple `AS OF NOW` partitions"); + my_error(ER_VERS_WRONG_PARTS, MYF(0), info->alias); goto end; } result= FALSE; diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 17bd8a983a7..bbe4e6cff0e 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7500,7 +7500,7 @@ ER_VERS_FIELD_WRONG_TYPE eng "%`s must be of type %`s for versioned table %`s" ER_VERS_WRONG_PARAMS - eng "Wrong parameters for %`s: %s" + eng "Wrong parameters %s" ER_VERS_ENGINE_UNSUPPORTED eng "Engine does not support System Versioning for %`s" @@ -7532,8 +7532,8 @@ WARN_VERS_PART_NON_HISTORICAL ER_VERS_NOT_ALLOWED eng "%`s is not allowed for versioned table" -ER_VERS_WRONG_QUERY_TYPE - eng "%`s works only with %`s query type" +ER_VERS_RANGE_PROHIBITED + eng "SYSTEM_TIME range selector is prohibited" ER_VERS_VIEW_PROHIBITED eng "Creating VIEW %`s is prohibited!" @@ -7541,14 +7541,53 @@ ER_VERS_VIEW_PROHIBITED ER_VERS_DERIVED_PROHIBITED eng "Derived table is prohibited!" -ER_VERS_WRONG_QUERY - eng "Wrong versioned query: %s" +ER_VERS_UNUSED_CLAUSE + eng "Unused clause: '%s'" WARN_VERS_ALIAS_TOO_LONG - eng "Auto generated alias for `%s.%s` is too long; using `%s`." + eng "Auto generated alias for `%s.%s` is too long; using `%s`" ER_VERS_VTMD_ERROR eng "VTMD error: %s" +ER_MULTIPLE_CLAUSE + eng "for %`s: multiple '%s'" + +ER_MULTIPLE_CLAUSE_FOR + eng "for %`s: multiple '%s' for %`s" + +ER_MULTIPLE_CLAUSE_2 + eng "for %`s: multiple '%s' (%`s, %`s)" + +ER_MULTIPLE_IDENTIFIER + eng "for %`s: multiple %`s for '%s'" + +ER_NOT_ALLOWED + eng "for %`s: not allowed '%s'" + +ER_VERS_DIFFERENT_TABLES + eng "for %`s: system fields selected from different tables" + +ER_VERS_NO_COLS_DEFINED + eng "for %`s: no columns defined '%s'" + +ER_VERS_NOT_VERSIONED + eng "for %`s: table is not versioned" + +ER_MISSING + eng "for %`s: missing '%s'" + +ER_MISMATCH + eng "for %`s: mismatch '%s' and '%s'" + +ER_PART_WRONG_VALUE + eng "for partitioned %`s: wrong value for '%s'" + +ER_VERS_WRONG_PARTS + eng "Wrong partitions consistency for %`s: must have at least one 'VERSIONING' and exactly one last 'AS OF NOW'" + +ER_VERS_HISTORY_LOCK + eng "Versioned SELECT write-locking of history rows" + ER_WRONG_TABLESPACE_NAME 42000 eng "Incorrect tablespace name `%-.192s`" diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index caca441e5e4..7278b56a017 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -5368,7 +5368,8 @@ that are reorganised. { if (num_parts_dropped >= tab_part_info->num_parts - 1) { - my_error(ER_VERS_WRONG_PARAMS, MYF(0), "BY SYSTEM_TIME", "one `AS OF NOW` and at least one `VERSIONING` partition required"); + DBUG_ASSERT(table && table->s && table->s->table_name.str); + my_error(ER_VERS_WRONG_PARTS, MYF(0), table->s->table_name.str); goto err; } } @@ -5394,7 +5395,8 @@ that are reorganised. { if (part_elem->type == partition_element::AS_OF_NOW) { - my_error(ER_VERS_WRONG_PARAMS, MYF(0), "BY SYSTEM_TIME", "`AS OF NOW` partition can not be dropped"); + DBUG_ASSERT(table && table->s && table->s->table_name.str); + my_error(ER_VERS_WRONG_PARTS, MYF(0), table->s->table_name.str); goto err; } /* diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 2355552ab77..fef673b8c33 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -824,7 +824,7 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, case TL_WRITE_LOW_PRIORITY: case TL_WRITE: case TL_WRITE_ONLY: - my_error(ER_VERS_WRONG_PARAMS, MYF(0), "FOR SYSTEM_TIME query", "write-locking of historic rows"); + my_error(ER_VERS_HISTORY_LOCK, MYF(0)); DBUG_RETURN(-1); default: break; @@ -1004,7 +1004,7 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, if (!slex->vers_conditions.used && slex->vers_conditions) { - my_error(ER_VERS_WRONG_QUERY, MYF(0), "unused `QUERY FOR SYSTEM_TIME` clause!"); + my_error(ER_VERS_UNUSED_CLAUSE, MYF(0), "QUERY FOR SYSTEM_TIME"); DBUG_RETURN(-1); } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 73860aadca5..8b3014f301a 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -67,6 +67,7 @@ #include "lex_token.h" #include "sql_lex.h" #include "sql_sequence.h" +#include "vers_utils.h" /* this is to get the bison compilation windows warnings out */ #ifdef _MSC_VER @@ -851,6 +852,7 @@ Virtual_column_info *add_virtual_expression(THD *thd, Item *expr) enum trigger_order_type trigger_action_order_type; DDL_options_st object_ddl_options; enum vers_range_unit_t vers_range_unit; + enum Column_definition::enum_column_versioning vers_column_versioning; } %{ @@ -1973,7 +1975,7 @@ END_OF_INPUT %type trans_or_timestamp %type opt_for_system_time_clause - +%type with_or_without_system %% @@ -5206,8 +5208,11 @@ opt_part_values: } else { + DBUG_ASSERT(Lex->create_last_non_select_table); + DBUG_ASSERT(Lex->create_last_non_select_table->table_name); // FIXME: other ALTER commands? - my_yyabort_error((ER_VERS_WRONG_PARAMS, MYF(0), "BY SYSTEM_TIME", "AS OF NOW partition can not be added")); + my_yyabort_error((ER_VERS_WRONG_PARTS, MYF(0), + Lex->create_last_non_select_table->table_name)); } elem->type= partition_element::AS_OF_NOW; DBUG_ASSERT(part_info->vers_info); @@ -5235,7 +5240,11 @@ opt_part_values: } DBUG_ASSERT(part_info->vers_info); if (part_info->vers_info->now_part) - my_yyabort_error((ER_VERS_WRONG_PARAMS, MYF(0), "BY SYSTEM_TIME", "AS OF NOW partition is not last")); + { + DBUG_ASSERT(Lex->create_last_non_select_table); + DBUG_ASSERT(Lex->create_last_non_select_table->table_name); + my_yyabort_error((ER_VERS_WRONG_PARTS, MYF(0), Lex->create_last_non_select_table->table_name)); + } elem->type= partition_element::VERSIONING; if (part_info->init_column_part(thd)) { @@ -5551,10 +5560,13 @@ opt_versioning_interval: partition_info *part_info= Lex->part_info; DBUG_ASSERT(part_info->part_type == VERSIONING_PARTITION); INTERVAL interval; - if (get_interval_value($2, $3, &interval)) - my_yyabort_error((ER_VERS_WRONG_PARAMS, MYF(0), "BY SYSTEM_TIME", "wrong INTERVAL value")); - if (part_info->vers_set_interval(interval)) + if (get_interval_value($2, $3, &interval) || + part_info->vers_set_interval(interval)) + { + my_error_as(ER_VERS_WRONG_PARAMS, ER_PART_WRONG_VALUE, MYF(0), + Lex->create_last_non_select_table->table_name, "INTERVAL"); MYSQL_YYABORT; + } } ; @@ -5565,7 +5577,11 @@ opt_versioning_limit: partition_info *part_info= Lex->part_info; DBUG_ASSERT(part_info->part_type == VERSIONING_PARTITION); if (part_info->vers_set_limit($2)) + { + my_error_as(ER_VERS_WRONG_PARAMS, ER_PART_WRONG_VALUE, MYF(0), + Lex->create_last_non_select_table->table_name, "LIMIT"); MYSQL_YYABORT; + } } ; @@ -5936,32 +5952,28 @@ create_table_option: Lex->create_info.used_fields|= HA_CREATE_USED_SEQUENCE; Lex->create_info.sequence= $3; } - | WITH_SYSTEM_SYM VERSIONING_SYM + | WITH_SYSTEM_SYM table_versioning { - const char *table_name= - Lex->create_last_non_select_table->table_name; - Vers_parse_info &info= Lex->vers_get_info(); - if (info.declared_with_system_versioning || - info.declared_without_system_versioning) - my_yyabort_error( - (ER_VERS_WRONG_PARAMS, MYF(0), table_name, - "Versioning specified more than once for the same table")); - - info.declared_with_system_versioning= true; + Lex->vers_get_info().with_system_versioning= true; Lex->create_info.options|= HA_VERSIONED_TABLE; } - | WITHOUT SYSTEM VERSIONING_SYM + | WITHOUT SYSTEM table_versioning { - const char *table_name= - Lex->create_last_non_select_table->table_name; - Vers_parse_info &info= Lex->vers_get_info(); - if (info.declared_with_system_versioning || - info.declared_without_system_versioning) - my_yyabort_error( - (ER_VERS_WRONG_PARAMS, MYF(0), table_name, - "Versioning specified more than once for the same table")); + Lex->vers_get_info().without_system_versioning= true; + } + ; - info.declared_without_system_versioning= true; +table_versioning: + VERSIONING_SYM + { + Vers_parse_info &info= Lex->vers_get_info(); + if (info.with_system_versioning || info.without_system_versioning) + { + my_error_as(ER_VERS_WRONG_PARAMS, ER_MULTIPLE_CLAUSE, MYF(0), + Lex->create_last_non_select_table->table_name, + "WITH/WITHOUT SYSTEM VERSIONING"); + MYSQL_YYABORT; + } } ; @@ -6169,9 +6181,10 @@ period_for_system_time: Vers_parse_info &info= Lex->vers_get_info(); if (!my_strcasecmp(system_charset_info, $4.str, $6.str)) { - my_yyabort_error((ER_VERS_WRONG_PARAMS, MYF(0), - Lex->create_last_non_select_table->table_name, - "'PERIOD FOR SYSTEM_TIME' columns must be different")); + my_error_as(ER_VERS_WRONG_PARAMS, ER_MULTIPLE_IDENTIFIER, MYF(0), + Lex->create_last_non_select_table->table_name, $4.str, + "PERIOD FOR SYSTEM_TIME"); + MYSQL_YYABORT; } info.set_period_for_system_time($4, $6); } @@ -6288,17 +6301,17 @@ field_def: const char *table_name= lex->create_last_non_select_table->table_name; LString_i *p; - const char* err; + const char* clause; switch ($4) { case 1: p= &info.generated_as_row.start; - err= "multiple 'GENERATED ALWAYS AS ROW START'"; + clause= "AS ROW START"; lex->last_field->flags|= VERS_SYS_START_FLAG; break; case 0: p= &info.generated_as_row.end; - err= "multiple 'GENERATED ALWAYS AS ROW END'"; + clause= "AS ROW END"; lex->last_field->flags|= VERS_SYS_END_FLAG; break; default: @@ -6309,7 +6322,9 @@ field_def: DBUG_ASSERT(p); if (*p) { - my_yyabort_error((ER_VERS_WRONG_PARAMS, MYF(0), table_name, err)); + my_error_as(ER_VERS_WRONG_PARAMS, ER_MULTIPLE_CLAUSE_2, MYF(0), + table_name, clause, field_name, p->ptr()); + MYSQL_YYABORT; } *p= field_name; if (lex->last_field->implicit_not_null) @@ -6805,31 +6820,30 @@ serial_attribute: new (thd->mem_root) engine_option_value($1, &Lex->last_field->option_list, &Lex->option_list_last); } - | WITH_SYSTEM_SYM VERSIONING_SYM + | with_or_without_system VERSIONING_SYM { - if (Lex->last_field->versioning != - Column_definition::VERSIONING_NOT_SET) - my_yyabort_error( - (ER_VERS_WRONG_PARAMS, MYF(0), + if (Lex->last_field->versioning != Column_definition::VERSIONING_NOT_SET) + { + my_error_as(ER_VERS_WRONG_PARAMS, ER_MULTIPLE_CLAUSE_FOR, MYF(0), Lex->create_last_non_select_table->table_name, - "Versioning specified more than once for the same field")); - - Lex->last_field->versioning = Column_definition::WITH_VERSIONING; - Lex->create_info.vers_info.has_versioned_fields= true; + "WITH/WITHOUT SYSTEM VERSIONING", Lex->last_field->field_name); + MYSQL_YYABORT; + } + Lex->last_field->versioning= $1; Lex->create_info.options|= HA_VERSIONED_TABLE; } - | WITHOUT SYSTEM VERSIONING_SYM - { - if (Lex->last_field->versioning != - Column_definition::VERSIONING_NOT_SET) - my_yyabort_error( - (ER_VERS_WRONG_PARAMS, MYF(0), - Lex->create_last_non_select_table->table_name, - "Versioning specified more than once for the same field")); + ; - Lex->last_field->versioning = Column_definition::WITHOUT_VERSIONING; - Lex->create_info.vers_info.has_unversioned_fields= true; - Lex->create_info.options|= HA_VERSIONED_TABLE; +with_or_without_system: + WITH_SYSTEM_SYM + { + Lex->create_info.vers_info.versioned_fields= true; + $$= Column_definition::WITH_VERSIONING; + } + | WITHOUT SYSTEM + { + Lex->create_info.vers_info.unversioned_fields= true; + $$= Column_definition::WITHOUT_VERSIONING; } ; @@ -13336,9 +13350,9 @@ show_param: lex->create_info.storage_media= HA_SM_DEFAULT; if (lex->vers_conditions.type != FOR_SYSTEM_TIME_UNSPECIFIED && - lex->vers_conditions.type != FOR_SYSTEM_TIME_AS_OF) { - my_yyabort_error((ER_VERS_WRONG_PARAMS, MYF(0), "FOR SYSTEM_TIME", - "only AS OF allowed here")); + lex->vers_conditions.type != FOR_SYSTEM_TIME_AS_OF) + { + my_yyabort_error((ER_VERS_RANGE_PROHIBITED, MYF(0))); } if ($4) Lex->last_table()->vers_conditions= Lex->vers_conditions; -- cgit v1.2.1 From 5570ab378935319400acbb69048fecf80fb7c57d Mon Sep 17 00:00:00 2001 From: Eugene Kosov Date: Mon, 3 Jul 2017 12:15:55 +0300 Subject: SQL: history records became alive on copy [fixes #212] --- sql/sql_table.cc | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 4c1ab495651..9728b2d77e8 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -10052,7 +10052,17 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, error= 1; break; } - error=to->file->ha_write_row(to->record[0]); + if (keep_versioned && to->versioned_by_engine() && + !thd->variables.vers_ddl_survival) + { + to->s->versioned= false; + } + error= to->file->ha_write_row(to->record[0]); + if (keep_versioned && to->versioned_by_engine() && + !thd->variables.vers_ddl_survival) + { + to->s->versioned= true; + } to->auto_increment_field_not_null= FALSE; if (error) { -- cgit v1.2.1 From 72de7721b9ebc80ab8cb8019d2ca955f666368df Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Mon, 3 Jul 2017 17:38:59 +0300 Subject: SQL: No implicit versioning when created from SELECT [closes #219] --- sql/handler.cc | 49 +++++++++++++++++-------------------------------- sql/sql_base.cc | 27 ++++++++++++++++++--------- 2 files changed, 35 insertions(+), 41 deletions(-) (limited to 'sql') diff --git a/sql/handler.cc b/sql/handler.cc index 3589c3aee19..0bf4f766dac 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -6675,31 +6675,31 @@ bool Vers_parse_info::check_and_fix_implicit( if (table->table && table->table->versioned()) vers_tables++; } - - // Possibly override default storage engine to match - // one used in source table. - if (!(create_info->used_fields & HA_CREATE_USED_ENGINE)) - { - List_iterator_fast it(alter_info->create_list); - while (Create_field *f= it++) - { - if (is_trx_start(*f) || is_trx_end(*f)) - { - create_info->db_type= f->field->orig_table->file->ht; - break; - } - } - } } // CREATE ... SELECT: if at least one table in SELECT is versioned, // then created table will be versioned. - if (thd->variables.vers_force || vers_tables > 0) + if (thd->variables.vers_force) { with_system_versioning= true; create_info->options|= HA_VERSIONED_TABLE; } + // Possibly override default storage engine to match one used in source table. + if (from_select && with_system_versioning && + !(create_info->used_fields & HA_CREATE_USED_ENGINE)) + { + List_iterator_fast it(alter_info->create_list); + while (Create_field *f= it++) + { + if (is_trx_start(*f) || is_trx_end(*f)) + { + create_info->db_type= f->field->orig_table->file->ht; + break; + } + } + } + if (!need_check()) return false; @@ -6764,22 +6764,7 @@ bool Vers_parse_info::check_and_fix_implicit( bool integer_fields= create_info->db_type->flags & HTON_NATIVE_SYS_VERSIONING; - if (vers_tables > 0) - { - if (!generated_as_row.start && !generated_as_row.end) - { - with_system_versioning= false; - create_info->options&= ~HA_VERSIONED_TABLE; - return false; - } - if (!generated_as_row.start || !generated_as_row.end) - { - my_error_as(ER_VERS_WRONG_PARAMS, ER_MISSING, MYF(0), table_name, - generated_as_row.start ? "AS ROW END" : "AS ROW START"); - return true; - } - } - else if (fix_implicit(thd, alter_info, integer_fields)) + if (fix_implicit(thd, alter_info, integer_fields)) return true; int plain_cols= 0; // column doesn't have WITH or WITHOUT SYSTEM VERSIONING diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 8cdc835c0df..238fa3a0fa4 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -7596,16 +7596,25 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, tl->vers_conditions.type == FOR_SYSTEM_TIME_UNSPECIFIED ? slex->vers_conditions.type : tl->vers_conditions.type; - if ((sys_field && (thd->lex->sql_command == SQLCOM_CREATE_VIEW || - slex->nest_level > 0 || - (vers_hide == VERS_HIDE_FULL && thd->lex->sql_command != SQLCOM_CREATE_TABLE))) || - ((fl & HIDDEN_FLAG) && ( - !sys_field || - vers_hide == VERS_HIDE_IMPLICIT || - (vers_hide == VERS_HIDE_AUTO && ( - vers_type == FOR_SYSTEM_TIME_UNSPECIFIED || - vers_type == FOR_SYSTEM_TIME_AS_OF))))) + enum_sql_command sql_command= thd->lex->sql_command; + unsigned int create_options= thd->lex->create_info.options; + + if ( + sql_command == SQLCOM_CREATE_TABLE ? + sys_field && !(create_options & HA_VERSIONED_TABLE) : ( + sys_field ? + (sql_command == SQLCOM_CREATE_VIEW || + slex->nest_level > 0 || + vers_hide == VERS_HIDE_FULL || + ((fl & HIDDEN_FLAG) && ( + vers_hide == VERS_HIDE_IMPLICIT || + (vers_hide == VERS_HIDE_AUTO && ( + vers_type == FOR_SYSTEM_TIME_UNSPECIFIED || + vers_type == FOR_SYSTEM_TIME_AS_OF))))) : + (fl & HIDDEN_FLAG))) + { continue; + } } else if (item->type() == Item::REF_ITEM) { -- cgit v1.2.1 From 1903b407da2fc74fb3dc90351b1a92edaeff8874 Mon Sep 17 00:00:00 2001 From: Eugene Kosov Date: Tue, 4 Jul 2017 17:45:14 +0300 Subject: SQL: ignore columns WITHOUT VERSIONING [fixes #220] --- sql/handler.cc | 54 ++++++++++++++++++++++++++++++------------------------ sql/handler.h | 16 ++++++++-------- sql/sql_table.cc | 6 +++--- sql/sql_yacc.yy | 4 ++-- sql/unireg.cc | 6 ++---- 5 files changed, 45 insertions(+), 41 deletions(-) (limited to 'sql') diff --git a/sql/handler.cc b/sql/handler.cc index 0bf4f766dac..ae685482219 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -6584,12 +6584,12 @@ int del_global_index_stat(THD *thd, TABLE* table, KEY* key_info) bool Vers_parse_info::is_trx_start(const char *name) const { DBUG_ASSERT(name); - return generated_as_row.start && generated_as_row.start == LString_i(name); + return as_row.start && as_row.start == LString_i(name); } bool Vers_parse_info::is_trx_end(const char *name) const { DBUG_ASSERT(name); - return generated_as_row.end && generated_as_row.end == LString_i(name); + return as_row.end && as_row.end == LString_i(name); } bool Vers_parse_info::is_trx_start(const Create_field &f) const { @@ -6638,8 +6638,7 @@ bool Vers_parse_info::fix_implicit(THD *thd, Alter_info *alter_info, bool integer_fields) { // If user specified some of these he must specify the others too. Do nothing. - if (generated_as_row.start || generated_as_row.end || - period_for_system_time.start || period_for_system_time.end) + if (as_row.start || as_row.end || system_time.start || system_time.end) return false; alter_info->flags|= Alter_info::ALTER_ADD_COLUMN; @@ -6647,8 +6646,8 @@ bool Vers_parse_info::fix_implicit(THD *thd, Alter_info *alter_info, static const LString sys_trx_start= "sys_trx_start"; static const LString sys_trx_end= "sys_trx_end"; - period_for_system_time= start_end_t(sys_trx_start, sys_trx_end); - generated_as_row= period_for_system_time; + system_time= start_end_t(sys_trx_start, sys_trx_end); + as_row= system_time; return vers_create_sys_field(thd, sys_trx_start, alter_info, VERS_SYS_START_FLAG, @@ -6703,6 +6702,13 @@ bool Vers_parse_info::check_and_fix_implicit( if (!need_check()) return false; + if (!versioned_fields && unversioned_fields && !with_system_versioning) + { + // All is correct but this table is not versioned. + create_info->options&= ~HA_VERSIONED_TABLE; + return false; + } + if (without_system_versioning) { my_error_as(ER_VERS_WRONG_PARAMS, ER_NOT_ALLOWED, MYF(0), table_name, @@ -6710,7 +6716,8 @@ bool Vers_parse_info::check_and_fix_implicit( return true; } - if (!with_system_versioning && !versioned_fields) + if ((system_time.start || system_time.end || as_row.start || as_row.end) && + !with_system_versioning) { my_error_as(ER_VERS_WRONG_PARAMS, ER_MISSING, MYF(0), table_name, "WITH SYSTEM VERSIONING"); @@ -6723,7 +6730,7 @@ bool Vers_parse_info::check_and_fix_implicit( { if (is_trx_start(*f)) { - if (!generated_as_row.start) // not inited in CREATE ... SELECT + if (!as_row.start) // not inited in CREATE ... SELECT { DBUG_ASSERT(vers_tables > 0); if (orig_table && orig_table != f->field->orig_table) @@ -6733,14 +6740,14 @@ bool Vers_parse_info::check_and_fix_implicit( return true; } orig_table= f->field->orig_table; - generated_as_row.start= f->field_name; - period_for_system_time.start= generated_as_row.start; + as_row.start= f->field_name; + system_time.start= as_row.start; } continue; } if (is_trx_end(*f)) { - if (!generated_as_row.end) + if (!as_row.end) { DBUG_ASSERT(vers_tables > 0); if (orig_table && orig_table != f->field->orig_table) @@ -6748,8 +6755,8 @@ bool Vers_parse_info::check_and_fix_implicit( goto err_different_tables; } orig_table= f->field->orig_table; - generated_as_row.end= f->field_name; - period_for_system_time.end= generated_as_row.end; + as_row.end= f->field_name; + system_time.end= as_row.end; } continue; } @@ -6782,8 +6789,7 @@ bool Vers_parse_info::check_and_fix_implicit( } bool table_with_system_versioning= - generated_as_row.start || generated_as_row.end || - period_for_system_time.start || period_for_system_time.end; + as_row.start || as_row.end || system_time.start || system_time.end; if (!thd->lex->tmp_table() && // CREATE from SELECT (Create_fields are not yet added) @@ -6853,8 +6859,8 @@ bool Vers_parse_info::check_and_fix_alter(THD *thd, Alter_info *alter_info, const char *end= share->vers_end_field()->field_name; DBUG_ASSERT(start && end); - generated_as_row= start_end_t(start, end); - period_for_system_time= generated_as_row; + as_row= start_end_t(start, end); + system_time= as_row; if (alter_info->create_list.elements) { @@ -6945,8 +6951,8 @@ bool Vers_parse_info::fix_create_like(THD *thd, Alter_info *alter_info, return true; } - generated_as_row= start_end_t(f_start->field_name, f_end->field_name); - period_for_system_time= generated_as_row; + as_row= start_end_t(f_start->field_name, f_end->field_name); + system_time= as_row; create_info->options|= HA_VERSIONED_TABLE; return false; @@ -6955,28 +6961,28 @@ bool Vers_parse_info::fix_create_like(THD *thd, Alter_info *alter_info, bool Vers_parse_info::check_with_conditions(const char *table_name) const { - if (!generated_as_row.start || !generated_as_row.end) + if (!as_row.start || !as_row.end) { my_error_as(ER_VERS_WRONG_PARAMS, ER_MISSING, MYF(0), table_name, - generated_as_row.start ? "AS ROW END" : "AS ROW START"); + as_row.start ? "AS ROW END" : "AS ROW START"); return true; } - if (!period_for_system_time.start || !period_for_system_time.end) + if (!system_time.start || !system_time.end) { my_error_as(ER_VERS_WRONG_PARAMS, ER_MISSING, MYF(0), table_name, "PERIOD FOR SYSTEM_TIME"); return true; } - if (generated_as_row.start != period_for_system_time.start) + if (as_row.start != system_time.start) { my_error_as(ER_VERS_WRONG_PARAMS, ER_MISMATCH, MYF(0), table_name, "PERIOD FOR SYSTEM_TIME", "AS ROW START"); return true; } - if (generated_as_row.end != period_for_system_time.end) + if (as_row.end != system_time.end) { my_error_as(ER_VERS_WRONG_PARAMS, ER_MISMATCH, MYF(0), table_name, "PERIOD FOR SYSTEM_TIME", "AS ROW END"); diff --git a/sql/handler.h b/sql/handler.h index 5462b33a3aa..24d1a73ec92 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -1694,13 +1694,13 @@ struct Vers_parse_info LString_i end; }; - start_end_t period_for_system_time; - start_end_t generated_as_row; + start_end_t system_time; + start_end_t as_row; void set_period_for_system_time(LString start, LString end) { - period_for_system_time.start = start; - period_for_system_time.end = end; + system_time.start = start; + system_time.end = end; } private: @@ -1716,10 +1716,10 @@ private: unversioned_fields || with_system_versioning || without_system_versioning || - period_for_system_time.start.str || - period_for_system_time.end.str || - generated_as_row.start.str || - generated_as_row.end.str; + system_time.start || + system_time.end || + as_row.start || + as_row.end; } bool check_with_conditions(const char *table_name) const; bool check_generated_type(const char *table_name, Alter_info *alter_info, diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 9728b2d77e8..cb4bb052a65 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -4326,9 +4326,9 @@ vers_prepare_keys(THD *thd, { DBUG_ASSERT(create_info->versioned()); - const char *row_start_field= create_info->vers_info.generated_as_row.start; + const char *row_start_field= create_info->vers_info.as_row.start; DBUG_ASSERT(row_start_field); - const char *row_end_field= create_info->vers_info.generated_as_row.end; + const char *row_end_field= create_info->vers_info.as_row.end; DBUG_ASSERT(row_end_field); List_iterator key_it(alter_info->key_list); @@ -4355,7 +4355,7 @@ vers_prepare_keys(THD *thd, continue; // Key already contains Sys_start or Sys_end Key_part_spec *key_part_sys_end_col= - new(thd->mem_root) Key_part_spec(create_info->vers_info.generated_as_row.end, 0); + new (thd->mem_root) Key_part_spec(create_info->vers_info.as_row.end, 0); key->columns.push_back(key_part_sys_end_col); } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 8b3014f301a..b0365d2b97f 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -6305,12 +6305,12 @@ field_def: switch ($4) { case 1: - p= &info.generated_as_row.start; + p= &info.as_row.start; clause= "AS ROW START"; lex->last_field->flags|= VERS_SYS_START_FLAG; break; case 0: - p= &info.generated_as_row.end; + p= &info.as_row.end; clause= "AS ROW END"; lex->last_field->flags|= VERS_SYS_END_FLAG; break; diff --git a/sql/unireg.cc b/sql/unireg.cc index 7a5aadb1c22..b647a284b14 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -99,10 +99,8 @@ vers_get_field(HA_CREATE_INFO *create_info, List &create_fields, b List_iterator it(create_fields); Create_field *sql_field = NULL; - const char *row_field = - row_start ? - create_info->vers_info.generated_as_row.start : - create_info->vers_info.generated_as_row.end; + const char *row_field= row_start ? create_info->vers_info.as_row.start + : create_info->vers_info.as_row.end; DBUG_ASSERT(row_field); for (unsigned field_no = 0; (sql_field = it++); ++field_no) -- cgit v1.2.1 From dcb54040bcb6927e1fafe039979b816865c7c805 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Fri, 7 Jul 2017 17:52:23 +0300 Subject: SQL: VTQ testing iface moved to plugin [closes #224] --- sql/item_create.cc | 169 ++++------------------------------------------------- sql/item_create.h | 40 +++++++++++++ sql/item_func.h | 3 + sql/item_vers.cc | 63 +++++--------------- sql/item_vers.h | 24 ++++---- sql/sql_plugin.cc | 14 ----- sql/sql_plugin.h | 2 - sql/sql_select.cc | 27 +++++---- sql/vers_string.h | 4 ++ 9 files changed, 96 insertions(+), 250 deletions(-) (limited to 'sql') diff --git a/sql/item_create.cc b/sql/item_create.cc index 7e8c60591e6..140abdd074d 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -104,36 +104,6 @@ bool get_length_and_scale(ulonglong length, ulonglong decimals, ============================================================================= */ -/** - Adapter for native functions with a variable number of arguments. - The main use of this class is to discard the following calls: - foo(expr1 AS name1, expr2 AS name2, ...) - which are syntactically correct (the syntax can refer to a UDF), - but semantically invalid for native functions. -*/ - -class Create_native_func : public Create_func -{ -public: - virtual Item *create_func(THD *thd, LEX_STRING name, List *item_list); - - /** - Builder method, with no arguments. - @param thd The current thread - @param name The native function name - @param item_list The function parameters, none of which are named - @return An item representing the function call - */ - virtual Item *create_native(THD *thd, LEX_STRING name, - List *item_list) = 0; - -protected: - /** Constructor. */ - Create_native_func() {} - /** Destructor. */ - virtual ~Create_native_func() {} -}; - /** Adapter for functions that takes exactly zero arguments. @@ -6678,125 +6648,6 @@ Create_func_year_week::create_native(THD *thd, LEX_STRING name, } -/* System Versioning: VTQ_TRX_ID(), VTQ_COMMIT_ID(), VTQ_BEGIN_TS(), VTQ_COMMIT_TS(), VTQ_ISO_LEVEL() */ -template -class Create_func_vtq : public Create_native_func -{ -public: - virtual Item *create_native(THD *thd, LEX_STRING name, List *item_list); - - static Create_func_vtq s_singleton; - -protected: - Create_func_vtq() {} - virtual ~Create_func_vtq() {} -}; - -template -Create_func_vtq Create_func_vtq::s_singleton; - -template -Item* -Create_func_vtq::create_native(THD *thd, LEX_STRING name, - List *item_list) -{ - Item *func= NULL; - int arg_count= 0; - - if (item_list != NULL) - arg_count= item_list->elements; - - switch (arg_count) { - case 1: - { - Item *param_1= item_list->pop(); - switch (VTQ_FIELD) - { - case VTQ_BEGIN_TS: - case VTQ_COMMIT_TS: - func= new (thd->mem_root) Item_func_vtq_ts(thd, param_1, VTQ_FIELD); - break; - case VTQ_TRX_ID: - case VTQ_COMMIT_ID: - case VTQ_ISO_LEVEL: - func= new (thd->mem_root) Item_func_vtq_id(thd, param_1, VTQ_FIELD); - break; - default: - DBUG_ASSERT(0); - } - break; - } - case 2: - { - Item *param_1= item_list->pop(); - Item *param_2= item_list->pop(); - switch (VTQ_FIELD) - { - case VTQ_TRX_ID: - case VTQ_COMMIT_ID: - func= new (thd->mem_root) Item_func_vtq_id(thd, param_1, param_2, VTQ_FIELD); - break; - default: - goto error; - } - break; - } - error: - default: - { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); - break; - } - } - - return func; -}; - -template -class Create_func_vtq_trx_sees : public Create_native_func -{ -public: - virtual Item *create_native(THD *thd, LEX_STRING name, List *item_list) - { - Item *func= NULL; - int arg_count= 0; - - if (item_list != NULL) - arg_count= item_list->elements; - - switch (arg_count) { - case 2: - { - Item *param_1= item_list->pop(); - Item *param_2= item_list->pop(); - func= new (thd->mem_root) Item_func_vtq_trx_seesX(thd, param_1, param_2); - break; - } - default: - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); - break; - } - - return func; - } - - static Create_func_vtq_trx_sees s_singleton; - -protected: - Create_func_vtq_trx_sees() {} - virtual ~Create_func_vtq_trx_sees() {} -}; - -template -Create_func_vtq_trx_sees Create_func_vtq_trx_sees::s_singleton; - - -struct Native_func_registry -{ - LEX_STRING name; - Create_func *builder; -}; - #define BUILDER(F) & F::s_singleton #ifdef HAVE_SPATIAL @@ -7146,13 +6997,6 @@ static Native_func_registry func_array[] = { { C_STRING_WITH_LEN("UUID") }, BUILDER(Create_func_uuid)}, { { C_STRING_WITH_LEN("UUID_SHORT") }, BUILDER(Create_func_uuid_short)}, { { C_STRING_WITH_LEN("VERSION") }, BUILDER(Create_func_version)}, - { { C_STRING_WITH_LEN("VTQ_BEGIN_TS") }, BUILDER(Create_func_vtq)}, - { { C_STRING_WITH_LEN("VTQ_COMMIT_ID") }, BUILDER(Create_func_vtq)}, - { { C_STRING_WITH_LEN("VTQ_COMMIT_TS") }, BUILDER(Create_func_vtq)}, - { { C_STRING_WITH_LEN("VTQ_ISO_LEVEL") }, BUILDER(Create_func_vtq)}, - { { C_STRING_WITH_LEN("VTQ_TRX_ID") }, BUILDER(Create_func_vtq)}, - { { C_STRING_WITH_LEN("VTQ_TRX_SEES") }, BUILDER(Create_func_vtq_trx_sees)}, - { { C_STRING_WITH_LEN("VTQ_TRX_SEES_EQ") }, BUILDER(Create_func_vtq_trx_sees)}, { { C_STRING_WITH_LEN("WEEKDAY") }, BUILDER(Create_func_weekday)}, { { C_STRING_WITH_LEN("WEEKOFYEAR") }, BUILDER(Create_func_weekofyear)}, { { C_STRING_WITH_LEN("WITHIN") }, GEOM_BUILDER(Create_func_within)}, @@ -7182,8 +7026,6 @@ get_native_fct_hash_key(const uchar *buff, size_t *length, int item_create_init() { - Native_func_registry *func; - DBUG_ENTER("item_create_init"); if (my_hash_init(& native_functions_hash, @@ -7196,7 +7038,16 @@ int item_create_init() MYF(0))) DBUG_RETURN(1); - for (func= func_array; func->builder != NULL; func++) + DBUG_RETURN(item_create_append(func_array)); +} + +int item_create_append(Native_func_registry array[]) +{ + Native_func_registry *func; + + DBUG_ENTER("item_create_append"); + + for (func= array; func->builder != NULL; func++) { if (my_hash_insert(& native_functions_hash, (uchar*) func)) DBUG_RETURN(1); diff --git a/sql/item_create.h b/sql/item_create.h index 05fe48f656a..b8749a4513a 100644 --- a/sql/item_create.h +++ b/sql/item_create.h @@ -19,6 +19,8 @@ #ifndef ITEM_CREATE_H #define ITEM_CREATE_H +#include "item_func.h" // Cast_target + typedef struct st_udf_func udf_func; /** @@ -66,6 +68,37 @@ protected: }; +/** + Adapter for native functions with a variable number of arguments. + The main use of this class is to discard the following calls: + foo(expr1 AS name1, expr2 AS name2, ...) + which are syntactically correct (the syntax can refer to a UDF), + but semantically invalid for native functions. +*/ + +class Create_native_func : public Create_func +{ +public: + virtual Item *create_func(THD *thd, LEX_STRING name, List *item_list); + + /** + Builder method, with no arguments. + @param thd The current thread + @param name The native function name + @param item_list The function parameters, none of which are named + @return An item representing the function call + */ + virtual Item *create_native(THD *thd, LEX_STRING name, + List *item_list) = 0; + +protected: + /** Constructor. */ + Create_native_func() {} + /** Destructor. */ + virtual ~Create_native_func() {} +}; + + /** Function builder for qualified functions. This builder is used with functions call using a qualified function name @@ -183,7 +216,14 @@ Item *create_temporal_literal(THD *thd, const String *str, type, send_error); } +struct Native_func_registry +{ + LEX_STRING name; + Create_func *builder; +}; + int item_create_init(); +int item_create_append(Native_func_registry array[]); void item_create_cleanup(); Item *create_func_dyncol_create(THD *thd, List &list); diff --git a/sql/item_func.h b/sql/item_func.h index 0b398adb937..b7fb5d9cdfa 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -30,6 +30,9 @@ extern "C" /* Bug in BSDI include file */ } #endif +#include "sql_udf.h" // udf_handler +#include "my_decimal.h" // string2my_decimal + class Item_func :public Item_func_or_sum { diff --git a/sql/item_vers.cc b/sql/item_vers.cc index eff4c15db6b..3562cc7fbc9 100644 --- a/sql/item_vers.cc +++ b/sql/item_vers.cc @@ -25,57 +25,27 @@ Item_func_vtq_ts::Item_func_vtq_ts( THD *thd, - Item* a, - vtq_field_t _vtq_field, - handlerton* hton) : - VTQ_common(thd, a, hton), - vtq_field(_vtq_field) -{ - decimals= 6; - null_value= true; - DBUG_ASSERT(arg_count == 1 && args[0]); -} - -Item_func_vtq_ts::Item_func_vtq_ts( - THD *thd, + handlerton* hton, Item* a, vtq_field_t _vtq_field) : - VTQ_common(thd, a), + VTQ_common(thd, hton, a), vtq_field(_vtq_field) { decimals= 6; null_value= true; DBUG_ASSERT(arg_count == 1 && args[0]); + check_hton(); } template void -VTQ_common::init_hton() +VTQ_common::check_hton() { - if (!hton) + DBUG_ASSERT(hton); + if (!(hton->flags & HTON_NATIVE_SYS_VERSIONING) && hton->db_type != DB_TYPE_HEAP) { - if (Item_func_X::args[0]->type() == Item::FIELD_ITEM) - { - Item_field *f= - static_cast(Item_func_X::args[0]); - DBUG_ASSERT( - f->field && - f->field->table && - f->field->table->s && - f->field->table->s->db_plugin); - hton= f->field->table->file->partition_ht(); - DBUG_ASSERT(hton); - } - else if (innodb_plugin) - { - hton= plugin_hton(plugin_int_to_ref(innodb_plugin)); - DBUG_ASSERT(hton); - } - if (hton && !(hton->flags & HTON_NATIVE_SYS_VERSIONING)) - { - my_error(ER_VERS_ENGINE_UNSUPPORTED, MYF(0), Item::name ? Item::name : this->func_name()); - hton= NULL; - } + my_error(ER_VERS_ENGINE_UNSUPPORTED, MYF(0), Item::name ? Item::name : this->func_name()); + hton= NULL; } } @@ -92,8 +62,6 @@ Item_func_vtq_ts::get_date(MYSQL_TIME *res, ulonglong fuzzy_date) return false; } - init_hton(); - if (!hton) return true; @@ -106,10 +74,11 @@ Item_func_vtq_ts::get_date(MYSQL_TIME *res, ulonglong fuzzy_date) Item_func_vtq_id::Item_func_vtq_id( THD *thd, + handlerton *hton, Item* a, vtq_field_t _vtq_field, bool _backwards) : - VTQ_common(thd, a), + VTQ_common(thd, hton, a), vtq_field(_vtq_field), backwards(_backwards) { @@ -118,14 +87,16 @@ Item_func_vtq_id::Item_func_vtq_id( unsigned_flag= 1; null_value= true; DBUG_ASSERT(arg_count == 1 && args[0]); + check_hton(); } Item_func_vtq_id::Item_func_vtq_id( THD *thd, + handlerton *hton, Item* a, Item* b, vtq_field_t _vtq_field) : - VTQ_common(thd, a, b), + VTQ_common(thd, hton, a, b), vtq_field(_vtq_field), backwards(false) { @@ -134,6 +105,7 @@ Item_func_vtq_id::Item_func_vtq_id( unsigned_flag= 1; null_value= true; DBUG_ASSERT(arg_count == 2 && args[0] && args[1]); + check_hton(); } longlong @@ -186,8 +158,6 @@ Item_func_vtq_id::get_by_commit_ts(MYSQL_TIME &commit_ts, bool backwards) longlong Item_func_vtq_id::val_int() { - init_hton(); - if (!hton) { null_value= true; @@ -222,9 +192,10 @@ Item_func_vtq_id::val_int() Item_func_vtq_trx_sees::Item_func_vtq_trx_sees( THD *thd, + handlerton *hton, Item* a, Item* b) : - VTQ_common(thd, a, b), + VTQ_common(thd, hton, a, b), accept_eq(false) { null_value= true; @@ -237,8 +208,6 @@ Item_func_vtq_trx_sees::val_int() THD *thd= current_thd; DBUG_ASSERT(thd); - init_hton(); - if (!hton) { null_value= true; diff --git a/sql/item_vers.h b/sql/item_vers.h index aa5575ff9b1..86e0711e5d2 100644 --- a/sql/item_vers.h +++ b/sql/item_vers.h @@ -29,15 +29,12 @@ class VTQ_common : public Item_func_X { protected: handlerton *hton; - void init_hton(); + void check_hton(); public: - VTQ_common(THD *thd, Item* a) : - Item_func_X(thd, a), - hton(NULL) {} - VTQ_common(THD *thd, Item* a, Item* b) : + VTQ_common(THD *thd, handlerton* _hton, Item* a, Item* b) : Item_func_X(thd, a, b), - hton(NULL) {} - VTQ_common(THD *thd, Item* a, handlerton* _hton) : + hton(_hton) {} + VTQ_common(THD *thd, handlerton* _hton, Item* a) : Item_func_X(thd, a), hton(_hton) {} }; @@ -47,8 +44,7 @@ class Item_func_vtq_ts : { vtq_field_t vtq_field; public: - Item_func_vtq_ts(THD *thd, Item* a, vtq_field_t _vtq_field, handlerton *hton); - Item_func_vtq_ts(THD *thd, Item* a, vtq_field_t _vtq_field); + Item_func_vtq_ts(THD *thd, handlerton *hton, Item* a, vtq_field_t _vtq_field); const char *func_name() const { if (vtq_field == VTQ_BEGIN_TS) @@ -73,8 +69,8 @@ class Item_func_vtq_id : longlong get_by_commit_ts(MYSQL_TIME &commit_ts, bool backwards); public: - Item_func_vtq_id(THD *thd, Item* a, vtq_field_t _vtq_field, bool _backwards= false); - Item_func_vtq_id(THD *thd, Item* a, Item* b, vtq_field_t _vtq_field); + Item_func_vtq_id(THD *thd, handlerton *hton, Item* a, vtq_field_t _vtq_field, bool _backwards= false); + Item_func_vtq_id(THD *thd, handlerton *hton, Item* a, Item* b, vtq_field_t _vtq_field); vtq_record_t *vtq_cached_result() { return &cached_result; } @@ -112,7 +108,7 @@ protected: bool accept_eq; public: - Item_func_vtq_trx_sees(THD *thd, Item* a, Item* b); + Item_func_vtq_trx_sees(THD *thd, handlerton *hton, Item* a, Item* b); const char *func_name() const { return "vtq_trx_sees"; @@ -126,8 +122,8 @@ class Item_func_vtq_trx_sees_eq : public Item_func_vtq_trx_sees { public: - Item_func_vtq_trx_sees_eq(THD *thd, Item* a, Item* b) : - Item_func_vtq_trx_sees(thd, a, b) + Item_func_vtq_trx_sees_eq(THD *thd, handlerton *hton, Item* a, Item* b) : + Item_func_vtq_trx_sees(thd, hton, a, b) { accept_eq= true; } diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index 82fbb7c62cc..c307bc4f425 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -233,8 +233,6 @@ static int plugin_array_version=0; static bool initialized= 0; ulong dlopen_count; -st_plugin_int* innodb_plugin= NULL; - /* write-lock on LOCK_system_variables_hash is required before modifying @@ -1424,18 +1422,6 @@ static int plugin_initialize(MEM_ROOT *tmp_root, struct st_plugin_int *plugin, } state= PLUGIN_IS_READY; // plugin->init() succeeded - { - static const char * INNODB= "InnoDB"; - static const uint INNODB_LEN= strlen(INNODB); - - if (!my_strnncoll(&my_charset_latin1, - (const uchar *) plugin->name.str, plugin->name.length, - (const uchar *) INNODB, INNODB_LEN)) - { - innodb_plugin= plugin; - } - } - if (plugin->plugin->status_vars) { /* diff --git a/sql/sql_plugin.h b/sql/sql_plugin.h index e399fec8339..7b89246a9f9 100644 --- a/sql/sql_plugin.h +++ b/sql/sql_plugin.h @@ -160,8 +160,6 @@ extern ulong plugin_maturity; extern TYPELIB plugin_maturity_values; extern const char *plugin_maturity_names[]; -extern st_plugin_int* innodb_plugin; - extern int plugin_init(int *argc, char **argv, int init_flags); extern void plugin_shutdown(void); void add_plugin_options(DYNAMIC_ARRAY *options, MEM_ROOT *mem_root); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index fef673b8c33..208946c1191 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -875,15 +875,14 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, DBUG_ASSERT(hton); row_start= newx Item_func_vtq_ts( thd, + hton, row_start, - VTQ_COMMIT_TS, - // FIXME: is it needed to pass hton or it can be deduced from arg 'a'? - hton); + VTQ_COMMIT_TS); row_end= newx Item_func_vtq_ts( thd, + hton, row_end, - VTQ_COMMIT_TS, - hton); + VTQ_COMMIT_TS); } Item *cond1= 0, *cond2= 0, *curr= 0; @@ -952,17 +951,17 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, break; case FOR_SYSTEM_TIME_AS_OF: trx_id0= vers_conditions.unit == UNIT_TIMESTAMP ? - newx Item_func_vtq_id(thd, vers_conditions.start, VTQ_TRX_ID) : + newx Item_func_vtq_id(thd, hton, vers_conditions.start, VTQ_TRX_ID) : vers_conditions.start; - cond1= newx Item_func_vtq_trx_sees_eq(thd, trx_id0, row_start); - cond2= newx Item_func_vtq_trx_sees(thd, row_end, trx_id0); + cond1= newx Item_func_vtq_trx_sees_eq(thd, hton, trx_id0, row_start); + cond2= newx Item_func_vtq_trx_sees(thd, hton, row_end, trx_id0); break; case FOR_SYSTEM_TIME_FROM_TO: case FOR_SYSTEM_TIME_BETWEEN: if (vers_conditions.unit == UNIT_TIMESTAMP) { - trx_id0= newx Item_func_vtq_id(thd, vers_conditions.start, VTQ_TRX_ID, true); - trx_id1= newx Item_func_vtq_id(thd, vers_conditions.end, VTQ_TRX_ID, false); + trx_id0= newx Item_func_vtq_id(thd, hton, vers_conditions.start, VTQ_TRX_ID, true); + trx_id1= newx Item_func_vtq_id(thd, hton, vers_conditions.end, VTQ_TRX_ID, false); } else { @@ -971,13 +970,13 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, } cond1= vers_conditions.type == FOR_SYSTEM_TIME_FROM_TO ? - newx Item_func_vtq_trx_sees(thd, trx_id1, row_start) : - newx Item_func_vtq_trx_sees_eq(thd, trx_id1, row_start); - cond2= newx Item_func_vtq_trx_sees_eq(thd, row_end, trx_id0); + newx Item_func_vtq_trx_sees(thd, hton, trx_id1, row_start) : + newx Item_func_vtq_trx_sees_eq(thd, hton, trx_id1, row_start); + cond2= newx Item_func_vtq_trx_sees_eq(thd, hton, row_end, trx_id0); break; case FOR_SYSTEM_TIME_BEFORE: trx_id0= vers_conditions.unit == UNIT_TIMESTAMP ? - newx Item_func_vtq_id(thd, vers_conditions.start, VTQ_TRX_ID) : + newx Item_func_vtq_id(thd, hton, vers_conditions.start, VTQ_TRX_ID) : vers_conditions.start; cond1= newx Item_func_lt(thd, row_end, trx_id0); break; diff --git a/sql/vers_string.h b/sql/vers_string.h index ddd691276db..9f42692791b 100644 --- a/sql/vers_string.h +++ b/sql/vers_string.h @@ -60,6 +60,10 @@ struct LEX_STRING_u : public Storage { return *this; } + const LEX_STRING& lex_string() const + { + return *(LEX_STRING *)this; + } }; template > -- cgit v1.2.1 From 60e456df3328657bba1648157abd1c1aa4b0838c Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Wed, 12 Jul 2017 10:36:02 +0300 Subject: SQL: system_time propagation from derived table [fixes #228] --- sql/sql_select.cc | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) (limited to 'sql') diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 208946c1191..ff3ce427ef5 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -771,7 +771,7 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, } SELECT_LEX *outer_slex= slex->next_select_in_list(); - bool use_slex_conds= false; + bool force_slex_conds= false; if (outer_slex) { if (slex->vers_derived_conds) @@ -786,15 +786,26 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, } if (slex->vers_conditions.import_outer) { - // Propagate query conditions from nearest outer SELECT_LEX: - while (outer_slex && (!outer_slex->vers_conditions || outer_slex->vers_conditions.from_inner)) - outer_slex= outer_slex->next_select_in_list(); - if (outer_slex) + DBUG_ASSERT(slex->master_unit()); + TABLE_LIST* derived= slex->master_unit()->derived; + DBUG_ASSERT(derived); + if (derived->vers_conditions) { - slex->vers_conditions= outer_slex->vers_conditions; - outer_slex->vers_conditions.used= true; - DBUG_ASSERT(slex->master_unit()->derived); - use_slex_conds= slex->master_unit()->derived->is_view(); + slex->vers_conditions= derived->vers_conditions; + derived->vers_conditions.used= true; + force_slex_conds= derived->is_view(); + } + else + { + // Propagate query conditions from nearest outer SELECT_LEX: + while (outer_slex && (!outer_slex->vers_conditions || outer_slex->vers_conditions.from_inner)) + outer_slex= outer_slex->next_select_in_list(); + if (outer_slex) + { + slex->vers_conditions= outer_slex->vers_conditions; + outer_slex->vers_conditions.used= true; + force_slex_conds= derived->is_view(); + } } } } @@ -803,7 +814,7 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, { if (table->table && table->table->versioned()) { - vers_select_conds_t &vers_conditions= use_slex_conds || !table->vers_conditions? + vers_select_conds_t &vers_conditions= force_slex_conds || !table->vers_conditions? (slex->vers_conditions.used= true, slex->vers_conditions) : table->vers_conditions; -- cgit v1.2.1 From 91c8b43e77ea1ec65eb16a178d8e63a7b3065854 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Wed, 12 Jul 2017 11:24:03 +0300 Subject: Parser: syntax for query system_time [closes #230] Eliminated `QUERY FOR`. --- sql/sql_lex.cc | 15 --------------- sql/sql_select.cc | 6 +++--- sql/sql_yacc.yy | 11 +++++------ sql/sql_yacc_ora.yy | 1 - 4 files changed, 8 insertions(+), 25 deletions(-) (limited to 'sql') diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 067b78e3a05..589d02fb55b 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1368,21 +1368,6 @@ int MYSQLlex(YYSTYPE *yylval, THD *thd) return FOR_SYM; } break; - case QUERY_SYM: - { - CHARSET_INFO *cs= thd->charset(); - const char *p= lip->get_ptr(); - while (my_isspace(cs, *p)) - ++p; - if (lip->get_end_of_query() - p > 3 && my_isspace(cs, p[3]) && - 0 == strncasecmp(p, "for", 3)) - { - token= lex_one_token(yylval, thd); - lip->add_digest_token(token, yylval); - return QUERY_FOR_SYM; - } - return QUERY_SYM; - } default: break; } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index ff3ce427ef5..2ae574398fc 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -708,7 +708,7 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, versioned_tables++; else if (table->vers_conditions) { - my_error(ER_VERSIONING_REQUIRED, MYF(0), "`FOR SYSTEM_TIME` query"); + my_error(ER_VERSIONING_REQUIRED, MYF(0), table->alias); DBUG_RETURN(-1); } } @@ -717,7 +717,7 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, { if (slex->vers_conditions) { - my_error(ER_VERSIONING_REQUIRED, MYF(0), "`FOR SYSTEM_TIME` query"); + my_error(ER_VERS_UNUSED_CLAUSE, MYF(0), "SYSTEM_TIME"); DBUG_RETURN(-1); } DBUG_RETURN(0); @@ -1014,7 +1014,7 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, if (!slex->vers_conditions.used && slex->vers_conditions) { - my_error(ER_VERS_UNUSED_CLAUSE, MYF(0), "QUERY FOR SYSTEM_TIME"); + my_error(ER_VERS_UNUSED_CLAUSE, MYF(0), "SYSTEM_TIME"); DBUG_RETURN(-1); } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index b0365d2b97f..27705f6bd41 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1355,7 +1355,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token PURGE %token QUARTER_SYM %token QUERY_SYM -%token QUERY_FOR_SYM /* INTERNAL */ %token QUICK %token RAISE_SYM /* Oracle-PLSQL-R */ %token RANGE_SYM /* SQL-2003-R */ @@ -8804,7 +8803,7 @@ table_expression: opt_group_clause opt_having_clause opt_window_clause - opt_query_for_system_time_clause + opt_system_time_clause ; opt_table_expression: @@ -8850,10 +8849,10 @@ trans_or_timestamp: } ; -opt_query_for_system_time_clause: +opt_system_time_clause: /* empty */ {} - | QUERY_FOR_SYM SYSTEM_TIME_SYM for_system_time_expr + | SYSTEM_TIME_SYM system_time_expr { DBUG_ASSERT(Select); Select->vers_conditions= Lex->vers_conditions; @@ -8865,13 +8864,13 @@ opt_for_system_time_clause: { $$= false; } - | FOR_SYSTEM_TIME_SYM for_system_time_expr + | FOR_SYSTEM_TIME_SYM system_time_expr { $$= true; } ; -for_system_time_expr: +system_time_expr: AS OF_SYM trans_or_timestamp simple_expr { Lex->vers_conditions.init(FOR_SYSTEM_TIME_AS_OF, $3, $4); diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy index f989e3b93ff..d7403228831 100644 --- a/sql/sql_yacc_ora.yy +++ b/sql/sql_yacc_ora.yy @@ -763,7 +763,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token PURGE %token QUARTER_SYM %token QUERY_SYM -%token QUERY_FOR_SYM /* INTERNAL */ %token QUICK %token RAISE_SYM /* Oracle-PLSQL-R */ %token RANGE_SYM /* SQL-2003-R */ -- cgit v1.2.1 From 909867d014d3fd29654a0e5bf26619c2e485b97b Mon Sep 17 00:00:00 2001 From: Eugene Kosov Date: Thu, 13 Jul 2017 18:48:30 +0300 Subject: SQL: optimized fields fix for NOT NULL [fixes #226] --- sql/field.cc | 2 +- sql/field.h | 4 ---- sql/item.cc | 38 ++++++++++++++++++++++++-------------- sql/item.h | 3 +++ sql/sql_select.cc | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 78 insertions(+), 19 deletions(-) (limited to 'sql') diff --git a/sql/field.cc b/sql/field.cc index 3a1fee59937..ac0e59773be 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -1631,7 +1631,7 @@ Field::Field(uchar *ptr_arg,uint32 length_arg,uchar *null_ptr_arg, :ptr(ptr_arg), null_ptr(null_ptr_arg), table(0), orig_table(0), table_name(0), field_name(field_name_arg), option_list(0), option_struct(0), key_start(0), part_of_key(0), - part_of_key_not_clustered(0), force_null(false), part_of_sortkey(0), + part_of_key_not_clustered(0), part_of_sortkey(0), unireg_check(unireg_check_arg), field_length(length_arg), null_bit(null_bit_arg), is_created_from_null_item(FALSE), read_stats(NULL), collected_stats(0), vcol_info(0), check_constraint(0), diff --git a/sql/field.h b/sql/field.h index 317ae018066..28d47b337a7 100644 --- a/sql/field.h +++ b/sql/field.h @@ -703,8 +703,6 @@ public: /* Field is part of the following keys */ key_map key_start, part_of_key, part_of_key_not_clustered; - bool force_null; - /* Bitmap of indexes that have records ordered by col1, ... this_field, ... @@ -1090,8 +1088,6 @@ public: virtual uint size_of() const =0; // For new field inline bool is_null(my_ptrdiff_t row_offset= 0) const { - if (force_null) - return true; /* The table may have been marked as containing only NULL values for all fields if it is a NULL-complemented row of an OUTER JOIN diff --git a/sql/item.cc b/sql/item.cc index d27ab95aaa4..0df22234f88 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -2759,20 +2759,6 @@ void Item_field::set_field(Field *field_par) fixed= 1; if (field->table->s->tmp_table == SYSTEM_TMP_TABLE) any_privileges= 0; - - field->force_null= false; - if (field->flags & VERS_OPTIMIZED_UPDATE_FLAG && context && - ((field->table->pos_in_table_list && - field->table->pos_in_table_list->vers_conditions) || - (context->select_lex && context->select_lex->vers_conditions))) - { - field->force_null= true; - push_warning_printf( - current_thd, Sql_condition::WARN_LEVEL_WARN, - ER_NON_VERSIONED_FIELD_IN_VERSIONED_QUERY, - ER_THD(current_thd, ER_NON_VERSIONED_FIELD_IN_VERSIONED_QUERY), - field_name); - } } @@ -10757,6 +10743,30 @@ bool Item_field::exclusive_dependence_on_grouping_fields_processor(void *arg) return true; } +Item *Item_field::vers_optimized_fields_transformer(THD *thd, uchar *) +{ + if (!field) + return this; + + if (field->flags & VERS_OPTIMIZED_UPDATE_FLAG && context && + ((field->table->pos_in_table_list && + field->table->pos_in_table_list->vers_conditions) || + (context->select_lex && context->select_lex->vers_conditions))) + { + push_warning_printf( + current_thd, Sql_condition::WARN_LEVEL_WARN, + ER_NON_VERSIONED_FIELD_IN_VERSIONED_QUERY, + ER_THD(current_thd, ER_NON_VERSIONED_FIELD_IN_VERSIONED_QUERY), + field_name); + + Item *null_item= new (thd->mem_root) Item_null(thd); + if (null_item) + return null_item; + } + + return this; +} + void Item::register_in(THD *thd) { next= thd->free_list; diff --git a/sql/item.h b/sql/item.h index eb695726472..ab2c9dd65a9 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1655,6 +1655,8 @@ public: virtual Item_field *field_for_view_update() { return 0; } + virtual Item *vers_optimized_fields_transformer(THD *thd, uchar *) + { return this; } virtual Item *neg_transformer(THD *thd) { return NULL; } virtual Item *update_value_transformer(THD *thd, uchar *select_arg) { return this; } @@ -2750,6 +2752,7 @@ public: uint32 max_display_length() const { return field->max_display_length(); } Item_field *field_for_view_update() { return this; } int fix_outer_field(THD *thd, Field **field, Item **reference); + virtual Item *vers_optimized_fields_transformer(THD *thd, uchar *); virtual Item *update_value_transformer(THD *thd, uchar *select_arg); Item *derived_field_transformer_for_having(THD *thd, uchar *arg); Item *derived_field_transformer_for_where(THD *thd, uchar *arg); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 2ae574398fc..a1fc9f16b21 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1380,6 +1380,46 @@ JOIN::prepare(TABLE_LIST *tables_init, if (!procedure && result && result->prepare(fields_list, unit_arg)) goto err; /* purecov: inspected */ + if (!thd->stmt_arena->is_stmt_prepare()) + { + bool have_versioned_tables= false; + for (TABLE_LIST *table= tables_list; table; table= table->next_local) + { + if (table->table && table->table->versioned()) + { + have_versioned_tables= true; + break; + } + } + + if (have_versioned_tables) + { + Item_transformer transformer= &Item::vers_optimized_fields_transformer; + + if (conds) + { + conds= conds->transform(thd, transformer, NULL); + } + + for (ORDER *ord= order; ord; ord= ord->next) + { + ord->item_ptr= (*ord->item)->transform(thd, transformer, NULL); + ord->item= &ord->item_ptr; + } + + for (ORDER *ord= group_list; ord; ord= ord->next) + { + ord->item_ptr= (*ord->item)->transform(thd, transformer, NULL); + ord->item= &ord->item_ptr; + } + + if (having) + { + having= having->transform(thd, transformer, NULL); + } + } + } + unit= unit_arg; if (prepare_stage2()) goto err; @@ -3827,6 +3867,16 @@ void JOIN::exec_inner() result->send_result_set_metadata( procedure ? procedure_fields_list : *fields, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF); + + { + List_iterator it(*columns_list); + while (Item *item= it++) + { + Item_transformer transformer= &Item::vers_optimized_fields_transformer; + it.replace(item->transform(thd, transformer, NULL)); + } + } + error= do_select(this, procedure); /* Accumulate the counts from all join iterations of all join parts. */ thd->inc_examined_row_count(join_examined_rows); -- cgit v1.2.1 From f8b625699282df07b4166809c0195760c7abb357 Mon Sep 17 00:00:00 2001 From: Eugene Kosov Date: Sat, 15 Jul 2017 13:59:15 +0300 Subject: SQL: disallow ALTER CHANGE of system fields [fixes #213] --- sql/handler.cc | 6 ++++++ sql/share/errmsg-utf8.txt | 3 +++ 2 files changed, 9 insertions(+) (limited to 'sql') diff --git a/sql/handler.cc b/sql/handler.cc index ae685482219..9f4ab646aaa 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -6869,6 +6869,12 @@ bool Vers_parse_info::check_and_fix_alter(THD *thd, Alter_info *alter_info, { if (f->versioning == Column_definition::WITHOUT_VERSIONING) f->flags|= VERS_OPTIMIZED_UPDATE_FLAG; + + if (f->change && (!strcmp(f->change, start) || !strcmp(f->change, end))) + { + my_error(ER_VERS_ALTER_SYSTEM_FIELD, MYF(0), f->change); + return true; + } } } diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index bbe4e6cff0e..1a386c4f16c 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7591,3 +7591,6 @@ ER_VERS_HISTORY_LOCK ER_WRONG_TABLESPACE_NAME 42000 eng "Incorrect tablespace name `%-.192s`" + +ER_VERS_ALTER_SYSTEM_FIELD + eng "Can not change system versioning field '%s'" -- cgit v1.2.1 From a5ec9fc1b409c22b0d9a18999525c5b234c28113 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Fri, 21 Jul 2017 17:52:47 +0300 Subject: Style: mysqld.h comments --- sql/mysqld.h | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'sql') diff --git a/sql/mysqld.h b/sql/mysqld.h index e8a72b0b063..5ca5570460e 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -176,6 +176,7 @@ extern char *opt_backup_history_logname, *opt_backup_progress_logname, extern const char *log_output_str; extern const char *log_backup_output_str; +/* System Versioning begin */ enum vers_range_type_t { FOR_SYSTEM_TIME_UNSPECIFIED = 0, @@ -186,8 +187,11 @@ enum vers_range_type_t FOR_SYSTEM_TIME_BEFORE }; +/* Used only for @@versioning_current_time sysvar. This struct must be POD + * because of str_value, which is used as interface to user. + * So no virtual-anything! */ struct st_vers_current_time -{ // This struct must be POD, so no virtual-anything! +{ char *str_value; // must be first vers_range_type_t type; MYSQL_TIME ltime; @@ -197,12 +201,15 @@ struct st_vers_current_time {} }; -enum vers_hide_enum { +enum vers_hide_enum +{ VERS_HIDE_AUTO= 0, VERS_HIDE_IMPLICIT, VERS_HIDE_FULL, VERS_HIDE_NEVER }; +/* System Versioning end */ + extern char *mysql_home_ptr, *pidfile_name_ptr; extern MYSQL_PLUGIN_IMPORT char glob_hostname[FN_REFLEN]; extern char mysql_home[FN_REFLEN]; -- cgit v1.2.1 From aa292666cc3e25e267b5d69e80f30596f84f352d Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Sun, 23 Jul 2017 17:07:56 +0300 Subject: Parser: moved 'for system_time' before alias Due to standard (see 7.6
). --- sql/sql_yacc.yy | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'sql') diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 27705f6bd41..4cab239795d 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -11334,9 +11334,9 @@ table_primary_ident: SELECT_LEX *sel= Select; sel->table_join_options= 0; } - table_ident opt_use_partition opt_table_alias opt_key_definition opt_for_system_time_clause + table_ident opt_use_partition opt_for_system_time_clause opt_table_alias opt_key_definition { - if (!($$= Select->add_table_to_list(thd, $2, $4, + if (!($$= Select->add_table_to_list(thd, $2, $5, Select->get_table_join_options(), YYPS->m_lock_type, YYPS->m_mdl_type, @@ -11344,7 +11344,7 @@ table_primary_ident: $3))) MYSQL_YYABORT; Select->add_joined_table($$); - if ($6) + if ($4) $$->vers_conditions= Lex->vers_conditions; } ; -- cgit v1.2.1 From 88454b3320792a02b6d683e213e414d65fc6df8c Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Mon, 24 Jul 2017 16:22:59 +0300 Subject: SQL: comment and check about ALTER in update_auto_increment() --- sql/handler.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/handler.cc b/sql/handler.cc index 9f4ab646aaa..7e7f161f14f 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -3029,7 +3029,8 @@ int handler::update_auto_increment() enum enum_check_fields save_count_cuted_fields; DBUG_ENTER("handler::update_auto_increment"); - if (table->versioned_by_sql()) + // System Versioning: handle ALTER ADD COLUMN AUTO_INCREMENT + if (thd->lex->sql_command == SQLCOM_ALTER_TABLE && table->versioned_by_sql()) { Field *end= table->vers_end_field(); DBUG_ASSERT(end); -- cgit v1.2.1 From d998da03069dca1aae83ec6af7eb407bbdc9fc58 Mon Sep 17 00:00:00 2001 From: Eugene Kosov Date: Mon, 31 Jul 2017 11:42:48 +0300 Subject: SQL: replication fixes [fixes #234] --- sql/handler.cc | 2 ++ sql/log_event.cc | 56 ++++++++++++++++++++++++++++++++++++++++++++++++------- sql/log_event.h | 5 +++++ sql/sql_class.h | 18 ++++++++++++++++++ sql/sql_delete.cc | 10 ++-------- sql/sql_insert.cc | 6 ++++-- sql/sql_update.cc | 21 ++++++++++++++++++--- 7 files changed, 98 insertions(+), 20 deletions(-) (limited to 'sql') diff --git a/sql/handler.cc b/sql/handler.cc index 7e7f161f14f..3adf4969f73 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -5697,6 +5697,8 @@ bool ha_show_status(THD *thd, handlerton *db_type, enum ha_stat_type stat) bool handler::check_table_binlog_row_based(bool binlog_row) { + if (table->versioned_by_engine()) + return false; if (unlikely((table->in_use->variables.sql_log_bin_off))) return 0; /* Called by partitioning engine */ if (unlikely((!check_table_binlog_row_based_done))) diff --git a/sql/log_event.cc b/sql/log_event.cc index 4a48ebce077..1bedaf152f3 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -44,6 +44,7 @@ #include #include "compat56.h" #include "wsrep_mysqld.h" +#include "sql_insert.h" #endif /* MYSQL_CLIENT */ #include @@ -12509,6 +12510,22 @@ Rows_log_event::write_row(rpl_group_info *rgi, DBUG_RETURN(HA_ERR_GENERIC); // in case if error is not set yet } + // Handle INSERT. + // Set vers fields when replicating from not system-versioned table. + if (m_type == WRITE_ROWS_EVENT_V1 && table->versioned_by_sql()) + { + bitmap_set_bit(table->read_set, table->vers_start_field()->field_index); + // Check whether a row came from unversioned table and fix vers fields. + if (table->vers_start_field()->get_timestamp() == 0) + { + bitmap_set_bit(table->write_set, table->vers_start_field()->field_index); + bitmap_set_bit(table->write_set, table->vers_end_field()->field_index); + thd->set_current_time(); + table->vers_start_field()->set_time(); + table->vers_end_field()->set_max(); + } + } + /* Try to write record. If a corresponding record already exists in the table, we try to change it using ha_update_row() if possible. Otherwise we delete @@ -12799,7 +12816,7 @@ static bool record_compare(TABLE *table) /* Compare fields */ for (Field **ptr=table->field ; *ptr ; ptr++) { - if (table->versioned_by_engine() && *ptr == table->vers_start_field()) + if (table->versioned() && (*ptr)->vers_sys_field()) { continue; } @@ -12997,19 +13014,19 @@ int Rows_log_event::find_row(rpl_group_info *rgi) prepare_record(table, m_width, FALSE); error= unpack_current_row(rgi); - + m_vers_from_plain= false; if (table->versioned()) { Field *sys_trx_end= table->vers_end_field(); DBUG_ASSERT(table->read_set); bitmap_set_bit(table->read_set, sys_trx_end->field_index); - // master table is unversioned + // check whether master table is unversioned if (sys_trx_end->val_int() == 0) { - DBUG_ASSERT(table->write_set); - bitmap_set_bit(table->write_set, sys_trx_end->field_index); - sys_trx_end->set_max(); table->vers_start_field()->set_notnull(); + bitmap_set_bit(table->write_set, sys_trx_end->field_index); + table->vers_end_field()->set_max(); + m_vers_from_plain= true; } } @@ -13395,7 +13412,19 @@ int Delete_rows_log_event::do_exec_row(rpl_group_info *rgi) if (!error) { m_table->mark_columns_per_binlog_row_image(); - error= m_table->file->ha_delete_row(m_table->record[0]); + if (m_vers_from_plain && m_table->versioned_by_sql()) + { + Field *end= m_table->vers_end_field(); + bitmap_set_bit(m_table->write_set, end->field_index); + store_record(m_table, record[1]); + end->set_time(); + error= m_table->file->ha_update_row(m_table->record[1], + m_table->record[0]); + } + else + { + error= m_table->file->ha_delete_row(m_table->record[0]); + } m_table->default_column_bitmaps(); } if (invoke_triggers && !error && @@ -13652,9 +13681,22 @@ Update_rows_log_event::do_exec_row(rpl_group_info *rgi) memcpy(m_table->write_set->bitmap, m_cols_ai.bitmap, (m_table->write_set->n_bits + 7) / 8); m_table->mark_columns_per_binlog_row_image(); + if (m_vers_from_plain && m_table->versioned_by_sql()) + { + bitmap_set_bit(m_table->write_set, + m_table->vers_start_field()->field_index); + thd->set_current_time(); + m_table->vers_start_field()->set_time(); + } error= m_table->file->ha_update_row(m_table->record[1], m_table->record[0]); if (error == HA_ERR_RECORD_IS_THE_SAME) error= 0; + if (m_vers_from_plain && m_table->versioned_by_sql()) + { + store_record(m_table, record[2]); + error= vers_insert_history_row(m_table); + restore_record(m_table, record[2]); + } m_table->default_column_bitmaps(); if (invoke_triggers && !error && diff --git a/sql/log_event.h b/sql/log_event.h index e45151c8564..6a9ebae3a51 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -4588,6 +4588,8 @@ protected: uchar *m_extra_row_data; /* Pointer to extra row data if any */ /* If non null, first byte is length */ + bool m_vers_from_plain; + /* helper functions */ @@ -4737,6 +4739,7 @@ public: __attribute__((unused)), const uchar *after_record) { + DBUG_ASSERT(!table->versioned_by_engine()); return thd->binlog_write_row(table, is_transactional, after_record); } #endif @@ -4818,6 +4821,7 @@ public: const uchar *before_record, const uchar *after_record) { + DBUG_ASSERT(!table->versioned_by_engine()); return thd->binlog_update_row(table, is_transactional, before_record, after_record); } @@ -4907,6 +4911,7 @@ public: const uchar *after_record __attribute__((unused))) { + DBUG_ASSERT(!table->versioned_by_engine()); return thd->binlog_delete_row(table, is_transactional, before_record); } diff --git a/sql/sql_class.h b/sql/sql_class.h index d024a27c2cd..0c7179e5569 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -6052,6 +6052,24 @@ public: } }; +class ScopedStatementReplication +{ +public: + ScopedStatementReplication(THD *thd) : thd(thd) + { + if (thd) + saved_binlog_format= thd->set_current_stmt_binlog_format_stmt(); + } + ~ScopedStatementReplication() + { + if (thd) + thd->restore_stmt_binlog_format(saved_binlog_format); + } + +private: + enum_binlog_format saved_binlog_format; + THD *thd; +}; #endif /* MYSQL_SERVER */ #endif /* SQL_CLASS_INCLUDED */ diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 27e112c3285..62171ae2423 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -270,14 +270,6 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, TABLE *table= table_list->table; DBUG_ASSERT(table); - if (table->versioned_by_engine() && - table->file->check_table_binlog_row_based(1)) - { - my_error(ER_VERS_NOT_ALLOWED, MYF(0), - "TRUNCATE FOR SYSTEM_TIME with row-based replication"); - DBUG_RETURN(TRUE); - } - DBUG_ASSERT(!conds); if (vers_setup_select(thd, table_list, &conds, select_lex)) DBUG_RETURN(TRUE); @@ -724,6 +716,8 @@ cleanup: else errcode= query_error_code(thd, killed_status == NOT_KILLED); + ScopedStatementReplication scoped_stmt_rpl( + table->versioned_by_engine() ? thd : NULL); /* [binlog]: If 'handler::delete_all_rows()' was called and the storage engine does not inject the rows itself, we replicate diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index b0e1d06365f..7c916efa3d4 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1137,8 +1137,10 @@ values_loop_end: } else errcode= query_error_code(thd, thd->killed == NOT_KILLED); - - /* bug#22725: + + ScopedStatementReplication scoped_stmt_rpl( + table->versioned_by_engine() ? thd : NULL); + /* bug#22725: A query which per-row-loop can not be interrupted with KILLED, like INSERT, and that does not invoke stored diff --git a/sql/sql_update.cc b/sql/sql_update.cc index b5f4d593a10..80a036ae18b 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -1036,6 +1036,9 @@ int mysql_update(THD *thd, else errcode= query_error_code(thd, killed_status == NOT_KILLED); + ScopedStatementReplication scoped_stmt_rpl( + table->versioned_by_engine() ? thd : NULL); + if (thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query(), thd->query_length(), transactional_table, FALSE, FALSE, errcode)) @@ -2674,9 +2677,21 @@ bool multi_update::send_eof() thd->clear_error(); else errcode= query_error_code(thd, killed_status == NOT_KILLED); - if (thd->binlog_query(THD::ROW_QUERY_TYPE, - thd->query(), thd->query_length(), - transactional_tables, FALSE, FALSE, errcode)) + + bool force_stmt= false; + for (TABLE *table= all_tables->table; table; table= table->next) + { + if (table->versioned_by_engine()) + { + force_stmt= true; + break; + } + } + ScopedStatementReplication scoped_stmt_rpl(force_stmt ? thd : NULL); + + if (thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query(), + thd->query_length(), transactional_tables, FALSE, + FALSE, errcode)) { local_error= 1; // Rollback update } -- cgit v1.2.1 From c2c8808a16e561bb43ef448fa1b47ae5e32a0f40 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Thu, 3 Aug 2017 10:11:49 +0300 Subject: SQL: compare TRX_ID fields against timestamps [closes #231] --- sql/field.cc | 58 ++++++++++++++++++++++++++++++++++++++++++----- sql/field.h | 30 +++++++++++++++++++++--- sql/item.cc | 15 +++++++++--- sql/item.h | 12 ++++++++++ sql/item_cmpfunc.cc | 52 ++++++++++++++++++++++++++++++++++++++++++ sql/item_cmpfunc.h | 1 + sql/item_vers.cc | 19 ++++++++++++---- sql/share/errmsg-utf8.txt | 3 +++ sql/sql_base.cc | 1 + sql/sql_select.cc | 20 +--------------- sql/table.cc | 57 ++++++++++++++++++++++++++-------------------- sql/unireg.cc | 3 ++- 12 files changed, 209 insertions(+), 62 deletions(-) (limited to 'sql') diff --git a/sql/field.cc b/sql/field.cc index ac0e59773be..cd83e61ba43 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -1999,6 +1999,40 @@ bool Field_num::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) } +bool Field_vers_system::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate, ulonglong trx_id) +{ + ASSERT_COLUMN_MARKED_FOR_READ; + DBUG_ASSERT(ltime); + if (!table || !table->s) + return true; + DBUG_ASSERT(table->versioned_by_engine() || + (table->versioned() && table->s->table_category == TABLE_CATEGORY_TEMPORARY)); + if (!trx_id) + return true; + if (trx_id == ULONGLONG_MAX) + { + get_thd()->variables.time_zone->gmt_sec_to_TIME(ltime, TIMESTAMP_MAX_VALUE); + return false; + } + if (cached == trx_id) + { + *ltime= cache; + return false; + } + handlerton *hton= table->file->partition_ht(); + DBUG_ASSERT(hton); + DBUG_ASSERT(hton->vers_query_trx_id); + bool found= hton->vers_query_trx_id(get_thd(), &cache, trx_id, VTQ_COMMIT_TS); + if (found) + { + *ltime= cache; + cached= trx_id; + return false; + } + return true; +} + + Field_str::Field_str(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, utype unireg_check_arg, const char *field_name_arg, CHARSET_INFO *charset_arg) @@ -10373,7 +10407,8 @@ Field *make_field(TABLE_SHARE *share, Field::geometry_type geom_type, uint srid, Field::utype unireg_check, TYPELIB *interval, - const char *field_name) + const char *field_name, + uint32 flags) { uchar *UNINIT_VAR(bit_ptr); uchar UNINIT_VAR(bit_offset); @@ -10527,11 +10562,22 @@ Field *make_field(TABLE_SHARE *share, f_is_zerofill(pack_flag) != 0, f_is_dec(pack_flag) == 0); case MYSQL_TYPE_LONGLONG: - return new (mem_root) - Field_longlong(ptr,field_length,null_pos,null_bit, - unireg_check, field_name, - f_is_zerofill(pack_flag) != 0, - f_is_dec(pack_flag) == 0); + if (flags & (VERS_SYS_START_FLAG|VERS_SYS_END_FLAG)) + { + return new (mem_root) + Field_vers_system(ptr, field_length, null_pos, null_bit, + unireg_check, field_name, + f_is_zerofill(pack_flag) != 0, + f_is_dec(pack_flag) == 0); + } + else + { + return new (mem_root) + Field_longlong(ptr,field_length,null_pos,null_bit, + unireg_check, field_name, + f_is_zerofill(pack_flag) != 0, + f_is_dec(pack_flag) == 0); + } case MYSQL_TYPE_TIMESTAMP: { uint dec= field_length > MAX_DATETIME_WIDTH ? diff --git a/sql/field.h b/sql/field.h index 28d47b337a7..efd3fe18e6f 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1402,7 +1402,7 @@ public: FIELD_FLAGS_COLUMN_FORMAT; } - bool vers_sys_field() + bool vers_sys_field() const { return flags & (VERS_SYS_START_FLAG | VERS_SYS_END_FLAG); } @@ -2130,6 +2130,29 @@ public: }; +class Field_vers_system :public Field_longlong { + MYSQL_TIME cache; + ulonglong cached; +public: + Field_vers_system(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, + uchar null_bit_arg, + enum utype unireg_check_arg, const char *field_name_arg, + bool zero_arg, bool unsigned_arg) + :Field_longlong(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg, zero_arg,unsigned_arg), + cached(0) + {} + enum_field_types real_type() const { return MYSQL_TYPE_LONGLONG; } + enum_field_types type() const { return MYSQL_TYPE_DATETIME;} + uint size_of() const { return sizeof(*this); } + bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate, ulonglong trx_id); + bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + { + return get_date(ltime, fuzzydate, (ulonglong) val_int()); + } +}; + + class Field_float :public Field_real { public: Field_float(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, @@ -3778,7 +3801,8 @@ Field *make_field(TABLE_SHARE *share, MEM_ROOT *mem_root, CHARSET_INFO *cs, Field::geometry_type geom_type, uint srid, Field::utype unireg_check, - TYPELIB *interval, const char *field_name); + TYPELIB *interval, const char *field_name, + uint32 flags); /* Create field class for CREATE TABLE @@ -3965,7 +3989,7 @@ public: (uint32)length, null_pos, null_bit, pack_flag, sql_type, charset, geom_type, srid, unireg_check, interval, - field_name_arg); + field_name_arg, flags); } Field *make_field(TABLE_SHARE *share, MEM_ROOT *mem_root, const char *field_name_arg) diff --git a/sql/item.cc b/sql/item.cc index 0df22234f88..117662d9c79 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -6325,9 +6325,18 @@ Field *Item::tmp_table_field_from_field_type(TABLE *table, break; #ifdef HAVE_LONG_LONG case MYSQL_TYPE_LONGLONG: - field= new (mem_root) - Field_longlong((uchar*) 0, max_length, null_ptr, 0, Field::NONE, - name, 0, unsigned_flag); + if (field_flags() & (VERS_SYS_START_FLAG|VERS_SYS_END_FLAG)) + { + field= new (mem_root) + Field_vers_system((uchar*) 0, max_length, null_ptr, 0, Field::NONE, + name, 0, unsigned_flag); + } + else + { + field= new (mem_root) + Field_longlong((uchar*) 0, max_length, null_ptr, 0, Field::NONE, + name, 0, unsigned_flag); + } break; #endif case MYSQL_TYPE_FLOAT: diff --git a/sql/item.h b/sql/item.h index ab2c9dd65a9..130ad688593 100644 --- a/sql/item.h +++ b/sql/item.h @@ -747,6 +747,10 @@ public: virtual bool send(Protocol *protocol, String *str); virtual bool eq(const Item *, bool binary_cmp) const; virtual enum_field_types field_type() const= 0; + virtual uint field_flags() const + { + return 0; + } virtual const Type_handler *type_handler() const { return Type_handler::get_handler_by_field_type(field_type()); @@ -2669,6 +2673,10 @@ public: { return field->type(); } + uint32 field_flags() const + { + return field->flags; + } const Type_handler *real_type_handler() const; enum_monotonicity_info get_monotonicity_info() const { @@ -5898,6 +5906,10 @@ public: Item* get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; } uint flags; + uint32 field_flags() const + { + return flags; + } }; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index f5ba6f206c2..147bb270e37 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -5218,6 +5218,58 @@ bool fix_escape_item(THD *thd, Item *escape_item, String *tmp_str, return FALSE; } +bool Item_bool_func2::fix_fields(THD* thd, Item** ref) +{ + if (Item_bool_func::fix_fields(thd, ref)) + return true; + + // System Versioning: convert TRX_ID to DATETIME + Item *trx_id= NULL; + int arg_idx= -1; + Field_vers_system *sys_field= NULL; + DBUG_ASSERT(arg_count == 2); + // find trx_id and sys_field + for (int i= 0; i < 2; ++i) + { + Item *arg= args[i]; + if (arg->result_type() != INT_RESULT) + continue; + DBUG_ASSERT(arg); + if (arg->type() == Item::FIELD_ITEM) + { + Field *f= static_cast(arg)->field; + DBUG_ASSERT(f); + if (f->vers_sys_field() && f->real_type() == MYSQL_TYPE_LONGLONG) + { + if (sys_field) + return false; + sys_field= static_cast(f); + continue; + } + } + if (trx_id) + return false; + trx_id= arg; + arg_idx= i; + } + if (!trx_id || !sys_field) + return false; + MYSQL_TIME ltime; + ulonglong trx_id_val= (ulonglong) trx_id->val_int(); + if (sys_field->get_date(<ime, false, trx_id_val)) + { + my_error(ER_VERS_NO_TRX_ID, MYF(0), trx_id_val); + return true; + } + Query_arena_stmt on_stmt_arena(thd); + Item *datetime= new (thd->mem_root) Item_datetime_literal(thd, <ime, 6); + if (!datetime) + return true; + DBUG_ASSERT(arg_idx > -1); + args[arg_idx]= datetime; + return false; +} + bool Item_func_like::fix_fields(THD *thd, Item **ref) { diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index a94105b352d..6eb1b34e568 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -410,6 +410,7 @@ public: ftree= Item_func::get_mm_tree(param, cond_ptr); DBUG_RETURN(ftree); } + bool fix_fields(THD *thd, Item **ref); }; diff --git a/sql/item_vers.cc b/sql/item_vers.cc index 3562cc7fbc9..47e62d9ebf0 100644 --- a/sql/item_vers.cc +++ b/sql/item_vers.cc @@ -54,6 +54,14 @@ Item_func_vtq_ts::get_date(MYSQL_TIME *res, ulonglong fuzzy_date) { THD *thd= current_thd; // can it differ from constructor's? DBUG_ASSERT(thd); + DBUG_ASSERT(args[0]); + if (args[0]->result_type() != INT_RESULT) + { + my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0), + args[0]->type_handler()->name().ptr(), + func_name()); + return true; + } ulonglong trx_id= args[0]->val_uint(); if (trx_id == ULONGLONG_MAX) { @@ -62,13 +70,14 @@ Item_func_vtq_ts::get_date(MYSQL_TIME *res, ulonglong fuzzy_date) return false; } - if (!hton) - return true; - - DBUG_ASSERT(hton->vers_query_trx_id); + DBUG_ASSERT(hton && hton->vers_query_trx_id); null_value= !hton->vers_query_trx_id(thd, res, trx_id, vtq_field); + if (null_value) + { + my_error(ER_VERS_NO_TRX_ID, MYF(0), trx_id); + } - return false; + return null_value; } diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 1a386c4f16c..bae32e4d247 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7589,6 +7589,9 @@ ER_VERS_WRONG_PARTS ER_VERS_HISTORY_LOCK eng "Versioned SELECT write-locking of history rows" +ER_VERS_NO_TRX_ID + eng "TRX_ID %lu not found in VTQ" + ER_WRONG_TABLESPACE_NAME 42000 eng "Incorrect tablespace name `%-.192s`" diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 238fa3a0fa4..0a7ad7aef55 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -5704,6 +5704,7 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list, if (field_to_set) { TABLE *table= field_to_set->table; + DBUG_ASSERT(table); if (thd->mark_used_columns == MARK_COLUMNS_READ) bitmap_set_bit(table->read_set, field_to_set->field_index); else diff --git a/sql/sql_select.cc b/sql/sql_select.cc index a1fc9f16b21..8575755c267 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -877,24 +877,6 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, DBUG_RETURN(-1); } } - else if (thd->variables.vers_innodb_algorithm_simple && - vers_conditions.unit == UNIT_TIMESTAMP && - vers_conditions.type != FOR_SYSTEM_TIME_UNSPECIFIED) - { - DBUG_ASSERT(table->table->s && table->table->s->db_plugin); - handlerton *hton= plugin_hton(table->table->s->db_plugin); - DBUG_ASSERT(hton); - row_start= newx Item_func_vtq_ts( - thd, - hton, - row_start, - VTQ_COMMIT_TS); - row_end= newx Item_func_vtq_ts( - thd, - hton, - row_end, - VTQ_COMMIT_TS); - } Item *cond1= 0, *cond2= 0, *curr= 0; // Temporary tables of can be created from INNODB tables and thus will @@ -907,7 +889,7 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, switch (vers_conditions.type) { case FOR_SYSTEM_TIME_UNSPECIFIED: - if (table->table->versioned_by_sql() && !tmp_from_ib) + if (!tmp_from_ib) { MYSQL_TIME max_time; thd->variables.time_zone->gmt_sec_to_TIME(&max_time, TIMESTAMP_MAX_VALUE); diff --git a/sql/table.cc b/sql/table.cc index b2a511d89f4..2dcec49d46d 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -1769,6 +1769,27 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, strpos, vcol_screen_pos); } + /* Set system versioning information. */ + if (system_period == NULL) + { + versioned= false; + row_start_field = 0; + row_end_field = 0; + } + else + { + DBUG_PRINT("info", ("Setting system versioning informations")); + uint16 row_start= uint2korr(system_period); + uint16 row_end= uint2korr(system_period + sizeof(uint16)); + if (row_start >= share->fields || row_end >= share->fields) + goto err; + DBUG_PRINT("info", ("Columns with system versioning: [%d, %d]", row_start, row_end)); + versioned= true; + vers_init(); + row_start_field= row_start; + row_end_field= row_end; + } // if (system_period == NULL) + for (i=0 ; i < share->fields; i++, strpos+=field_pack_length, field_ptr++) { uint pack_flag, interval_nr, unireg_type, recpos, field_length; @@ -1781,6 +1802,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, Virtual_column_info *vcol_info= 0; uint gis_length, gis_decimals, srid= 0; Field::utype unireg_check; + uint32 flags= 0; if (new_frm_ver >= 3) { @@ -1988,6 +2010,14 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, swap_variables(uint, null_bit_pos, mysql57_vcol_null_bit_pos); } + if (versioned) + { + if (i == row_start_field) + flags|= VERS_SYS_START_FLAG; + else if (i == row_end_field) + flags|= VERS_SYS_END_FLAG; + } + /* Convert pre-10.2.2 timestamps to use Field::default_value */ unireg_check= (Field::utype) MTYP_TYPENR(unireg_type); *field_ptr= reg_field= @@ -1995,7 +2025,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, null_pos, null_bit_pos, pack_flag, field_type, charset, geom_type, srid, unireg_check, (interval_nr ? share->intervals+interval_nr-1 : NULL), - share->fieldnames.type_names[i]); + share->fieldnames.type_names[i], flags); if (!reg_field) // Not supported field type goto err; @@ -2010,6 +2040,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, reg_field->field_index= i; reg_field->comment=comment; reg_field->vcol_info= vcol_info; + reg_field->flags|= flags; if (extra2_field_flags) { uchar flags= *extra2_field_flags++; @@ -2555,30 +2586,6 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, #endif share->db_plugin= se_plugin; - - /* Set system versioning information. */ - if (system_period == NULL) - { - versioned= false; - row_start_field = 0; - row_end_field = 0; - } - else - { - DBUG_PRINT("info", ("Setting system versioning informations")); - uint16 row_start= uint2korr(system_period); - uint16 row_end= uint2korr(system_period + sizeof(uint16)); - if (row_start >= share->fields || row_end >= share->fields) - goto err; - DBUG_PRINT("info", ("Columns with system versioning: [%d, %d]", row_start, row_end)); - versioned= true; - vers_init(); - row_start_field= row_start; - row_end_field= row_end; - vers_start_field()->flags|= VERS_SYS_START_FLAG; - vers_end_field()->flags|= VERS_SYS_END_FLAG; - } // if (system_period == NULL) - delete handler_file; share->error= OPEN_FRM_OK; diff --git a/sql/unireg.cc b/sql/unireg.cc index b647a284b14..c4b4a784ccc 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -1050,7 +1050,8 @@ static bool make_empty_rec(THD *thd, uchar *buff, uint table_options, field->unireg_check, field->save_interval ? field->save_interval : field->interval, - field->field_name); + field->field_name, + field->flags); if (!regfield) { error= 1; -- cgit v1.2.1 From 5ce6044b1c1cd501695b4d2d6cebf3876691d751 Mon Sep 17 00:00:00 2001 From: Eugene Kosov Date: Thu, 3 Aug 2017 15:04:15 +0300 Subject: SQL: remove versioning only when hidden system fields [closes #211] --- sql/handler.cc | 60 +++++++++++++++++++++++++++++++++++++++-------- sql/share/errmsg-utf8.txt | 3 +++ 2 files changed, 53 insertions(+), 10 deletions(-) (limited to 'sql') diff --git a/sql/handler.cc b/sql/handler.cc index 3adf4969f73..7e540b59d08 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -6603,15 +6603,12 @@ bool Vers_parse_info::is_trx_end(const Create_field &f) const return f.flags & VERS_SYS_END_FLAG; } - -static bool vers_create_sys_field(THD *thd, const char *field_name, - Alter_info *alter_info, - int flags, - bool integer_fields) +static Create_field *vers_init_sys_field(THD *thd, const char *field_name, + int flags, bool integer_fields) { Create_field *f= new (thd->mem_root) Create_field(); if (!f) - return true; + return NULL; memset(f, 0, sizeof(*f)); f->field_name= field_name; @@ -6630,10 +6627,38 @@ static bool vers_create_sys_field(THD *thd, const char *field_name, } if (f->check(thd)) + return NULL; + + return f; +} + +static bool vers_create_sys_field(THD *thd, const char *field_name, + Alter_info *alter_info, int flags, + bool integer_fields) +{ + Create_field *f= vers_init_sys_field(thd, field_name, flags, integer_fields); + if (!f) return true; - alter_info->create_list.push_back(f); alter_info->flags|= Alter_info::ALTER_ADD_COLUMN; + alter_info->create_list.push_back(f); + + return false; +} + +static bool vers_change_sys_field(THD *thd, const char *field_name, + Alter_info *alter_info, int flags, + bool integer_fields, const char *change) +{ + Create_field *f= vers_init_sys_field(thd, field_name, flags, integer_fields); + if (!f) + return true; + + f->change= change; + + alter_info->flags|= Alter_info::ALTER_CHANGE_COLUMN; + alter_info->create_list.push_back(f); + return false; } @@ -6814,6 +6839,7 @@ static bool add_field_to_drop_list(THD *thd, Alter_info *alter_info, { DBUG_ASSERT(field); DBUG_ASSERT(field->field_name); + alter_info->flags|= Alter_info::ALTER_DROP_COLUMN; Alter_drop *ad= new (thd->mem_root) Alter_drop(Alter_drop::COLUMN, field->field_name, false); return !ad || alter_info->drop_list.push_back(ad, thd->mem_root); @@ -6838,11 +6864,23 @@ bool Vers_parse_info::check_and_fix_alter(THD *thd, Alter_info *alter_info, return true; } + if (!(share->vers_start_field()->flags & HIDDEN_FLAG)) + { + my_error(ER_VERS_SYS_FIELD_NOT_HIDDEN, MYF(0), + share->vers_start_field()->field_name); + return true; + } + if (!(share->vers_end_field()->flags & HIDDEN_FLAG)) + { + my_error(ER_VERS_SYS_FIELD_NOT_HIDDEN, MYF(0), + share->vers_end_field()->field_name); + return true; + } + if (add_field_to_drop_list(thd, alter_info, share->vers_start_field()) || add_field_to_drop_list(thd, alter_info, share->vers_end_field())) return true; - alter_info->flags|= Alter_info::ALTER_DROP_COLUMN; return false; } @@ -6908,14 +6946,16 @@ bool Vers_parse_info::check_and_fix_alter(THD *thd, Alter_info *alter_info, return true; } - if (vers_create_sys_field(thd, name, alter_info, + if (vers_change_sys_field(thd, name, alter_info, f->flags & (VERS_SYS_START_FLAG | VERS_SYS_END_FLAG), - integer_fields)) + integer_fields, name)) { return true; } + it.remove(); + if (done_start && done_end) break; } diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index bae32e4d247..9265362a2b4 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7597,3 +7597,6 @@ ER_WRONG_TABLESPACE_NAME 42000 ER_VERS_ALTER_SYSTEM_FIELD eng "Can not change system versioning field '%s'" + +ER_VERS_SYS_FIELD_NOT_HIDDEN + eng "System versioning field '%s' is not hidden" -- cgit v1.2.1 From 53370de103a58bff2d6c566f411e58d70bd5d446 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Fri, 4 Aug 2017 17:06:33 +0300 Subject: IB: partition-wise ha_innopart::rnd_init() [fixes #208] --- sql/partition_info.cc | 1 + sql/partitioning/partition_handler.cc | 1 + sql/partitioning/partition_handler.h | 7 +++++++ 3 files changed, 9 insertions(+) (limited to 'sql') diff --git a/sql/partition_info.cc b/sql/partition_info.cc index dd4ec54673a..86fb4b5bf63 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -1024,6 +1024,7 @@ bool partition_info::vers_scan_min_max(THD *thd, partition_element *part) for (; part_id < part_id_end; ++part_id) { handler *file= table->file->part_handler(part_id); // requires update_partition() for ha_innopart + DBUG_ASSERT(file); int rc= file->ha_external_lock(thd, F_RDLCK); // requires ha_commit_trans() for ha_innobase if (rc) { diff --git a/sql/partitioning/partition_handler.cc b/sql/partitioning/partition_handler.cc index 1e04439e100..ae395a71a42 100644 --- a/sql/partitioning/partition_handler.cc +++ b/sql/partitioning/partition_handler.cc @@ -286,6 +286,7 @@ Partition_helper::Partition_helper(handler *main_handler) m_part_info(), m_tot_parts(), m_last_part(), + m_cur_part(NO_CURRENT_PART_ID), m_err_rec(), m_ordered(), m_ordered_scan_ongoing(), diff --git a/sql/partitioning/partition_handler.h b/sql/partitioning/partition_handler.h index cf4e1dcb24b..f4436be4262 100644 --- a/sql/partitioning/partition_handler.h +++ b/sql/partitioning/partition_handler.h @@ -1061,6 +1061,13 @@ protected: /** Total number of partitions. */ uint m_tot_parts; uint m_last_part; // Last accessed partition. + uint m_cur_part; // Current partition + /* In fact m_cur_part duplicates m_part_spec, but is required due to different + rnd_init() semantics between ha_innopart and ha_partition. + ha_innopart::rnd_init() is table-wise (it has separate *_in_part() methods). + ha_partition::rnd_init() is partition-wise. m_cur_part is used to imitate + partition-wise rnd_init() and should not be merged with m_part_spec because of + different usage scenarios. */ const uchar *m_err_rec; // record which gave error. bool m_auto_increment_safe_stmt_log_lock; bool m_auto_increment_lock; -- cgit v1.2.1 From d3d2ea9fd59a836dd0b0577658c5961712a5ed9d Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 8 Aug 2017 17:12:16 +0300 Subject: SQL, Parser: system_time logic and syntax fixes [closes #237] --- sql/item.cc | 5 +- sql/share/errmsg-utf8.txt | 12 ---- sql/sql_base.cc | 4 +- sql/sql_delete.cc | 9 ++- sql/sql_derived.cc | 16 ++--- sql/sql_lex.cc | 4 +- sql/sql_lex.h | 5 +- sql/sql_select.cc | 147 +++++++++++++++++++++++------------------- sql/sql_truncate.cc | 13 ++-- sql/sql_view.cc | 8 ++- sql/sql_yacc.yy | 161 ++++++++++++++++++++-------------------------- sql/table.cc | 18 ++++++ sql/table.h | 19 +++--- 13 files changed, 212 insertions(+), 209 deletions(-) (limited to 'sql') diff --git a/sql/item.cc b/sql/item.cc index 117662d9c79..4314ee036e4 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -10758,9 +10758,8 @@ Item *Item_field::vers_optimized_fields_transformer(THD *thd, uchar *) return this; if (field->flags & VERS_OPTIMIZED_UPDATE_FLAG && context && - ((field->table->pos_in_table_list && - field->table->pos_in_table_list->vers_conditions) || - (context->select_lex && context->select_lex->vers_conditions))) + field->table->pos_in_table_list && + field->table->pos_in_table_list->vers_conditions) { push_warning_printf( current_thd, Sql_condition::WARN_LEVEL_WARN, diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 9265362a2b4..c551bcef809 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7550,18 +7550,6 @@ WARN_VERS_ALIAS_TOO_LONG ER_VERS_VTMD_ERROR eng "VTMD error: %s" -ER_MULTIPLE_CLAUSE - eng "for %`s: multiple '%s'" - -ER_MULTIPLE_CLAUSE_FOR - eng "for %`s: multiple '%s' for %`s" - -ER_MULTIPLE_CLAUSE_2 - eng "for %`s: multiple '%s' (%`s, %`s)" - -ER_MULTIPLE_IDENTIFIER - eng "for %`s: multiple %`s for '%s'" - ER_NOT_ALLOWED eng "for %`s: not allowed '%s'" diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 0a7ad7aef55..27bf5ab6931 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -7593,9 +7593,7 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, TABLE *table= f->field->table; DBUG_ASSERT(table && table->pos_in_table_list); TABLE_LIST *tl= table->pos_in_table_list; - vers_range_type_t vers_type= - tl->vers_conditions.type == FOR_SYSTEM_TIME_UNSPECIFIED ? - slex->vers_conditions.type : tl->vers_conditions.type; + vers_range_type_t vers_type= tl->vers_conditions.type; enum_sql_command sql_command= thd->lex->sql_command; unsigned int create_options= thd->lex->create_info.options; diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 62171ae2423..8dc6203ed86 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -263,8 +263,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (open_and_lock_tables(thd, table_list, TRUE, 0)) DBUG_RETURN(TRUE); - bool truncate_history= - select_lex->vers_conditions.type != FOR_SYSTEM_TIME_UNSPECIFIED; + bool truncate_history= table_list->vers_conditions; if (truncate_history) { TABLE *table= table_list->table; @@ -276,12 +275,12 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, // trx_sees() in InnoDB reads sys_trx_start if (!table->versioned_by_sql()) { - if (select_lex->vers_conditions.type == FOR_SYSTEM_TIME_BETWEEN || - select_lex->vers_conditions.type == FOR_SYSTEM_TIME_FROM_TO) + if (table_list->vers_conditions.type == FOR_SYSTEM_TIME_BETWEEN || + table_list->vers_conditions.type == FOR_SYSTEM_TIME_FROM_TO) { bitmap_set_bit(table->read_set, table->vers_start_field()->field_index); } - else if (select_lex->vers_conditions.type == FOR_SYSTEM_TIME_BEFORE) + else if (table_list->vers_conditions.type == FOR_SYSTEM_TIME_BEFORE) { bitmap_set_bit(table->read_set, table->vers_end_field()->field_index); } diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index d6a00827fef..cdbb01fa5aa 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -714,7 +714,7 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived) cursor->outer_join|= JOIN_TYPE_OUTER; } - // System Versioning begin + // System Versioning: fix system fields of versioned derived table #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wformat" #pragma GCC diagnostic ignored "-Wformat-extra-args" @@ -722,7 +722,11 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived) && sl->table_list.elements > 0) { // Similar logic as in mysql_create_view() - TABLE_LIST *impli_table= NULL, *expli_table= NULL; + // Leading versioning table detected implicitly (first one selected) + TABLE_LIST *impli_table= NULL; + // Leading versioning table specified explicitly + // (i.e. if at least one system field is selected) + TABLE_LIST *expli_table= NULL; const char *impli_start, *impli_end; Item_field *expli_start= NULL, *expli_end= NULL; @@ -826,14 +830,10 @@ expli_table_err: if (impli_table->vers_conditions) { - sl->vers_derived_conds= impli_table->vers_conditions; - if (derived->is_view() && !sl->vers_conditions) - sl->vers_conditions.import_outer= true; + sl->vers_export_outer= impli_table->vers_conditions; } - else if (sl->vers_conditions) - sl->vers_derived_conds= sl->vers_conditions; else - sl->vers_conditions.import_outer= true; + sl->vers_import_outer= true; // FIXME: is needed? } } // if (sl->table_list.elements > 0) #pragma GCC diagnostic pop diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 589d02fb55b..481ee76e0f2 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -2282,8 +2282,8 @@ void st_select_lex::init_select() with_dep= 0; join= 0; lock_type= TL_READ_DEFAULT; - vers_conditions.empty(); - vers_derived_conds.empty(); + vers_import_outer= false; + vers_export_outer.empty(); } /* diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 5f6232c2c50..6756148431d 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -992,8 +992,9 @@ public: thr_lock_type lock_type; /* System Versioning */ - vers_select_conds_t vers_conditions; - vers_select_conds_t vers_derived_conds; + vers_select_conds_t vers_export_outer; + bool vers_import_outer; + /* push new Item_field into item_list */ bool vers_push_field(THD *thd, TABLE_LIST *table, const char* field_name); void init_query(); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 8575755c267..437c04c2984 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -672,7 +672,7 @@ bool vers_select_conds_t::init_from_sysvar(THD *thd) { st_vers_current_time &in= thd->variables.vers_current_time; type= in.type; - unit= UNIT_TIMESTAMP; + unit_start= UNIT_TIMESTAMP; if (type != FOR_SYSTEM_TIME_UNSPECIFIED && type != FOR_SYSTEM_TIME_ALL) { DBUG_ASSERT(type == FOR_SYSTEM_TIME_AS_OF); @@ -714,14 +714,7 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, } if (versioned_tables == 0) - { - if (slex->vers_conditions) - { - my_error(ER_VERS_UNUSED_CLAUSE, MYF(0), "SYSTEM_TIME"); - DBUG_RETURN(-1); - } DBUG_RETURN(0); - } /* For prepared statements we create items on statement arena, because they must outlive execution phase for multiple executions. */ @@ -771,53 +764,43 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, } SELECT_LEX *outer_slex= slex->next_select_in_list(); - bool force_slex_conds= false; - if (outer_slex) + // propagate derived conditions to outer SELECT_LEX + if (outer_slex && slex->vers_export_outer) { - if (slex->vers_derived_conds) + for (table= outer_slex->table_list.first; table; table= table->next_local) { - // Propagate derived conditions to outer SELECT_LEX: - if (!outer_slex->vers_conditions) + if (!table->vers_conditions) { - outer_slex->vers_conditions= slex->vers_derived_conds; - outer_slex->vers_conditions.from_inner= true; - outer_slex->vers_conditions.used= true; + table->vers_conditions= slex->vers_export_outer; + table->vers_conditions.from_inner= true; } } - if (slex->vers_conditions.import_outer) + } + + for (table= tables; table; table= table->next_local) + { + if (table->table && table->table->versioned()) { - DBUG_ASSERT(slex->master_unit()); - TABLE_LIST* derived= slex->master_unit()->derived; - DBUG_ASSERT(derived); - if (derived->vers_conditions) - { - slex->vers_conditions= derived->vers_conditions; - derived->vers_conditions.used= true; - force_slex_conds= derived->is_view(); - } - else + vers_select_conds_t &vers_conditions= table->vers_conditions; + + // propagate system_time from nearest outer SELECT_LEX + if (!vers_conditions && outer_slex && slex->vers_import_outer) { - // Propagate query conditions from nearest outer SELECT_LEX: - while (outer_slex && (!outer_slex->vers_conditions || outer_slex->vers_conditions.from_inner)) + TABLE_LIST* derived= slex->master_unit()->derived; + while (outer_slex && (!derived->vers_conditions || derived->vers_conditions.from_inner)) + { + derived= outer_slex->master_unit()->derived; outer_slex= outer_slex->next_select_in_list(); + } if (outer_slex) { - slex->vers_conditions= outer_slex->vers_conditions; - outer_slex->vers_conditions.used= true; - force_slex_conds= derived->is_view(); + DBUG_ASSERT(derived); + DBUG_ASSERT(derived->vers_conditions); + vers_conditions= derived->vers_conditions; } } - } - } - - for (table= tables; table; table= table->next_local) - { - if (table->table && table->table->versioned()) - { - vers_select_conds_t &vers_conditions= force_slex_conds || !table->vers_conditions? - (slex->vers_conditions.used= true, slex->vers_conditions) : - table->vers_conditions; + // propagate system_time from sysvar if (!vers_conditions) { if (vers_conditions.init_from_sysvar(thd)) @@ -869,12 +852,57 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, bool tmp_from_ib= table->table->s->table_category == TABLE_CATEGORY_TEMPORARY && table->table->vers_start_field()->type() == MYSQL_TYPE_LONGLONG; - if (table->table->versioned_by_sql() && !tmp_from_ib) + bool timestamps_only= table->table->versioned_by_sql() && !tmp_from_ib; + + if (vers_conditions) { - if (vers_conditions.unit == UNIT_TRX_ID) + vers_conditions.resolve_units(timestamps_only); + if (timestamps_only) { - my_error(ER_VERS_ENGINE_UNSUPPORTED, MYF(0), table->table_name); - DBUG_RETURN(-1); + if (vers_conditions.unit_start == UNIT_TRX_ID || vers_conditions.unit_end == UNIT_TRX_ID) + { + my_error(ER_VERS_ENGINE_UNSUPPORTED, MYF(0), table->table_name); + DBUG_RETURN(-1); + } + } + else if (thd->variables.vers_innodb_algorithm_simple) + { + DBUG_ASSERT(table->table->s && table->table->s->db_plugin); + handlerton *hton= plugin_hton(table->table->s->db_plugin); + DBUG_ASSERT(hton); + bool convert_start= false; + bool convert_end= false; + switch (vers_conditions.type) + { + case FOR_SYSTEM_TIME_AS_OF: + if (vers_conditions.unit_start == UNIT_TIMESTAMP) + convert_start= convert_end= true; + break; + case FOR_SYSTEM_TIME_BEFORE: + if (vers_conditions.unit_start == UNIT_TIMESTAMP) + convert_end= true; + break; + case FOR_SYSTEM_TIME_FROM_TO: + case FOR_SYSTEM_TIME_BETWEEN: + if (vers_conditions.unit_start == UNIT_TIMESTAMP) + convert_end= true; + if (vers_conditions.unit_end == UNIT_TIMESTAMP) + convert_start= true; + default: + break; + } + if (convert_start) + row_start= newx Item_func_vtq_ts( + thd, + hton, + row_start, + VTQ_COMMIT_TS); + if (convert_end) + row_end= newx Item_func_vtq_ts( + thd, + hton, + row_end, + VTQ_COMMIT_TS); } } @@ -943,7 +971,7 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, cond1= newx Item_func_eq(thd, row_end2, curr); break; case FOR_SYSTEM_TIME_AS_OF: - trx_id0= vers_conditions.unit == UNIT_TIMESTAMP ? + trx_id0= vers_conditions.unit_start == UNIT_TIMESTAMP ? newx Item_func_vtq_id(thd, hton, vers_conditions.start, VTQ_TRX_ID) : vers_conditions.start; cond1= newx Item_func_vtq_trx_sees_eq(thd, hton, trx_id0, row_start); @@ -951,24 +979,19 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, break; case FOR_SYSTEM_TIME_FROM_TO: case FOR_SYSTEM_TIME_BETWEEN: - if (vers_conditions.unit == UNIT_TIMESTAMP) - { - trx_id0= newx Item_func_vtq_id(thd, hton, vers_conditions.start, VTQ_TRX_ID, true); - trx_id1= newx Item_func_vtq_id(thd, hton, vers_conditions.end, VTQ_TRX_ID, false); - } - else - { - trx_id0= vers_conditions.start; - trx_id1= vers_conditions.end; - } - + trx_id0= vers_conditions.unit_start == UNIT_TIMESTAMP ? + newx Item_func_vtq_id(thd, hton, vers_conditions.start, VTQ_TRX_ID, true) : + vers_conditions.start; + trx_id1= vers_conditions.unit_end == UNIT_TIMESTAMP ? + newx Item_func_vtq_id(thd, hton, vers_conditions.end, VTQ_TRX_ID, false) : + vers_conditions.end; cond1= vers_conditions.type == FOR_SYSTEM_TIME_FROM_TO ? newx Item_func_vtq_trx_sees(thd, hton, trx_id1, row_start) : newx Item_func_vtq_trx_sees_eq(thd, hton, trx_id1, row_start); cond2= newx Item_func_vtq_trx_sees_eq(thd, hton, row_end, trx_id0); break; case FOR_SYSTEM_TIME_BEFORE: - trx_id0= vers_conditions.unit == UNIT_TIMESTAMP ? + trx_id0= vers_conditions.unit_start == UNIT_TIMESTAMP ? newx Item_func_vtq_id(thd, hton, vers_conditions.start, VTQ_TRX_ID) : vers_conditions.start; cond1= newx Item_func_lt(thd, row_end, trx_id0); @@ -994,12 +1017,6 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, } // if (... table->table->versioned()) } // for (table= tables; ...) - if (!slex->vers_conditions.used && slex->vers_conditions) - { - my_error(ER_VERS_UNUSED_CLAUSE, MYF(0), "SYSTEM_TIME"); - DBUG_RETURN(-1); - } - DBUG_RETURN(0); #undef newx } diff --git a/sql/sql_truncate.cc b/sql/sql_truncate.cc index 4faad5b4711..b65818a2716 100644 --- a/sql/sql_truncate.cc +++ b/sql/sql_truncate.cc @@ -492,18 +492,17 @@ bool Sql_cmd_truncate_table::truncate_table(THD *thd, TABLE_LIST *table_ref) bool Sql_cmd_truncate_table::execute(THD *thd) { bool res= TRUE; - TABLE_LIST *first_table= thd->lex->select_lex.table_list.first; + TABLE_LIST *table= thd->lex->select_lex.table_list.first; DBUG_ENTER("Sql_cmd_truncate_table::execute"); - bool truncate_history= thd->lex->current_select->vers_conditions.type != - FOR_SYSTEM_TIME_UNSPECIFIED; - if (truncate_history) - DBUG_RETURN(mysql_delete(thd, first_table, NULL, NULL, -1, 0, NULL)); + DBUG_ASSERT(table); + if (table->vers_conditions) + DBUG_RETURN(mysql_delete(thd, table, NULL, NULL, -1, 0, NULL)); - if (check_one_table_access(thd, DROP_ACL, first_table)) + if (check_one_table_access(thd, DROP_ACL, table)) DBUG_RETURN(res); - if (! (res= truncate_table(thd, first_table))) + if (! (res= truncate_table(thd, table))) my_ok(thd); DBUG_RETURN(res); diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 46dd7570874..4626a0369fb 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -453,11 +453,15 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, goto err; } - { /* System Versioning begin */ + { /* System Versioning: fix system fields of versioned view */ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wformat" #pragma GCC diagnostic ignored "-Wformat-extra-args" - TABLE_LIST *impli_table= NULL, *expli_table= NULL; + // Leading versioning table detected implicitly (first one selected) + TABLE_LIST *impli_table= NULL; + // Leading versioning table specified explicitly + // (i.e. if at least one system field is selected) + TABLE_LIST *expli_table= NULL; const char *impli_start, *impli_end; Item_field *expli_start= NULL, *expli_end= NULL; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 4cab239795d..c0957699602 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -863,10 +863,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %parse-param { THD *thd } %lex-param { THD *thd } /* - Currently there are 103 shift/reduce conflicts. + Currently there are 116 shift/reduce conflicts. We should not introduce new conflicts any more. */ -%expect 103 +%expect 116 /* Comments for TOKENS. @@ -1623,7 +1623,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); opt_constraint constraint opt_ident sp_decl_ident sp_block_label - period_for_system_time_column_id %type TEXT_STRING @@ -1972,7 +1971,7 @@ END_OF_INPUT %type opt_with_column_list -%type trans_or_timestamp +%type opt_trans_or_timestamp %type opt_for_system_time_clause %type with_or_without_system %% @@ -2510,7 +2509,7 @@ create: sequence_definition())) MYSQL_YYABORT; } - opt_sequence opt_create_table_options + opt_sequence opt_create_sequence_options { LEX *lex= thd->lex; @@ -4774,7 +4773,7 @@ create_like: opt_create_select: /* empty */ {} - | opt_duplicate opt_as create_select_query_expression + | opt_duplicate opt_as create_select_query_expression opt_versioning_option ; create_select_query_expression: @@ -5691,14 +5690,19 @@ create_or_replace: } ; -opt_create_table_options: +opt_create_sequence_options: /* empty */ | create_table_options ; -create_table_options_space_separated: - create_table_option - | create_table_option create_table_options_space_separated +opt_create_table_options: + /* empty */ + | create_table_options_versioning + ; + +alter_table_options: + create_table_option_versioning + | create_table_option_versioning alter_table_options ; create_table_options: @@ -5707,6 +5711,12 @@ create_table_options: | create_table_option ',' create_table_options ; +create_table_options_versioning: + create_table_option_versioning + | create_table_option_versioning create_table_options_versioning + | create_table_option_versioning ',' create_table_options_versioning + ; + create_table_option: ENGINE_SYM opt_equal storage_engines { @@ -5951,31 +5961,30 @@ create_table_option: Lex->create_info.used_fields|= HA_CREATE_USED_SEQUENCE; Lex->create_info.sequence= $3; } - | WITH_SYSTEM_SYM table_versioning + ; + +create_table_option_versioning: + create_table_option + | versioning_option + ; + +opt_versioning_option: + /* empty */ + | versioning_option + ; + +versioning_option: + WITH_SYSTEM_SYM VERSIONING_SYM { Lex->vers_get_info().with_system_versioning= true; Lex->create_info.options|= HA_VERSIONED_TABLE; } - | WITHOUT SYSTEM table_versioning + | WITHOUT SYSTEM VERSIONING_SYM { Lex->vers_get_info().without_system_versioning= true; } ; -table_versioning: - VERSIONING_SYM - { - Vers_parse_info &info= Lex->vers_get_info(); - if (info.with_system_versioning || info.without_system_versioning) - { - my_error_as(ER_VERS_WRONG_PARAMS, ER_MULTIPLE_CLAUSE, MYF(0), - Lex->create_last_non_select_table->table_name, - "WITH/WITHOUT SYSTEM VERSIONING"); - MYSQL_YYABORT; - } - } - ; - default_charset: opt_default charset opt_equal charset_name_or_default { @@ -6175,16 +6184,9 @@ constraint_def: period_for_system_time: // If FOR_SYM is followed by SYSTEM_TIME_SYM then they are merged to: FOR_SYSTEM_TIME_SYM . - PERIOD_SYM FOR_SYSTEM_TIME_SYM '(' period_for_system_time_column_id ',' period_for_system_time_column_id ')' + PERIOD_SYM FOR_SYSTEM_TIME_SYM '(' ident ',' ident ')' { Vers_parse_info &info= Lex->vers_get_info(); - if (!my_strcasecmp(system_charset_info, $4.str, $6.str)) - { - my_error_as(ER_VERS_WRONG_PARAMS, ER_MULTIPLE_IDENTIFIER, MYF(0), - Lex->create_last_non_select_table->table_name, $4.str, - "PERIOD FOR SYSTEM_TIME"); - MYSQL_YYABORT; - } info.set_period_for_system_time($4, $6); } ; @@ -6297,7 +6299,6 @@ field_def: LEX *lex= Lex; Vers_parse_info &info= lex->vers_get_info(); const char *field_name= lex->last_field->field_name; - const char *table_name= lex->create_last_non_select_table->table_name; LString_i *p; const char* clause; @@ -6319,12 +6320,6 @@ field_def: break; } DBUG_ASSERT(p); - if (*p) - { - my_error_as(ER_VERS_WRONG_PARAMS, ER_MULTIPLE_CLAUSE_2, MYF(0), - table_name, clause, field_name, p->ptr()); - MYSQL_YYABORT; - } *p= field_name; if (lex->last_field->implicit_not_null) { @@ -6821,13 +6816,6 @@ serial_attribute: } | with_or_without_system VERSIONING_SYM { - if (Lex->last_field->versioning != Column_definition::VERSIONING_NOT_SET) - { - my_error_as(ER_VERS_WRONG_PARAMS, ER_MULTIPLE_CLAUSE_FOR, MYF(0), - Lex->create_last_non_select_table->table_name, - "WITH/WITHOUT SYSTEM VERSIONING", Lex->last_field->field_name); - MYSQL_YYABORT; - } Lex->last_field->versioning= $1; Lex->create_info.options|= HA_VERSIONED_TABLE; } @@ -7928,7 +7916,7 @@ alter_list_item: MYSQL_YYABORT; Lex->alter_info.flags|= Alter_info::ALTER_OPTIONS; } - | create_table_options_space_separated + | alter_table_options { LEX *lex=Lex; lex->alter_info.flags|= Alter_info::ALTER_OPTIONS; @@ -8838,8 +8826,12 @@ select_options: } ; -trans_or_timestamp: - TRANSACTION_SYM +opt_trans_or_timestamp: + /* empty */ + { + $$ = UNIT_AUTO; + } + | TRANSACTION_SYM { $$ = UNIT_TRX_ID; } @@ -8855,7 +8847,22 @@ opt_system_time_clause: | SYSTEM_TIME_SYM system_time_expr { DBUG_ASSERT(Select); - Select->vers_conditions= Lex->vers_conditions; + int used= 0; + if (Lex->vers_conditions) + { + for (TABLE_LIST *table= Select->table_list.first; table; table= table->next_local) + { + if (!table->vers_conditions) + { + table->vers_conditions= Lex->vers_conditions; + used++; + } + } + if (!used) + { + my_yyabort_error((ER_VERS_UNUSED_CLAUSE, MYF(0), "SYSTEM_TIME")); + } + } } ; @@ -8871,7 +8878,7 @@ opt_for_system_time_clause: ; system_time_expr: - AS OF_SYM trans_or_timestamp simple_expr + AS OF_SYM opt_trans_or_timestamp simple_expr { Lex->vers_conditions.init(FOR_SYSTEM_TIME_AS_OF, $3, $4); } @@ -8884,42 +8891,20 @@ system_time_expr: } | ALL { - Lex->vers_conditions.init(FOR_SYSTEM_TIME_ALL, UNIT_TIMESTAMP); + Lex->vers_conditions.init(FOR_SYSTEM_TIME_ALL); } - | FROM trans_or_timestamp simple_expr - TO_SYM trans_or_timestamp simple_expr + | FROM opt_trans_or_timestamp simple_expr + TO_SYM opt_trans_or_timestamp simple_expr { - if ($2 != $5) - { - Lex->parse_error(ER_VERS_RANGE_UNITS_MISMATCH); - MYSQL_YYABORT; - } - Lex->vers_conditions.init(FOR_SYSTEM_TIME_FROM_TO, $2, $3, $6); - } - | trans_or_timestamp - FROM simple_expr - TO_SYM simple_expr - { - Lex->vers_conditions.init(FOR_SYSTEM_TIME_FROM_TO, $1, $3, $5); - } - | BETWEEN_SYM trans_or_timestamp simple_expr - AND_SYM trans_or_timestamp simple_expr - { - if ($2 != $5) - { - Lex->parse_error(ER_VERS_RANGE_UNITS_MISMATCH); - MYSQL_YYABORT; - } - Lex->vers_conditions.init(FOR_SYSTEM_TIME_BETWEEN, $2, $3, $6); + Lex->vers_conditions.init(FOR_SYSTEM_TIME_FROM_TO, $2, $3, $5, $6); } - | trans_or_timestamp - BETWEEN_SYM simple_expr - AND_SYM simple_expr + | BETWEEN_SYM opt_trans_or_timestamp simple_expr + AND_SYM opt_trans_or_timestamp simple_expr { - Lex->vers_conditions.init(FOR_SYSTEM_TIME_BETWEEN, $1, $3, $5); + Lex->vers_conditions.init(FOR_SYSTEM_TIME_BETWEEN, $2, $3, $5, $6); } | BEFORE_SYM - trans_or_timestamp + opt_trans_or_timestamp simple_expr { Lex->vers_conditions.init(FOR_SYSTEM_TIME_BEFORE, $2, $3); @@ -13047,7 +13032,6 @@ truncate: lex->select_lex.init_order(); YYPS->m_lock_type= TL_WRITE; YYPS->m_mdl_type= MDL_EXCLUSIVE; - Select->vers_conditions.empty(); } table_name opt_for_system_time_clause opt_lock_wait_timeout { @@ -13057,7 +13041,7 @@ truncate: if (lex->m_sql_cmd == NULL) MYSQL_YYABORT; if ($5) - Select->vers_conditions= Lex->vers_conditions; + Lex->last_table()->vers_conditions= Lex->vers_conditions; } ; @@ -16244,13 +16228,6 @@ column_list: | column_list_id ; -period_for_system_time_column_id: - ident - { - $$= $1; - } - ; - column_list_id: ident { diff --git a/sql/table.cc b/sql/table.cc index 2dcec49d46d..5bb8e6aa3f8 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -8423,6 +8423,24 @@ LEX_CSTRING *fk_option_name(enum_fk_option opt) return names + opt; } +void vers_select_conds_t::resolve_units(bool timestamps_only) +{ + DBUG_ASSERT(type != FOR_SYSTEM_TIME_UNSPECIFIED); + DBUG_ASSERT(start); + if (unit_start == UNIT_AUTO) + { + unit_start= (!timestamps_only && (start->result_type() == INT_RESULT || + start->result_type() == REAL_RESULT)) ? + UNIT_TRX_ID : UNIT_TIMESTAMP; + } + if (end && unit_end == UNIT_AUTO) + { + unit_end= (!timestamps_only && (end->result_type() == INT_RESULT || + end->result_type() == REAL_RESULT)) ? + UNIT_TRX_ID : UNIT_TIMESTAMP; + } +} + Field *TABLE::find_field_by_name(const char *str) const { diff --git a/sql/table.h b/sql/table.h index 8bcdb001d59..82d41ff831b 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1845,7 +1845,8 @@ class Item_in_subselect; enum vers_range_unit_t { - UNIT_TIMESTAMP = 0, + UNIT_AUTO = 0, + UNIT_TIMESTAMP, UNIT_TRX_ID }; @@ -1853,31 +1854,32 @@ enum vers_range_unit_t struct vers_select_conds_t { vers_range_type_t type; - vers_range_unit_t unit; + vers_range_unit_t unit_start, unit_end; bool import_outer:1; bool from_inner:1; - bool used:1; Item *start, *end; void empty() { type= FOR_SYSTEM_TIME_UNSPECIFIED; - unit= UNIT_TIMESTAMP; - import_outer= from_inner= used= false; + unit_start= unit_end= UNIT_AUTO; + import_outer= from_inner= false; start= end= NULL; } void init( vers_range_type_t t, - vers_range_unit_t u, + vers_range_unit_t u_start= UNIT_AUTO, Item * s= NULL, + vers_range_unit_t u_end= UNIT_AUTO, Item * e= NULL) { type= t; - unit= u; + unit_start= u_start; + unit_end= u_end; start= s; end= e; - import_outer= from_inner= used= false; + import_outer= from_inner= false; } bool init_from_sysvar(THD *thd); @@ -1894,6 +1896,7 @@ struct vers_select_conds_t { return type != FOR_SYSTEM_TIME_UNSPECIFIED; } + void resolve_units(bool timestamps_only); }; struct LEX; -- cgit v1.2.1 From 9714c4463d5867c1bd5d03086df1949f8657dfa4 Mon Sep 17 00:00:00 2001 From: Eugene Kosov Date: Thu, 17 Aug 2017 08:58:07 +0300 Subject: Misc: comment --- sql/log_event.cc | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'sql') diff --git a/sql/log_event.cc b/sql/log_event.cc index 1bedaf152f3..2d1fc24690f 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -13023,8 +13023,13 @@ int Rows_log_event::find_row(rpl_group_info *rgi) // check whether master table is unversioned if (sys_trx_end->val_int() == 0) { + // sys_trx_start initialized with NULL when came from plain table. + // Set it notnull() because record_compare() count NULLs. table->vers_start_field()->set_notnull(); bitmap_set_bit(table->write_set, sys_trx_end->field_index); + // Plain source table may have a PRIMARY KEY. And sys_trx_end is always + // a part of PRIMARY KEY. Set it to max value for engine to find it in + // index. Needed for an UPDATE/DELETE cases. table->vers_end_field()->set_max(); m_vers_from_plain= true; } -- cgit v1.2.1 From 99baeaa951e0086e1d700c6bc6e0e153ba80c29f Mon Sep 17 00:00:00 2001 From: Eugene Kosov Date: Fri, 18 Aug 2017 14:29:22 +0300 Subject: SQL: MAX microseconds for current system rows [fixes #245] --- sql/field.cc | 7 +++++-- sql/item_vers.cc | 1 + sql/sql_select.cc | 7 +++++-- sql/sql_table.cc | 4 ++-- 4 files changed, 13 insertions(+), 6 deletions(-) (limited to 'sql') diff --git a/sql/field.cc b/sql/field.cc index cd83e61ba43..9afe74f0808 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -2012,6 +2012,7 @@ bool Field_vers_system::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate, ulonglo if (trx_id == ULONGLONG_MAX) { get_thd()->variables.time_zone->gmt_sec_to_TIME(ltime, TIMESTAMP_MAX_VALUE); + ltime->second_part= TIME_MAX_SECOND_PART; return false; } if (cached == trx_id) @@ -5481,10 +5482,11 @@ void Field_timestampf::set_max() { DBUG_ENTER("Field_timestampf::set_max"); ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; + DBUG_ASSERT(dec == TIME_SECOND_PART_DIGITS); set_notnull(); mi_int4store(ptr, TIMESTAMP_MAX_VALUE); - memset(ptr + 4, 0x0, value_length() - 4); + mi_int3store(ptr + 4, TIME_MAX_SECOND_PART); DBUG_VOID_RETURN; } @@ -5494,7 +5496,8 @@ bool Field_timestampf::is_max() DBUG_ENTER("Field_timestampf::is_max"); ASSERT_COLUMN_MARKED_FOR_READ; - DBUG_RETURN(mi_sint4korr(ptr) == 0x7fffffff); + DBUG_RETURN(mi_sint4korr(ptr) == TIMESTAMP_MAX_VALUE && + mi_sint3korr(ptr + 4) == TIME_MAX_SECOND_PART); } my_time_t Field_timestampf::get_timestamp(const uchar *pos, diff --git a/sql/item_vers.cc b/sql/item_vers.cc index 47e62d9ebf0..cbeb72810f3 100644 --- a/sql/item_vers.cc +++ b/sql/item_vers.cc @@ -67,6 +67,7 @@ Item_func_vtq_ts::get_date(MYSQL_TIME *res, ulonglong fuzzy_date) { null_value= false; thd->variables.time_zone->gmt_sec_to_TIME(res, TIMESTAMP_MAX_VALUE); + res->second_part= TIME_MAX_SECOND_PART; return false; } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 437c04c2984..68fb90ae9ec 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -676,7 +676,8 @@ bool vers_select_conds_t::init_from_sysvar(THD *thd) if (type != FOR_SYSTEM_TIME_UNSPECIFIED && type != FOR_SYSTEM_TIME_ALL) { DBUG_ASSERT(type == FOR_SYSTEM_TIME_AS_OF); - start= new (thd->mem_root) Item_datetime_literal(thd, &in.ltime, 6); + start= new (thd->mem_root) + Item_datetime_literal(thd, &in.ltime, TIME_SECOND_PART_DIGITS); if (!start) return true; } @@ -921,7 +922,9 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, { MYSQL_TIME max_time; thd->variables.time_zone->gmt_sec_to_TIME(&max_time, TIMESTAMP_MAX_VALUE); - curr= newx Item_datetime_literal(thd, &max_time); + max_time.second_part= TIME_MAX_SECOND_PART; + curr= newx Item_datetime_literal(thd, &max_time, + TIME_SECOND_PART_DIGITS); cond1= newx Item_func_eq(thd, row_end, curr); } else diff --git a/sql/sql_table.cc b/sql/sql_table.cc index cb4bb052a65..abf337c9fa6 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -10017,8 +10017,8 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, // TODO: write directly to record bypassing the same checks on every call to_sys_trx_start->store_time(&query_start); - static const timeval max_tv= {0x7fffffff, 0}; - static const uint dec= 6; + static const timeval max_tv= {TIMESTAMP_MAX_VALUE, TIME_MAX_SECOND_PART}; + static const uint dec= TIME_SECOND_PART_DIGITS; to_sys_trx_end->set_notnull(to_sys_trx_end->null_offset()); my_timestamp_to_binary(&max_tv, to_sys_trx_end->ptr, dec); } -- cgit v1.2.1 From ccddb4f7663769fa686be0aaa1cd3bd70d2196fa Mon Sep 17 00:00:00 2001 From: Eugene Kosov Date: Fri, 18 Aug 2017 21:06:02 +0300 Subject: Misc: simplify code --- sql/sql_table.cc | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'sql') diff --git a/sql/sql_table.cc b/sql/sql_table.cc index abf337c9fa6..1da16fc289c 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -10013,14 +10013,9 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, if (make_versioned) { - to_sys_trx_start->set_notnull(to_sys_trx_start->null_offset()); - // TODO: write directly to record bypassing the same checks on every call + to_sys_trx_start->set_notnull(); to_sys_trx_start->store_time(&query_start); - - static const timeval max_tv= {TIMESTAMP_MAX_VALUE, TIME_MAX_SECOND_PART}; - static const uint dec= TIME_SECOND_PART_DIGITS; - to_sys_trx_end->set_notnull(to_sys_trx_end->null_offset()); - my_timestamp_to_binary(&max_tv, to_sys_trx_end->ptr, dec); + to_sys_trx_end->set_max(); } else if (make_unversioned) { -- cgit v1.2.1 From a6aaa4fefeaa515f0d13b426d1d3cb807a56bf59 Mon Sep 17 00:00:00 2001 From: Eugene Kosov Date: Wed, 30 Aug 2017 15:28:43 +0300 Subject: SQL: move Vers_extended_item::vtq_cached_result() to Item [closes #250] --- sql/item.h | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) (limited to 'sql') diff --git a/sql/item.h b/sql/item.h index 130ad688593..f73b09e2d25 100644 --- a/sql/item.h +++ b/sql/item.h @@ -480,20 +480,8 @@ public: String_copier_for_item(THD *thd): m_thd(thd) { } }; -/* System versioning */ -class Vers_extended_item -{ -public: - virtual vtq_record_t* vtq_cached_result() - { - return NULL; - } -}; - - class Item: public Value_source, - public Type_std_attributes, - public Vers_extended_item + public Type_std_attributes { void operator=(Item &); /** @@ -1845,8 +1833,10 @@ public: { marker &= ~EXTRACTION_MASK; } -}; + /* System versioning */ + virtual vtq_record_t *vtq_cached_result() { return NULL; } +}; template inline Item* get_item_copy (THD *thd, MEM_ROOT *mem_root, T* item) -- cgit v1.2.1 From c2a70c8050125f219b1942a800aa4c134cc8520d Mon Sep 17 00:00:00 2001 From: Eugene Kosov Date: Fri, 1 Sep 2017 12:41:46 +0300 Subject: SQL, IB: option to drop historical rows on ALTER [closes #249] --- sql/handler.h | 2 ++ sql/mysqld.h | 7 +++++++ sql/sql_class.h | 2 +- sql/sql_rename.cc | 4 ++-- sql/sql_table.cc | 36 ++++++++++++++++++++++++++---------- sql/sql_yacc.yy | 6 ++++++ sql/sys_vars.cc | 9 ++++++--- 7 files changed, 50 insertions(+), 16 deletions(-) (limited to 'sql') diff --git a/sql/handler.h b/sql/handler.h index 24d1a73ec92..2ab9ed48c8f 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -2119,6 +2119,8 @@ public: static const HA_ALTER_FLAGS ALTER_DROP_CHECK_CONSTRAINT= 1ULL << 40; + static const HA_ALTER_FLAGS ALTER_DROP_HISTORICAL = 1ULL << 41; + /** Create options (like MAX_ROWS) for the new version of table. diff --git a/sql/mysqld.h b/sql/mysqld.h index 5ca5570460e..a30612067ba 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -208,6 +208,13 @@ enum vers_hide_enum VERS_HIDE_FULL, VERS_HIDE_NEVER }; + +enum vers_alter_history_enum +{ + VERS_ALTER_HISTORY_KEEP, + VERS_ALTER_HISTORY_SURVIVE, + VERS_ALTER_HISTORY_DROP +}; /* System Versioning end */ extern char *mysql_home_ptr, *pidfile_name_ptr; diff --git a/sql/sql_class.h b/sql/sql_class.h index 0c7179e5569..fb0cb3f7b0d 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -711,7 +711,7 @@ typedef struct system_variables my_bool vers_force; ulong vers_hide; my_bool vers_innodb_algorithm_simple; - my_bool vers_ddl_survival; + ulong vers_alter_history; } SV; /** diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc index 9b80ef78c63..631c7664994 100644 --- a/sql/sql_rename.cc +++ b/sql/sql_rename.cc @@ -300,7 +300,7 @@ do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db, char *new_table_name, (void) rename_table_in_stat_tables(thd, &db_name, &table_name, &new_db_name, &new_table); VTMD_rename vtmd(*ren_table); - if (thd->variables.vers_ddl_survival) + if (thd->variables.vers_alter_history == VERS_ALTER_HISTORY_SURVIVE) { rc= vtmd.try_rename(thd, new_db_name, new_table); if (rc) @@ -313,7 +313,7 @@ do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db, char *new_table_name, new_alias); if (rc) { - if (thd->variables.vers_ddl_survival) + if (thd->variables.vers_alter_history == VERS_ALTER_HISTORY_SURVIVE) vtmd.revert_rename(thd, new_db_name); revert_table_name: /* diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 1da16fc289c..1831eecbd26 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -2477,8 +2477,8 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, *(end= path + path_length - reg_ext_length)= '\0'; if (thd->lex->sql_command == SQLCOM_DROP_TABLE && - thd->variables.vers_ddl_survival && - table_type && table_type != view_pseudo_hton) + thd->variables.vers_alter_history == VERS_ALTER_HISTORY_SURVIVE && + table_type && table_type != view_pseudo_hton) { error= vtmd.check_exists(thd); if (error) @@ -5075,7 +5075,8 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table, } } - if (create_info->versioned() && thd->variables.vers_ddl_survival) + if (create_info->versioned() && + thd->variables.vers_alter_history == VERS_ALTER_HISTORY_SURVIVE) { VTMD_table vtmd(*create_table); if (vtmd.update(thd)) @@ -6372,6 +6373,8 @@ static bool fill_alter_inplace_info(THD *thd, ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_ADD_CHECK_CONSTRAINT; if (alter_info->flags & Alter_info::ALTER_DROP_CHECK_CONSTRAINT) ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_DROP_CHECK_CONSTRAINT; + if (thd->variables.vers_alter_history == VERS_ALTER_HISTORY_DROP) + ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_DROP_HISTORICAL; /* If we altering table with old VARCHAR fields we will be automatically @@ -8561,8 +8564,11 @@ simple_rename_or_index_change(THD *thd, TABLE_LIST *table_list, else { VTMD_rename vtmd(*table_list); - if (thd->variables.vers_ddl_survival && vtmd.try_rename(thd, new_db_name, new_table_name)) + if (thd->variables.vers_alter_history == VERS_ALTER_HISTORY_SURVIVE && + vtmd.try_rename(thd, new_db_name, new_table_name)) + { goto revert_table_name; + } else if (Table_triggers_list::change_table_name(thd, alter_ctx->db, alter_ctx->alias, @@ -8570,7 +8576,7 @@ simple_rename_or_index_change(THD *thd, TABLE_LIST *table_list, alter_ctx->new_db, alter_ctx->new_alias)) { - if (thd->variables.vers_ddl_survival) + if (thd->variables.vers_alter_history == VERS_ALTER_HISTORY_SURVIVE) vtmd.revert_rename(thd, new_db_name); revert_table_name: (void) mysql_rename_table(old_db_type, @@ -8711,7 +8717,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, thd->open_options&= ~HA_OPEN_FOR_ALTER; bool versioned= table_list->table && table_list->table->versioned(); bool vers_data_mod= versioned && - thd->variables.vers_ddl_survival && + thd->variables.vers_alter_history == VERS_ALTER_HISTORY_SURVIVE && alter_info->vers_data_modifying(); if (vers_data_mod) @@ -9949,11 +9955,14 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, else if (keep_versioned) { to->file->vers_auto_decrement= 0xffffffffffffffff; - if (thd->variables.vers_ddl_survival) + if (thd->variables.vers_alter_history == VERS_ALTER_HISTORY_SURVIVE) { query_start= thd->query_start_TIME(); from_sys_trx_end= from->vers_end_field(); to_sys_trx_start= to->vers_start_field(); + } else if (thd->variables.vers_alter_history == VERS_ALTER_HISTORY_DROP) + { + from_sys_trx_end= from->vers_end_field(); } } @@ -10011,6 +10020,12 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, copy_ptr->do_copy(copy_ptr); } + if (thd->variables.vers_alter_history == VERS_ALTER_HISTORY_DROP && + from_sys_trx_end && !from_sys_trx_end->is_max()) + { + continue; + } + if (make_versioned) { to_sys_trx_start->set_notnull(); @@ -10022,7 +10037,8 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, if (!from_sys_trx_end->is_max()) continue; // Drop history rows. } - else if (keep_versioned && thd->variables.vers_ddl_survival) + else if (keep_versioned && + thd->variables.vers_alter_history == VERS_ALTER_HISTORY_SURVIVE) { if (!from_sys_trx_end->is_max()) continue; // Do not copy history rows. @@ -10048,13 +10064,13 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, break; } if (keep_versioned && to->versioned_by_engine() && - !thd->variables.vers_ddl_survival) + thd->variables.vers_alter_history != VERS_ALTER_HISTORY_SURVIVE) { to->s->versioned= false; } error= to->file->ha_write_row(to->record[0]); if (keep_versioned && to->versioned_by_engine() && - !thd->variables.vers_ddl_survival) + thd->variables.vers_alter_history != VERS_ALTER_HISTORY_SURVIVE) { to->s->versioned= true; } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index c0957699602..20674ef5d27 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -15660,6 +15660,12 @@ set_expr_or_default: if ($$ == NULL) MYSQL_YYABORT; } + | DROP + { + $$=new (thd->mem_root) Item_string_sys(thd, "DROP", 4); + if ($$ == NULL) + MYSQL_YYABORT; + } ; /* Lock function */ diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index c618a4f4bc2..25437f7b3b4 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -406,9 +406,12 @@ static Sys_var_mybool Sys_vers_innodb_algorithm_simple( SESSION_VAR(vers_innodb_algorithm_simple), CMD_LINE(OPT_ARG), DEFAULT(TRUE)); -static Sys_var_mybool Sys_vers_ddl_survival( - "versioning_ddl_survival", "Use system versioning DDL survival feature", - SESSION_VAR(vers_ddl_survival), CMD_LINE(OPT_ARG), DEFAULT(FALSE)); +static const char *vers_alter_history_keywords[]= {"KEEP", "SURVIVE", "DROP", + NULL}; +static Sys_var_enum Sys_vers_alter_history( + "versioning_alter_history", "Versioning ALTER TABLE mode", + SESSION_VAR(vers_alter_history), CMD_LINE(OPT_ARG), + vers_alter_history_keywords, DEFAULT(VERS_ALTER_HISTORY_KEEP)); static Sys_var_ulonglong Sys_binlog_cache_size( "binlog_cache_size", "The size of the transactional cache for " -- cgit v1.2.1 From 88ccf759c1fce8f8e521475daf789083c8911600 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Fri, 1 Sep 2017 19:05:20 +0300 Subject: SQL: pruning, derived, view fixes [fixes #244] --- sql/field.cc | 9 +++++++-- sql/field.h | 35 +++++++++++++++++++++++++++++--- sql/item.cc | 10 +++++++-- sql/item.h | 8 ++++++++ sql/item_cmpfunc.cc | 58 ++++------------------------------------------------- sql/item_cmpfunc.h | 1 - sql/sql_select.cc | 12 +++++------ sql/sql_type.cc | 4 +++- sql/sql_type.h | 7 ++++--- sql/sql_yacc.yy | 16 +++++++++------ sql/table.cc | 5 ++++- 11 files changed, 86 insertions(+), 79 deletions(-) (limited to 'sql') diff --git a/sql/field.cc b/sql/field.cc index 9afe74f0808..c1497c3ddf6 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -1999,7 +1999,7 @@ bool Field_num::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) } -bool Field_vers_system::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate, ulonglong trx_id) +bool Field_vers_trx_id::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate, ulonglong trx_id) { ASSERT_COLUMN_MARKED_FOR_READ; DBUG_ASSERT(ltime); @@ -10568,7 +10568,7 @@ Field *make_field(TABLE_SHARE *share, if (flags & (VERS_SYS_START_FLAG|VERS_SYS_END_FLAG)) { return new (mem_root) - Field_vers_system(ptr, field_length, null_pos, null_bit, + Field_vers_trx_id(ptr, field_length, null_pos, null_bit, unireg_check, field_name, f_is_zerofill(pack_flag) != 0, f_is_dec(pack_flag) == 0); @@ -10657,6 +10657,11 @@ Field *make_field(TABLE_SHARE *share, return 0; } +bool Field_vers_trx_id::test_if_equality_guarantees_uniqueness(const Item* item) const +{ + return item->type() == Item::DATE_ITEM; +} + /** Create a field suitable for create of table. */ diff --git a/sql/field.h b/sql/field.h index efd3fe18e6f..219f88ced0a 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1407,6 +1407,11 @@ public: return flags & (VERS_SYS_START_FLAG | VERS_SYS_END_FLAG); } + virtual bool vers_trx_id() const + { + return false; + } + /* Validate a non-null field value stored in the given record according to the current thread settings, e.g. sql_mode. @@ -2130,11 +2135,11 @@ public: }; -class Field_vers_system :public Field_longlong { +class Field_vers_trx_id :public Field_longlong { MYSQL_TIME cache; ulonglong cached; public: - Field_vers_system(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, + Field_vers_trx_id(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, bool zero_arg, bool unsigned_arg) @@ -2143,13 +2148,37 @@ public: cached(0) {} enum_field_types real_type() const { return MYSQL_TYPE_LONGLONG; } - enum_field_types type() const { return MYSQL_TYPE_DATETIME;} + enum_field_types type() const { return MYSQL_TYPE_LONGLONG;} uint size_of() const { return sizeof(*this); } bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate, ulonglong trx_id); bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) { return get_date(ltime, fuzzydate, (ulonglong) val_int()); } + bool test_if_equality_guarantees_uniqueness(const Item *item) const; + bool can_optimize_keypart_ref(const Item_bool_func *cond, + const Item *item) const + { + return true; + } + + bool can_optimize_group_min_max(const Item_bool_func *cond, + const Item *const_item) const + { + return true; + } + bool can_optimize_range(const Item_bool_func *cond, + const Item *item, + bool is_eq_func) const + { + return true; + } + /* cmp_type() cannot be TIME_RESULT, because we want to compare this field against + integers. But in all other cases we treat it as TIME_RESULT! */ + bool vers_trx_id() const + { + return true; + } }; diff --git a/sql/item.cc b/sql/item.cc index 4314ee036e4..67e59016864 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -6328,7 +6328,7 @@ Field *Item::tmp_table_field_from_field_type(TABLE *table, if (field_flags() & (VERS_SYS_START_FLAG|VERS_SYS_END_FLAG)) { field= new (mem_root) - Field_vers_system((uchar*) 0, max_length, null_ptr, 0, Field::NONE, + Field_vers_trx_id((uchar*) 0, max_length, null_ptr, 0, Field::NONE, name, 0, unsigned_flag); } else @@ -6671,7 +6671,7 @@ int Item_int::save_in_field(Field *field, bool no_conversions) Item *Item_int::clone_item(THD *thd) { - return new (thd->mem_root) Item_int(thd, name, value, max_length); + return new (thd->mem_root) Item_int(thd, name, value, max_length, unsigned_flag); } @@ -10775,6 +10775,12 @@ Item *Item_field::vers_optimized_fields_transformer(THD *thd, uchar *) return this; } +bool Item_field::vers_trx_id() const +{ + DBUG_ASSERT(field); + return field->vers_trx_id(); +} + void Item::register_in(THD *thd) { next= thd->free_list; diff --git a/sql/item.h b/sql/item.h index f73b09e2d25..6e74329eb86 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1337,6 +1337,8 @@ public: { if (other->cmp_type() == TIME_RESULT) return other->field_type(); + if (vers_trx_id() || other->vers_trx_id()) + return MYSQL_TYPE_DATETIME; DBUG_ASSERT(0); // Two non-temporal data types, we should not get to here return MYSQL_TYPE_DATETIME; } @@ -1649,6 +1651,8 @@ public: virtual Item *vers_optimized_fields_transformer(THD *thd, uchar *) { return this; } + virtual bool vers_trx_id() const + { return false; } virtual Item *neg_transformer(THD *thd) { return NULL; } virtual Item *update_value_transformer(THD *thd, uchar *select_arg) { return this; } @@ -2751,6 +2755,7 @@ public: Item_field *field_for_view_update() { return this; } int fix_outer_field(THD *thd, Field **field, Item **reference); virtual Item *vers_optimized_fields_transformer(THD *thd, uchar *); + virtual bool vers_trx_id() const; virtual Item *update_value_transformer(THD *thd, uchar *select_arg); Item *derived_field_transformer_for_having(THD *thd, uchar *arg); Item *derived_field_transformer_for_where(THD *thd, uchar *arg); @@ -3188,6 +3193,9 @@ public: Item_int(THD *thd, const char *str_arg,longlong i,uint length): Item_num(thd), value(i) { max_length=length; name=(char*) str_arg; fixed= 1; } + Item_int(THD *thd, const char *str_arg,longlong i,uint length, bool flag): + Item_num(thd), value(i) + { max_length=length; name=(char*) str_arg; fixed= 1; unsigned_flag= flag; } Item_int(THD *thd, const char *str_arg, uint length=64); enum Type type() const { return INT_ITEM; } enum Item_result result_type () const { return INT_RESULT; } diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 147bb270e37..b390f709fb4 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -127,9 +127,12 @@ Type_handler_hybrid_field_type::aggregate_for_comparison(const char *funcname, many cases. */ set_handler(items[0]->type_handler()->type_handler_for_comparison()); + m_vers_trx_id= items[0]->vers_trx_id(); for (uint i= 1 ; i < nitems ; i++) { unsigned_count+= items[i]->unsigned_flag; + if (!m_vers_trx_id) + m_vers_trx_id= items[i]->vers_trx_id(); if (aggregate_for_comparison(items[i]->type_handler()-> type_handler_for_comparison())) { @@ -421,7 +424,7 @@ void Item_func::convert_const_compared_to_int_field(THD *thd) args[field= 1]->real_item()->type() == FIELD_ITEM) { Item_field *field_item= (Item_field*) (args[field]->real_item()); - if ((field_item->field_type() == MYSQL_TYPE_LONGLONG || + if (((field_item->field_type() == MYSQL_TYPE_LONGLONG && !field_item->vers_trx_id()) || field_item->field_type() == MYSQL_TYPE_YEAR)) convert_const_to_int(thd, field_item, &args[!field]); } @@ -5218,59 +5221,6 @@ bool fix_escape_item(THD *thd, Item *escape_item, String *tmp_str, return FALSE; } -bool Item_bool_func2::fix_fields(THD* thd, Item** ref) -{ - if (Item_bool_func::fix_fields(thd, ref)) - return true; - - // System Versioning: convert TRX_ID to DATETIME - Item *trx_id= NULL; - int arg_idx= -1; - Field_vers_system *sys_field= NULL; - DBUG_ASSERT(arg_count == 2); - // find trx_id and sys_field - for (int i= 0; i < 2; ++i) - { - Item *arg= args[i]; - if (arg->result_type() != INT_RESULT) - continue; - DBUG_ASSERT(arg); - if (arg->type() == Item::FIELD_ITEM) - { - Field *f= static_cast(arg)->field; - DBUG_ASSERT(f); - if (f->vers_sys_field() && f->real_type() == MYSQL_TYPE_LONGLONG) - { - if (sys_field) - return false; - sys_field= static_cast(f); - continue; - } - } - if (trx_id) - return false; - trx_id= arg; - arg_idx= i; - } - if (!trx_id || !sys_field) - return false; - MYSQL_TIME ltime; - ulonglong trx_id_val= (ulonglong) trx_id->val_int(); - if (sys_field->get_date(<ime, false, trx_id_val)) - { - my_error(ER_VERS_NO_TRX_ID, MYF(0), trx_id_val); - return true; - } - Query_arena_stmt on_stmt_arena(thd); - Item *datetime= new (thd->mem_root) Item_datetime_literal(thd, <ime, 6); - if (!datetime) - return true; - DBUG_ASSERT(arg_idx > -1); - args[arg_idx]= datetime; - return false; -} - - bool Item_func_like::fix_fields(THD *thd, Item **ref) { DBUG_ASSERT(fixed == 0); diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 6eb1b34e568..a94105b352d 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -410,7 +410,6 @@ public: ftree= Item_func::get_mm_tree(param, cond_ptr); DBUG_RETURN(ftree); } - bool fix_fields(THD *thd, Item **ref); }; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 68fb90ae9ec..a5f1e3c9626 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -918,7 +918,7 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, switch (vers_conditions.type) { case FOR_SYSTEM_TIME_UNSPECIFIED: - if (!tmp_from_ib) + if (t->vers_start_field()->real_type() != MYSQL_TYPE_LONGLONG) { MYSQL_TIME max_time; thd->variables.time_zone->gmt_sec_to_TIME(&max_time, TIMESTAMP_MAX_VALUE); @@ -25555,6 +25555,11 @@ void TABLE_LIST::print(THD *thd, table_map eliminated_tables, String *str, } #endif /* WITH_PARTITION_STORAGE_ENGINE */ } + if (table && table->versioned()) + { + // versioning conditions are already unwrapped to WHERE clause + str->append(" FOR SYSTEM_TIME ALL"); + } if (my_strcasecmp(table_alias_charset, cmp_name, alias)) { char t_alias_buff[MAX_ALIAS_NAME]; @@ -25573,11 +25578,6 @@ void TABLE_LIST::print(THD *thd, table_map eliminated_tables, String *str, append_identifier(thd, str, t_alias, strlen(t_alias)); } - if (table && table->versioned()) - { - // versioning conditions are already unwrapped to WHERE clause - str->append(" FOR SYSTEM_TIME ALL"); - } if (index_hints) { diff --git a/sql/sql_type.cc b/sql/sql_type.cc index 055a8969787..b61cadd0ea6 100644 --- a/sql/sql_type.cc +++ b/sql/sql_type.cc @@ -428,7 +428,9 @@ Type_handler_hybrid_field_type::aggregate_for_comparison(const Type_handler *h) Item_result a= cmp_type(); Item_result b= h->cmp_type(); - if (a == STRING_RESULT && b == STRING_RESULT) + if (m_vers_trx_id && (a == STRING_RESULT || b == STRING_RESULT)) + m_type_handler= &type_handler_datetime; + else if (a == STRING_RESULT && b == STRING_RESULT) m_type_handler= &type_handler_long_blob; else if (a == INT_RESULT && b == INT_RESULT) m_type_handler= &type_handler_longlong; diff --git a/sql/sql_type.h b/sql/sql_type.h index 053132626a3..49056a7d0de 100644 --- a/sql/sql_type.h +++ b/sql/sql_type.h @@ -1473,16 +1473,17 @@ public: class Type_handler_hybrid_field_type { const Type_handler *m_type_handler; + bool m_vers_trx_id; public: Type_handler_hybrid_field_type(); Type_handler_hybrid_field_type(const Type_handler *handler) - :m_type_handler(handler) + :m_type_handler(handler), m_vers_trx_id(false) { } Type_handler_hybrid_field_type(enum_field_types type) - :m_type_handler(Type_handler::get_handler_by_field_type(type)) + :m_type_handler(Type_handler::get_handler_by_field_type(type)), m_vers_trx_id(false) { } Type_handler_hybrid_field_type(const Type_handler_hybrid_field_type *other) - :m_type_handler(other->m_type_handler) + :m_type_handler(other->m_type_handler), m_vers_trx_id(other->m_vers_trx_id) { } const Type_handler *type_handler() const { return m_type_handler; } enum_field_types field_type() const { return m_type_handler->field_type(); } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 20674ef5d27..ea7eb443896 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -11353,11 +11353,11 @@ table_primary_ident: */ table_primary_derived: - '(' get_select_lex select_derived_union ')' opt_table_alias + '(' get_select_lex select_derived_union ')' opt_for_system_time_clause opt_table_alias { /* Use $2 instead of Lex->current_select as derived table will alter value of Lex->current_select. */ - if (!($3 || $5) && $2->embedding && + if (!($3 || $6) && $2->embedding && !$2->embedding->nested_join->join_list.elements) { /* we have a derived table ($3 == NULL) but no alias, @@ -11380,7 +11380,7 @@ table_primary_derived: if (ti == NULL) MYSQL_YYABORT; if (!($$= sel->add_table_to_list(thd, - ti, $5, 0, + ti, $6, 0, TL_READ, MDL_SHARED_READ))) MYSQL_YYABORT; @@ -11388,7 +11388,7 @@ table_primary_derived: lex->pop_context(); lex->nest_level--; } - else if ($5 != NULL) + else if ($6 != NULL) { /* Tables with or without joins within parentheses cannot @@ -11412,11 +11412,13 @@ table_primary_derived: if ($$ && $$->derived && !$$->derived->first_select()->next_select()) $$->select_lex->add_where_field($$->derived->first_select()); + if ($5) + $$->vers_conditions= Lex->vers_conditions; } /* Represents derived table with WITH clause */ | '(' get_select_lex subselect_start with_clause query_expression_body - subselect_end ')' opt_table_alias + subselect_end ')' opt_for_system_time_clause opt_table_alias { LEX *lex=Lex; SELECT_LEX *sel= $2; @@ -11427,10 +11429,12 @@ table_primary_derived: $5->set_with_clause($4); lex->current_select= sel; if (!($$= sel->add_table_to_list(lex->thd, - ti, $8, 0, + ti, $9, 0, TL_READ, MDL_SHARED_READ))) MYSQL_YYABORT; sel->add_joined_table($$); + if ($8) + $$->vers_conditions= Lex->vers_conditions; } ; diff --git a/sql/table.cc b/sql/table.cc index 5bb8e6aa3f8..902e6ea0224 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -4730,16 +4730,19 @@ bool TABLE_LIST::create_field_translation(THD *thd) */ if (is_view() && get_unit()->prepared && !field_translation_updated) { + field_translation_updated= TRUE; + if (field_translation_end - field_translation < select->item_list.elements) + goto allocate; while ((item= it++)) { field_translation[field_count++].item= item; } - field_translation_updated= TRUE; } DBUG_RETURN(FALSE); } +allocate: arena= thd->activate_stmt_arena_if_needed(&backup); /* Create view fields translation table */ -- cgit v1.2.1 From a734c2f0fb44b777866d8b0bc9ec46a08e4e6505 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 5 Sep 2017 16:49:08 +0300 Subject: Style: partitioning, sysvars, handler fixes * Sys_var_vers_asof formatting * Vers_field_stats renamed to Vers_min_max_stats * Item_temporal_literal::set_lower()/set_higher() replaced by operator>()/operator<() * handler API comments --- sql/handler.h | 36 +++++++++++++++++++++++++++++++++-- sql/item.cc | 20 ++++++++++---------- sql/item.h | 5 +++-- sql/partition_element.h | 8 +++++--- sql/partition_info.cc | 26 +++++++++++++++---------- sql/partition_info.h | 6 +++--- sql/sys_vars.ic | 50 ++++++++----------------------------------------- sql/table.h | 4 ++-- 8 files changed, 81 insertions(+), 74 deletions(-) (limited to 'sql') diff --git a/sql/handler.h b/sql/handler.h index 2ab9ed48c8f..a0c40d2765a 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -1392,9 +1392,41 @@ struct handlerton /* System Versioning */ + /** + Query VTQ by TRX_ID. + @param[in] thd MySQL thread + @param[out] out field value or whole record returned by query (selected by `field`) + @param[in] in_trx_id query parameter TRX_ID + @param[in] field field to get in `out` or VTQ_ALL for whole record (vtq_record_t) + @return TRUE if record is found, FALSE otherwise */ bool (*vers_query_trx_id)(THD* thd, void *out, ulonglong trx_id, vtq_field_t field); - bool (*vers_query_commit_ts)(THD* thd, void *out, const MYSQL_TIME &commit_ts, vtq_field_t field, bool backwards); - bool (*vers_trx_sees)(THD *thd, bool &result, ulonglong trx_id1, ulonglong trx_id0, ulonglong commit_id1, uchar iso_level1, ulonglong commit_id0); + + /** Query VTQ by COMMIT_TS. + @param[in] thd MySQL thread + @param[out] out field value or whole record returned by query (selected by `field`) + @param[in] commit_ts query parameter COMMIT_TS + @param[in] field field to get in `out` or VTQ_ALL for whole record (vtq_record_t) + @param[in] backwards direction of VTQ search + @return TRUE if record is found, FALSE otherwise */ + bool (*vers_query_commit_ts)(THD* thd, void *out, const MYSQL_TIME &commit_ts, + vtq_field_t field, bool backwards); + + /** Check if transaction TX1 sees transaction TX0. + @param[in] thd MySQL thread + @param[out] result true if TX1 sees TX0 + @param[in] trx_id1 TX1 TRX_ID + @param[in] trx_id0 TX0 TRX_ID + @param[in] commit_id1 TX1 COMMIT_ID + @param[in] iso_level1 TX1 isolation level + @param[in] commit_id0 TX0 COMMIT_ID + @return FALSE if there is no trx_id1 in VTQ, otherwise TRUE */ + bool (*vers_trx_sees)(THD *thd, bool &result, ulonglong trx_id1, ulonglong trx_id0, + ulonglong commit_id1, uchar iso_level1, ulonglong commit_id0); + + /** Upgrade InnoDB table handler to InnoDB partitioning handler. + @param[in] hnd InnoDB table handler + @param[in] mem_root mem_root for resulting handler + @return InnoDB partitioning handler or NULL on error */ handler *(*vers_upgrade_handler)(handler *hnd, MEM_ROOT *mem_root); }; diff --git a/sql/item.cc b/sql/item.cc index 67e59016864..5743c742900 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -6983,26 +6983,26 @@ bool Item_temporal_literal::eq(const Item *item, bool binary_cmp) const &((Item_temporal_literal *) item)->cached_time); } -bool Item_temporal_literal::set_lower(MYSQL_TIME * ltime) +bool Item_temporal_literal::operator<(const MYSQL_TIME <ime) const { - if (my_time_compare(ltime, &cached_time) < 0) - { - cached_time= *ltime; + if (my_time_compare(&cached_time, <ime) < 0) return true; - } return false; } -bool Item_temporal_literal::set_higher(MYSQL_TIME * ltime) +bool Item_temporal_literal::operator>(const MYSQL_TIME <ime) const { - if (my_time_compare(ltime, &cached_time) > 0) - { - cached_time= *ltime; + if (my_time_compare(&cached_time, <ime) > 0) return true; - } return false; } +bool Item_temporal_literal::operator==(const MYSQL_TIME <ime) const +{ + if (my_time_compare(&cached_time, <ime) == 0) + return true; + return false; +} void Item_date_literal::print(String *str, enum_query_type query_type) { diff --git a/sql/item.h b/sql/item.h index 6e74329eb86..ceb73c18264 100644 --- a/sql/item.h +++ b/sql/item.h @@ -3878,8 +3878,9 @@ public: { cached_time= *ltime; } - bool set_lower(MYSQL_TIME *ltime); - bool set_higher(MYSQL_TIME *ltime); + bool operator>(const MYSQL_TIME <ime) const; + bool operator<(const MYSQL_TIME <ime) const; + bool operator==(const MYSQL_TIME <ime) const; }; diff --git a/sql/partition_element.h b/sql/partition_element.h index 18276ef713e..723d2f21a46 100644 --- a/sql/partition_element.h +++ b/sql/partition_element.h @@ -90,7 +90,9 @@ typedef struct p_elem_val struct st_ddl_log_memory_entry; -class Vers_field_stats : public Sql_alloc +/* Used for collecting MIN/MAX stats on sys_trx_end for doing pruning + in SYSTEM_TIME partitiong. */ +class Vers_min_max_stats : public Sql_alloc { static const uint buf_size= 4 + (TIME_SECOND_PART_DIGITS + 1) / 2; uchar min_buf[buf_size]; @@ -100,7 +102,7 @@ class Vers_field_stats : public Sql_alloc mysql_rwlock_t lock; public: - Vers_field_stats(const char *field_name, TABLE_SHARE *share) : + Vers_min_max_stats(const char *field_name, TABLE_SHARE *share) : min_value(min_buf, NULL, 0, Field::NONE, field_name, share, 6), max_value(max_buf, NULL, 0, Field::NONE, field_name, share, 6) { @@ -108,7 +110,7 @@ public: memset(max_buf, 0, buf_size); mysql_rwlock_init(key_rwlock_LOCK_vers_stats, &lock); } - ~Vers_field_stats() + ~Vers_min_max_stats() { mysql_rwlock_destroy(&lock); } diff --git a/sql/partition_info.cc b/sql/partition_info.cc index 86fb4b5bf63..8bf2e2cd72d 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -926,8 +926,8 @@ bool partition_info::vers_setup_1(THD * thd, uint32 added) if (added) { DBUG_ASSERT(partitions.elements > added + 1); - Vers_field_stats** old_array= table->s->stat_trx; - table->s->stat_trx= static_cast( + Vers_min_max_stats** old_array= table->s->stat_trx; + table->s->stat_trx= static_cast( alloc_root(&table->s->mem_root, sizeof(void *) * partitions.elements * num_columns)); memcpy(table->s->stat_trx, old_array, sizeof(void *) * (partitions.elements - added) * num_columns); } @@ -961,8 +961,8 @@ bool partition_info::vers_setup_1(THD * thd, uint32 added) if (el->id == UINT32_MAX || el->type == partition_element::AS_OF_NOW) { DBUG_ASSERT(table && table->s); - Vers_field_stats *stat_trx_end= new (&table->s->mem_root) - Vers_field_stats(table->s->vers_end_field()->field_name, table->s); + Vers_min_max_stats *stat_trx_end= new (&table->s->mem_root) + Vers_min_max_stats(table->s->vers_end_field()->field_name, table->s); table->s->stat_trx[id * num_columns + STAT_TRX_END]= stat_trx_end; el->id= id++; if (el->type == partition_element::AS_OF_NOW) @@ -1107,7 +1107,7 @@ void partition_info::vers_update_col_vals(THD *thd, partition_element *el0, part my_time_t ts; part_column_list_val *col_val; Item_datetime_literal *val_item; - Vers_field_stats *stat_trx_x; + Vers_min_max_stats *stat_trx_x; for (uint i= 0; i < num_columns; ++i) { stat_trx_x= table->s->stat_trx[idx + i]; @@ -1118,8 +1118,11 @@ void partition_info::vers_update_col_vals(THD *thd, partition_element *el0, part col_val= &el0->get_col_val(i); val_item= static_cast(col_val->item_expression); DBUG_ASSERT(val_item); - if (val_item->set_lower(&t)) + if (*val_item > t) + { + val_item->set_time(&t); col_val->fixed= 0; + } } col_val= &el1->get_col_val(i); if (!col_val->max_value) @@ -1128,8 +1131,11 @@ void partition_info::vers_update_col_vals(THD *thd, partition_element *el0, part thd->variables.time_zone->gmt_sec_to_TIME(&t, ts); val_item= static_cast(col_val->item_expression); DBUG_ASSERT(val_item); - if (val_item->set_higher(&t)) + if (*val_item < t) + { + val_item->set_time(&t); col_val->fixed= 0; + } } } } @@ -1162,7 +1168,7 @@ bool partition_info::vers_setup_2(THD * thd, bool is_create_table_ind) if (!table->s->stat_trx) { DBUG_ASSERT(partitions.elements > 1); - table->s->stat_trx= static_cast( + table->s->stat_trx= static_cast( alloc_root(&table->s->mem_root, sizeof(void *) * partitions.elements * num_columns)); dont_stat= false; } @@ -1183,8 +1189,8 @@ bool partition_info::vers_setup_2(THD * thd, bool is_create_table_ind) } { - Vers_field_stats *stat_trx_end= new (&table->s->mem_root) - Vers_field_stats(table->s->vers_end_field()->field_name, table->s); + Vers_min_max_stats *stat_trx_end= new (&table->s->mem_root) + Vers_min_max_stats(table->s->vers_end_field()->field_name, table->s); table->s->stat_trx[el->id * num_columns + STAT_TRX_END]= stat_trx_end; } diff --git a/sql/partition_info.h b/sql/partition_info.h index 9dff7a41a4a..d69774e0841 100644 --- a/sql/partition_info.h +++ b/sql/partition_info.h @@ -478,14 +478,14 @@ public: // TODO: cache thread-shared part_recs and increment on INSERT return table->file->part_records(part) >= vers_info->limit; } - Vers_field_stats& vers_stat_trx(stat_trx_field fld, uint32 part_element_id) + Vers_min_max_stats& vers_stat_trx(stat_trx_field fld, uint32 part_element_id) { DBUG_ASSERT(table && table->s && table->s->stat_trx); - Vers_field_stats* res= table->s->stat_trx[part_element_id * num_columns + fld]; + Vers_min_max_stats* res= table->s->stat_trx[part_element_id * num_columns + fld]; DBUG_ASSERT(res); return *res; } - Vers_field_stats& vers_stat_trx(stat_trx_field fld, partition_element *part) + Vers_min_max_stats& vers_stat_trx(stat_trx_field fld, partition_element *part) { DBUG_ASSERT(part); return vers_stat_trx(fld, part->id); diff --git a/sql/sys_vars.ic b/sql/sys_vars.ic index 27987a8d8b8..4f075c3436e 100644 --- a/sql/sys_vars.ic +++ b/sql/sys_vars.ic @@ -2495,32 +2495,11 @@ public: class Sys_var_vers_asof: public sys_var { public: - Sys_var_vers_asof( - const char *name_arg, - const char *comment, - int flag_args, - ptrdiff_t off, - size_t size, - CMD_LINE getopt, - enum charset_enum is_os_charset_arg, - const char *def_val, - on_check_function on_check_func=0, - on_update_function on_update_func=0) : - sys_var( - &all_sys_vars, - name_arg, - comment, - flag_args, - off, - getopt.id, - getopt.arg_type, - SHOW_CHAR, - (intptr) def_val, - 0, - VARIABLE_NOT_IN_BINLOG, - on_check_func, - on_update_func, - 0) + Sys_var_vers_asof(const char *name_arg, const char *comment, int flag_args, + ptrdiff_t off, size_t size, CMD_LINE getopt, enum charset_enum is_os_charset_arg, + const char *def_val, on_check_function on_check_func=0, on_update_function on_update_func=0) : + sys_var(&all_sys_vars, name_arg, comment, flag_args, off, getopt.id, getopt.arg_type, + SHOW_CHAR, (intptr) def_val, 0, VARIABLE_NOT_IN_BINLOG, on_check_func, on_update_func, 0) { option.var_type|= GET_STR; if (global_update(def_val)) @@ -2534,31 +2513,18 @@ public: bool update(String &in, st_vers_current_time &out) { - if (in.length() == 3 && - 0 == my_strcasecmp( - in.charset(), - "ALL", - in.ptr())) + if (in.length() == 3 && 0 == my_strcasecmp(in.charset(), "ALL", in.ptr())) { out.type= FOR_SYSTEM_TIME_ALL; } - else if (in.length() == 3 && - 0 == my_strcasecmp( - in.charset(), - "NOW", - in.ptr())) + else if (in.length() == 3 && 0 == my_strcasecmp(in.charset(), "NOW", in.ptr())) { out.type= FOR_SYSTEM_TIME_UNSPECIFIED; } else { MYSQL_TIME_STATUS status; - if (str_to_datetime( - in.ptr(), - in.length(), - &out.ltime, - flags, - &status) || + if (str_to_datetime(in.ptr(), in.length(), &out.ltime, flags, &status) || out.ltime.time_type != MYSQL_TIMESTAMP_DATETIME || (status.warnings & ~MYSQL_TIME_NOTE_TRUNCATED) != 0) { diff --git a/sql/table.h b/sql/table.h index 82d41ff831b..a2ede121064 100644 --- a/sql/table.h +++ b/sql/table.h @@ -561,7 +561,7 @@ struct TABLE_STATISTICS_CB bool histograms_are_read; }; -class Vers_field_stats; +class Vers_min_max_stats; #ifndef UINT32_MAX #define UINT32_MAX (4294967295U) @@ -755,7 +755,7 @@ struct TABLE_SHARE uint16 row_start_field; uint16 row_end_field; uint32 hist_part_id; - Vers_field_stats** stat_trx; + Vers_min_max_stats** stat_trx; ulonglong stat_serial; // guards check_range_constants() updates bool busy_rotation; -- cgit v1.2.1 From 904b69cd9ed15b0d605b9923439e9a66b5057084 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Thu, 7 Sep 2017 15:49:11 +0300 Subject: SQL: partitioning misc fixes [closes #242] * cleanup: *never* use assert(A && B) * vers_setup_1() revisited * vers_setup_2() renamed * partition_element::type removed * Copy ctor instead of memcpy() * Handle return value from check_range_constants() * Malloc error fix * error, style, misc fixes --- sql/ha_partition.cc | 7 +-- sql/ha_partition.h | 5 --- sql/handler.h | 2 +- sql/opt_range.cc | 7 ++- sql/partition_element.h | 37 ++++++++++------ sql/partition_info.cc | 114 ++++++++++++++++++++++++++---------------------- sql/partition_info.h | 32 +++++++------- sql/sql_partition.cc | 10 ++--- sql/sql_yacc.yy | 4 +- sql/table.cc | 2 +- sql/table.h | 4 +- 11 files changed, 121 insertions(+), 103 deletions(-) (limited to 'sql') diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 747b9a8871f..68fffa24a02 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -2022,15 +2022,12 @@ int ha_partition::copy_partitions(ulonglong * const copied, else { THD *thd= ha_thd(); - handler *new_file= m_new_file[new_part]; /* Copy record to new handler */ (*copied)++; - if (new_file->ha_external_lock(thd, F_UNLCK) || new_file->ha_external_lock(thd, F_WRLCK)) - goto error; tmp_disable_binlog(thd); /* Do not replicate the low-level changes. */ - result= new_file->ha_write_row(m_rec0); + result= m_new_file[new_part]->ha_write_row(m_rec0); reenable_binlog(thd); - if (new_file->ha_external_lock(thd, F_UNLCK) || new_file->ha_external_lock(thd, F_RDLCK) || result) + if (result) goto error; } } diff --git a/sql/ha_partition.h b/sql/ha_partition.h index 98e0c1baafe..0eb96aa2d00 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -1355,11 +1355,6 @@ public: return h; } - virtual bool versioned() const - { - return m_innodb; - } - virtual ha_rows part_records(void *_part_elem) { partition_element *part_elem= reinterpret_cast(_part_elem); diff --git a/sql/handler.h b/sql/handler.h index a0c40d2765a..911eb25ba0e 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -4404,7 +4404,7 @@ public: virtual int find_unique_row(uchar *record, uint unique_ref) { return -1; /*unsupported */} - virtual bool versioned() const + bool native_versioned() const { DBUG_ASSERT(ht); return partition_ht()->flags & HTON_NATIVE_SYS_VERSIONING; } virtual ha_rows part_records(void *_part_elem) { DBUG_ASSERT(0); return false; } diff --git a/sql/opt_range.cc b/sql/opt_range.cc index fcf038a212b..53208571ee2 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -3455,9 +3455,11 @@ bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond) DBUG_RETURN(FALSE); } - if (part_info->part_type == VERSIONING_PARTITION) + if (part_info->part_type == VERSIONING_PARTITION && + part_info->vers_update_range_constants(thd)) { - part_info->vers_update_range_constants(thd); + retval= TRUE; + goto end2; } dbug_tmp_use_all_columns(table, old_sets, @@ -3559,6 +3561,7 @@ all_used: mark_all_partitions_as_used(prune_param.part_info); end: dbug_tmp_restore_column_maps(table->read_set, table->write_set, old_sets); +end2: thd->no_errors=0; thd->mem_root= range_par->old_root; free_root(&alloc,MYF(0)); // Return memory & allocator diff --git a/sql/partition_element.h b/sql/partition_element.h index 723d2f21a46..b8b716e5273 100644 --- a/sql/partition_element.h +++ b/sql/partition_element.h @@ -151,6 +151,13 @@ enum stat_trx_field class partition_element :public Sql_alloc { public: + enum elem_type + { + CONVENTIONAL= 0, + AS_OF_NOW, + VERSIONING + }; + List subpartitions; List list_val_list; ha_rows part_max_rows; @@ -172,14 +179,18 @@ public: uint32 id; bool empty; - enum elem_type + // TODO: subclass partition_element by partitioning type to avoid such semantic + // mixup + elem_type type() { - CONVENTIONAL= 0, - AS_OF_NOW, - VERSIONING - }; + return (elem_type)(signed_flag << 1 | max_value); + } - elem_type type; + void type(elem_type val) + { + max_value= val & 1; + signed_flag= val & 2; + } partition_element() : part_max_rows(0), part_min_rows(0), range_value(0), @@ -190,8 +201,7 @@ public: nodegroup_id(UNDEF_NODEGROUP), has_null_value(FALSE), signed_flag(FALSE), max_value(FALSE), id(UINT32_MAX), - empty(true), - type(CONVENTIONAL) + empty(true) {} partition_element(partition_element *part_elem) : part_max_rows(part_elem->part_max_rows), @@ -207,17 +217,16 @@ public: nodegroup_id(part_elem->nodegroup_id), has_null_value(FALSE), id(part_elem->id), - empty(part_elem->empty), - type(part_elem->type) + empty(part_elem->empty) {} ~partition_element() {} part_column_list_val& get_col_val(uint idx) { - DBUG_ASSERT(type != CONVENTIONAL); - DBUG_ASSERT(list_val_list.elements == 1); - part_elem_value *ev= static_cast(list_val_list.first_node()->info); - DBUG_ASSERT(ev && ev->col_val_array); + DBUG_ASSERT(type() == CONVENTIONAL || list_val_list.elements == 1); + part_elem_value *ev= list_val_list.head(); + DBUG_ASSERT(ev); + DBUG_ASSERT(ev->col_val_array); return ev->col_val_array[idx]; } }; diff --git a/sql/partition_info.cc b/sql/partition_info.cc index 8bf2e2cd72d..af97791ab80 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -44,13 +44,12 @@ partition_info *partition_info::get_clone(THD *thd) List_iterator part_it(partitions); partition_element *part; - partition_info *clone= new (mem_root) partition_info(); + partition_info *clone= new (mem_root) partition_info(*this); if (!clone) { mem_alloc_error(sizeof(partition_info)); DBUG_RETURN(NULL); } - memcpy(clone, this, sizeof(partition_info)); memset(&(clone->read_partitions), 0, sizeof(clone->read_partitions)); memset(&(clone->lock_partitions), 0, sizeof(clone->lock_partitions)); clone->bitmaps_are_initialized= FALSE; @@ -913,28 +912,59 @@ partition_info::vers_part_rotate(THD * thd) return vers_info->hist_part; } -bool partition_info::vers_setup_1(THD * thd, uint32 added) +bool partition_info::vers_set_expression(THD *thd, partition_element *el, MYSQL_TIME& t) +{ + curr_part_elem= el; + init_column_part(thd); + el->list_val_list.empty(); + el->list_val_list.push_back(curr_list_val, thd->mem_root); + for (uint i= 0; i < num_columns; ++i) + { + part_column_list_val *col_val= add_column_value(thd); + if (el->type() == partition_element::AS_OF_NOW) + { + col_val->max_value= true; + col_val->item_expression= NULL; + col_val->column_value= NULL; + col_val->part_info= this; + col_val->fixed= 1; + continue; + } + Item *item_expression= new (thd->mem_root) Item_datetime_literal(thd, &t); + if (!item_expression) + return true; + /* We initialize col_val with bogus max value to make fix_partition_func() and check_range_constants() happy. + Later in vers_setup_stats() it is initialized with real stat value if there will be any. */ + /* FIXME: TIME_RESULT in col_val is expensive. It should be INT_RESULT + (got to be fixed when InnoDB is supported). */ + init_col_val(col_val, item_expression); + DBUG_ASSERT(item_expression == el->get_col_val(i).item_expression); + } // for (num_columns) + return false; +} + +bool partition_info::vers_setup_expression(THD * thd, uint32 alter_add) { DBUG_ASSERT(part_type == VERSIONING_PARTITION); if (!table->versioned()) { - my_error(ER_VERSIONING_REQUIRED, MYF(0), "`BY SYSTEM_TIME` partitioning"); + my_error(ER_VERSIONING_REQUIRED, MYF(0), table->s->table_name); return true; } - if (added) + if (alter_add) { - DBUG_ASSERT(partitions.elements > added + 1); + DBUG_ASSERT(partitions.elements > alter_add + 1); Vers_min_max_stats** old_array= table->s->stat_trx; table->s->stat_trx= static_cast( alloc_root(&table->s->mem_root, sizeof(void *) * partitions.elements * num_columns)); - memcpy(table->s->stat_trx, old_array, sizeof(void *) * (partitions.elements - added) * num_columns); + memcpy(table->s->stat_trx, old_array, sizeof(void *) * (partitions.elements - alter_add) * num_columns); } else { + /* Prepare part_field_list */ Field *sys_trx_end= table->vers_end_field(); - part_field_list.empty(); part_field_list.push_back(const_cast(sys_trx_end->field_name), thd->mem_root); DBUG_ASSERT(part_field_list.elements == num_columns); // needed in handle_list_of_fields() @@ -949,26 +979,29 @@ bool partition_info::vers_setup_1(THD * thd, uint32 added) uint32 id= 0; while ((el= it++)) { - DBUG_ASSERT(el->type != partition_element::CONVENTIONAL); + DBUG_ASSERT(el->type() != partition_element::CONVENTIONAL); ++ts; - if (added) + if (alter_add) { - if (el->type == partition_element::VERSIONING && !el->empty) + /* Non-empty historical partitions are left as is. */ + if (el->type() == partition_element::VERSIONING && !el->empty) { ++id; continue; } - if (el->id == UINT32_MAX || el->type == partition_element::AS_OF_NOW) + /* Newly added element is inserted before AS_OF_NOW. */ + if (el->id == UINT32_MAX || el->type() == partition_element::AS_OF_NOW) { DBUG_ASSERT(table && table->s); Vers_min_max_stats *stat_trx_end= new (&table->s->mem_root) Vers_min_max_stats(table->s->vers_end_field()->field_name, table->s); table->s->stat_trx[id * num_columns + STAT_TRX_END]= stat_trx_end; el->id= id++; - if (el->type == partition_element::AS_OF_NOW) + if (el->type() == partition_element::AS_OF_NOW) break; - goto create_col_val; + goto set_expression; } + /* Existing element expression is recalculated. */ thd->variables.time_zone->gmt_sec_to_TIME(&t, ts); for (uint i= 0; i < num_columns; ++i) { @@ -980,32 +1013,10 @@ bool partition_info::vers_setup_1(THD * thd, uint32 added) continue; } - create_col_val: - curr_part_elem= el; - init_column_part(thd); - el->list_val_list.empty(); - el->list_val_list.push_back(curr_list_val, thd->mem_root); + set_expression: thd->variables.time_zone->gmt_sec_to_TIME(&t, ts); - for (uint i= 0; i < num_columns; ++i) - { - part_column_list_val *col_val= add_column_value(thd); - if (el->type == partition_element::AS_OF_NOW) - { - col_val->max_value= true; - col_val->item_expression= NULL; - col_val->column_value= NULL; - col_val->part_info= this; - col_val->fixed= 1; - continue; - } - Item *item_expression= new (thd->mem_root) Item_datetime_literal(thd, &t); - /* We initialize col_val with bogus max value to make fix_partition_func() and check_range_constants() happy. - Later in vers_setup_2() it is initialized with real stat value if there will be any. */ - /* FIXME: TIME_RESULT in col_val is expensive. It should be INT_RESULT - (got to be fixed when InnoDB is supported). */ - init_col_val(col_val, item_expression); - DBUG_ASSERT(item_expression == el->get_col_val(i).item_expression); - } + if (vers_set_expression(thd, el, t)) + return true; } return false; } @@ -1019,7 +1030,7 @@ bool partition_info::vers_scan_min_max(THD *thd, partition_element *part) uint32 part_id= part->id * sub_factor; uint32 part_id_end= part_id + sub_factor; DBUG_ASSERT(part->empty); - DBUG_ASSERT(part->type == partition_element::VERSIONING); + DBUG_ASSERT(part->type() == partition_element::VERSIONING); DBUG_ASSERT(table->s->stat_trx); for (; part_id < part_id_end; ++part_id) { @@ -1141,8 +1152,8 @@ void partition_info::vers_update_col_vals(THD *thd, partition_element *el0, part } -// setup at open stage (TABLE_SHARE is initialized) -bool partition_info::vers_setup_2(THD * thd, bool is_create_table_ind) +// setup at open() phase (TABLE_SHARE is initialized) +bool partition_info::vers_setup_stats(THD * thd, bool is_create_table_ind) { DBUG_ASSERT(part_type == VERSIONING_PARTITION); DBUG_ASSERT(vers_info && vers_info->initialized(false)); @@ -1165,6 +1176,7 @@ bool partition_info::vers_setup_2(THD * thd, bool is_create_table_ind) bool dont_stat= true; bool col_val_updated= false; + // initialize stat_trx if (!table->s->stat_trx) { DBUG_ASSERT(partitions.elements > 1); @@ -1178,7 +1190,7 @@ bool partition_info::vers_setup_2(THD * thd, bool is_create_table_ind) partition_element *el= NULL, *prev; while ((prev= el, el= it++)) { - if (el->type == partition_element::VERSIONING && dont_stat) + if (el->type() == partition_element::VERSIONING && dont_stat) { if (el->id == table->s->hist_part_id) { @@ -1196,7 +1208,7 @@ bool partition_info::vers_setup_2(THD * thd, bool is_create_table_ind) if (!is_create_table_ind) { - if (el->type == partition_element::AS_OF_NOW) + if (el->type() == partition_element::AS_OF_NOW) { uchar buf[8]; Field_timestampf fld(buf, NULL, 0, Field::NONE, table->vers_end_field()->field_name, NULL, 6); @@ -1217,10 +1229,10 @@ bool partition_info::vers_setup_2(THD * thd, bool is_create_table_ind) } } - if (el->type == partition_element::AS_OF_NOW) + if (el->type() == partition_element::AS_OF_NOW) break; - DBUG_ASSERT(el->type == partition_element::VERSIONING); + DBUG_ASSERT(el->type() == partition_element::VERSIONING); if (vers_info->hist_part) { @@ -1435,7 +1447,7 @@ error: called for RANGE PARTITIONed tables. */ -bool partition_info::check_range_constants(THD *thd, bool init) +bool partition_info::check_range_constants(THD *thd, bool alloc) { partition_element* part_def; bool first= TRUE; @@ -1452,7 +1464,7 @@ bool partition_info::check_range_constants(THD *thd, bool init) part_column_list_val *UNINIT_VAR(current_largest_col_val); uint num_column_values= part_field_list.elements; uint size_entries= sizeof(part_column_list_val) * num_column_values; - if (init) + if (alloc) { range_col_array= (part_column_list_val*) thd->calloc(num_parts * size_entries); @@ -1493,7 +1505,7 @@ bool partition_info::check_range_constants(THD *thd, bool init) longlong part_range_value; bool signed_flag= !part_expr->unsigned_flag; - if (init) + if (alloc) { range_int_array= (longlong*) thd->alloc(num_parts * sizeof(longlong)); if (unlikely(range_int_array == NULL)) @@ -2065,13 +2077,13 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type, } if (part_type == VERSIONING_PARTITION) { - if (part_elem->type == partition_element::VERSIONING) + if (part_elem->type() == partition_element::VERSIONING) { hist_parts++; } else { - DBUG_ASSERT(part_elem->type == partition_element::AS_OF_NOW); + DBUG_ASSERT(part_elem->type() == partition_element::AS_OF_NOW); now_parts++; } } diff --git a/sql/partition_info.h b/sql/partition_info.h index d69774e0841..99772f29891 100644 --- a/sql/partition_info.h +++ b/sql/partition_info.h @@ -57,13 +57,12 @@ struct Vers_part_info : public Sql_alloc { if (now_part) { - DBUG_ASSERT( - now_part->id != UINT32_MAX && - now_part->type == partition_element::AS_OF_NOW && - (fully ? (bool) hist_part : true) && - (!hist_part || ( + DBUG_ASSERT(now_part->id != UINT32_MAX); + DBUG_ASSERT(now_part->type() == partition_element::AS_OF_NOW); + DBUG_ASSERT(!fully || (bool) hist_part); + DBUG_ASSERT(!hist_part || ( hist_part->id != UINT32_MAX && - hist_part->type == partition_element::VERSIONING))); + hist_part->type() == partition_element::VERSIONING)); return true; } return false; @@ -352,7 +351,7 @@ public: char *find_duplicate_field(); char *find_duplicate_name(); bool check_engine_mix(handlerton *engine_type, bool default_engine); - bool check_range_constants(THD *thd, bool init= true); + bool check_range_constants(THD *thd, bool alloc= true); bool check_list_constants(THD *thd); bool check_partition_info(THD *thd, handlerton **eng_type, handler *file, HA_CREATE_INFO *info, @@ -426,8 +425,9 @@ public: bool vers_set_interval(const INTERVAL &i); bool vers_set_limit(ulonglong limit); partition_element* vers_part_rotate(THD *thd); - bool vers_setup_1(THD *thd, uint32 added= 0); - bool vers_setup_2(THD *thd, bool is_create_table_ind); + bool vers_set_expression(THD *thd, partition_element *el, MYSQL_TIME &t); + bool vers_setup_expression(THD *thd, uint32 alter_add= 0); /* Stage 1. */ + bool vers_setup_stats(THD *thd, bool is_create_table_ind); /* Stage 2. */ bool vers_scan_min_max(THD *thd, partition_element *part); void vers_update_col_vals(THD *thd, partition_element *el0, partition_element *el1); @@ -443,8 +443,8 @@ public: partition_element *el; while ((el= it++)) { - DBUG_ASSERT(el->type != partition_element::CONVENTIONAL); - if (el->type == partition_element::VERSIONING && + DBUG_ASSERT(el->type() != partition_element::CONVENTIONAL); + if (el->type() == partition_element::VERSIONING && el->id == table->s->hist_part_id) { vers_info->hist_part= el; @@ -516,7 +516,7 @@ public: { DBUG_ASSERT(vers_info && vers_info->initialized()); DBUG_ASSERT(table && table->s); - DBUG_ASSERT(el && el->type == partition_element::VERSIONING); + DBUG_ASSERT(el && el->type() == partition_element::VERSIONING); bool updated; mysql_rwlock_wrlock(&table->s->LOCK_stat_serial); el->empty= false; @@ -552,7 +552,7 @@ public: if (part_id < vers_info->now_part->id) vers_update_stats(thd, get_partition(part_id)); } - void vers_update_range_constants(THD *thd) + bool vers_update_range_constants(THD *thd) { DBUG_ASSERT(vers_info && vers_info->initialized()); DBUG_ASSERT(table && table->s); @@ -561,17 +561,19 @@ public: if (vers_info->stat_serial == table->s->stat_serial) { mysql_rwlock_unlock(&table->s->LOCK_stat_serial); - return; + return false; } + bool result= false; for (uint i= 0; i < num_columns; ++i) { Field *f= part_field_array[i]; bitmap_set_bit(f->table->write_set, f->field_index); } - check_range_constants(thd, false); + result= check_range_constants(thd, false); vers_info->stat_serial= table->s->stat_serial; mysql_rwlock_unlock(&table->s->LOCK_stat_serial); + return result; } }; diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 7278b56a017..08ba4d84d9b 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -1689,7 +1689,7 @@ bool fix_partition_func(THD *thd, TABLE *table, if (part_info->column_list) { if (part_info->part_type == VERSIONING_PARTITION && - part_info->vers_setup_1(thd)) + part_info->vers_setup_expression(thd)) goto end; List_iterator it(part_info->part_field_list); if (unlikely(handle_list_of_fields(thd, it, table, part_info, FALSE))) @@ -2392,7 +2392,7 @@ static int add_partition_values(File fptr, partition_info *part_info, } else if (part_info->part_type == VERSIONING_PARTITION) { - switch (p_elem->type) + switch (p_elem->type()) { case partition_element::AS_OF_NOW: err+= add_string(fptr, " AS OF NOW"); @@ -5300,7 +5300,7 @@ that are reorganised. partition_element *el; while ((el= it++)) { - if (el->type == partition_element::AS_OF_NOW) + if (el->type() == partition_element::AS_OF_NOW) { DBUG_ASSERT(tab_part_info->vers_info && el == tab_part_info->vers_info->now_part); it.remove(); @@ -5393,7 +5393,7 @@ that are reorganised. if (is_name_in_list(part_elem->partition_name, alter_info->partition_names)) { - if (part_elem->type == partition_element::AS_OF_NOW) + if (part_elem->type() == partition_element::AS_OF_NOW) { DBUG_ASSERT(table && table->s && table->s->table_name.str); my_error(ER_VERS_WRONG_PARTS, MYF(0), table->s->table_name.str); @@ -5724,7 +5724,7 @@ the generated partition syntax in a correct manner. if (alter_info->flags & Alter_info::ALTER_ADD_PARTITION && tab_part_info->part_type == VERSIONING_PARTITION && - tab_part_info->vers_setup_1(thd, alt_part_info->partitions.elements)) + tab_part_info->vers_setup_expression(thd, alt_part_info->partitions.elements)) goto err; if (tab_part_info->check_partition_info(thd, (handlerton**)NULL, diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index ea7eb443896..1f0ce5fd6a0 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -5212,7 +5212,7 @@ opt_part_values: my_yyabort_error((ER_VERS_WRONG_PARTS, MYF(0), Lex->create_last_non_select_table->table_name)); } - elem->type= partition_element::AS_OF_NOW; + elem->type(partition_element::AS_OF_NOW); DBUG_ASSERT(part_info->vers_info); part_info->vers_info->now_part= elem; if (part_info->init_column_part(thd)) @@ -5243,7 +5243,7 @@ opt_part_values: DBUG_ASSERT(Lex->create_last_non_select_table->table_name); my_yyabort_error((ER_VERS_WRONG_PARTS, MYF(0), Lex->create_last_non_select_table->table_name)); } - elem->type= partition_element::VERSIONING; + elem->type(partition_element::VERSIONING); if (part_info->init_column_part(thd)) { MYSQL_YYABORT; diff --git a/sql/table.cc b/sql/table.cc index 902e6ea0224..07d912c4045 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -3455,7 +3455,7 @@ partititon_err: thd->stmt_arena= &part_func_arena; } - bool err= outparam->part_info->vers_setup_2(thd, is_create_table); + bool err= outparam->part_info->vers_setup_stats(thd, is_create_table); if (!work_part_info_used) { diff --git a/sql/table.h b/sql/table.h index a2ede121064..d857ba4752b 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1521,13 +1521,13 @@ public: bool versioned_by_sql() const { DBUG_ASSERT(s && file); - return s->versioned && !file->versioned(); + return s->versioned && !file->native_versioned(); } bool versioned_by_engine() const { DBUG_ASSERT(s && file); - return s->versioned && file->versioned(); + return s->versioned && file->native_versioned(); } Field *vers_start_field() const -- cgit v1.2.1 From a49239b57a5e46f118a7ab46b8f298b4f596d6ec Mon Sep 17 00:00:00 2001 From: Eugene Kosov Date: Fri, 8 Sep 2017 10:22:24 +0300 Subject: SQL: truncate syntax and privilege [closes #229] --- sql/sql_acl.cc | 19 ++++++++++++++----- sql/sql_acl.h | 27 ++++++++++++++++++--------- sql/sql_delete.cc | 11 ++--------- sql/sql_show.cc | 1 + sql/sql_truncate.cc | 5 ++++- sql/sql_yacc.yy | 22 ++++++++++++---------- sql/vtq.h | 0 7 files changed, 51 insertions(+), 34 deletions(-) mode change 100755 => 100644 sql/vtq.h (limited to 'sql') diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 09781f4bbe3..28422c53426 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -170,6 +170,11 @@ TABLE_FIELD_TYPE mysql_db_table_fields[MYSQL_DB_FIELD_COUNT] = { { C_STRING_WITH_LEN("Trigger_priv") }, { C_STRING_WITH_LEN("enum('N','Y')") }, { C_STRING_WITH_LEN("utf8") } + }, + { + { C_STRING_WITH_LEN("Delete_versioning_rows_priv") }, + { C_STRING_WITH_LEN("enum('N','Y')") }, + { C_STRING_WITH_LEN("utf8") } } }; @@ -695,9 +700,9 @@ bool ROLE_GRANT_PAIR::init(MEM_ROOT *mem, char *username, #endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */ #define NORMAL_HANDSHAKE_SIZE 6 -#define ROLE_ASSIGN_COLUMN_IDX 43 -#define DEFAULT_ROLE_COLUMN_IDX 44 -#define MAX_STATEMENT_TIME_COLUMN_IDX 45 +#define ROLE_ASSIGN_COLUMN_IDX 44 +#define DEFAULT_ROLE_COLUMN_IDX 45 +#define MAX_STATEMENT_TIME_COLUMN_IDX 46 /* various flags valid for ACL_USER */ #define IS_ROLE (1L << 0) @@ -2004,6 +2009,9 @@ static bool acl_load(THD *thd, const Grant_tables& tables) if (user_table.num_fields() <= 38 && (user.access & SUPER_ACL)) user.access|= TRIGGER_ACL; + if (user_table.num_fields() <= 46 && (user.access & DELETE_ACL)) + user.access|= DELETE_VERSIONING_ROWS_ACL; + user.sort= get_sort(2, user.host.hostname, user.user.str); user.hostname_length= safe_strlen(user.host.hostname); user.user_resource.user_conn= 0; @@ -8422,13 +8430,14 @@ static const char *command_array[]= "ALTER", "SHOW DATABASES", "SUPER", "CREATE TEMPORARY TABLES", "LOCK TABLES", "EXECUTE", "REPLICATION SLAVE", "REPLICATION CLIENT", "CREATE VIEW", "SHOW VIEW", "CREATE ROUTINE", "ALTER ROUTINE", - "CREATE USER", "EVENT", "TRIGGER", "CREATE TABLESPACE" + "CREATE USER", "EVENT", "TRIGGER", "CREATE TABLESPACE", + "DELETE VERSIONING ROWS" }; static uint command_lengths[]= { 6, 6, 6, 6, 6, 4, 6, 8, 7, 4, 5, 10, 5, 5, 14, 5, 23, 11, 7, 17, 18, 11, 9, - 14, 13, 11, 5, 7, 17 + 14, 13, 11, 5, 7, 17, 22, }; diff --git a/sql/sql_acl.h b/sql/sql_acl.h index daa36f6c32a..dbfab1cac21 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -50,6 +50,7 @@ #define EVENT_ACL (1UL << 26) #define TRIGGER_ACL (1UL << 27) #define CREATE_TABLESPACE_ACL (1UL << 28) +#define DELETE_VERSIONING_ROWS_ACL (1UL << 29) /* don't forget to update 1. static struct show_privileges_st sys_privileges[] @@ -63,12 +64,13 @@ (UPDATE_ACL | SELECT_ACL | INSERT_ACL | DELETE_ACL | CREATE_ACL | DROP_ACL | \ GRANT_ACL | REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL | \ LOCK_TABLES_ACL | EXECUTE_ACL | CREATE_VIEW_ACL | SHOW_VIEW_ACL | \ - CREATE_PROC_ACL | ALTER_PROC_ACL | EVENT_ACL | TRIGGER_ACL) + CREATE_PROC_ACL | ALTER_PROC_ACL | EVENT_ACL | TRIGGER_ACL | \ + DELETE_VERSIONING_ROWS_ACL) #define TABLE_ACLS \ (SELECT_ACL | INSERT_ACL | UPDATE_ACL | DELETE_ACL | CREATE_ACL | DROP_ACL | \ GRANT_ACL | REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_VIEW_ACL | \ - SHOW_VIEW_ACL | TRIGGER_ACL) + SHOW_VIEW_ACL | TRIGGER_ACL | DELETE_VERSIONING_ROWS_ACL) #define COL_ACLS \ (SELECT_ACL | INSERT_ACL | UPDATE_ACL | REFERENCES_ACL) @@ -86,7 +88,7 @@ CREATE_TMP_ACL | LOCK_TABLES_ACL | REPL_SLAVE_ACL | REPL_CLIENT_ACL | \ EXECUTE_ACL | CREATE_VIEW_ACL | SHOW_VIEW_ACL | CREATE_PROC_ACL | \ ALTER_PROC_ACL | CREATE_USER_ACL | EVENT_ACL | TRIGGER_ACL | \ - CREATE_TABLESPACE_ACL) + CREATE_TABLESPACE_ACL | DELETE_VERSIONING_ROWS_ACL) #define DEFAULT_CREATE_PROC_ACLS \ (ALTER_PROC_ACL | EXECUTE_ACL) @@ -118,31 +120,37 @@ CREATE_PROC_ACL | ALTER_PROC_ACL ) #define DB_CHUNK4 (EXECUTE_ACL) #define DB_CHUNK5 (EVENT_ACL | TRIGGER_ACL) +#define DB_CHUNK6 (DELETE_VERSIONING_ROWS_ACL) #define fix_rights_for_db(A) (((A) & DB_CHUNK0) | \ (((A) << 4) & DB_CHUNK1) | \ (((A) << 6) & DB_CHUNK2) | \ (((A) << 9) & DB_CHUNK3) | \ - (((A) << 2) & DB_CHUNK4))| \ - (((A) << 9) & DB_CHUNK5) + (((A) << 2) & DB_CHUNK4) | \ + (((A) << 9) & DB_CHUNK5) | \ + (((A) << 10) & DB_CHUNK6)) #define get_rights_for_db(A) (((A) & DB_CHUNK0) | \ (((A) & DB_CHUNK1) >> 4) | \ (((A) & DB_CHUNK2) >> 6) | \ (((A) & DB_CHUNK3) >> 9) | \ - (((A) & DB_CHUNK4) >> 2))| \ - (((A) & DB_CHUNK5) >> 9) + (((A) & DB_CHUNK4) >> 2) | \ + (((A) & DB_CHUNK5) >> 9) | \ + (((A) & DB_CHUNK6) >> 10)) #define TBL_CHUNK0 DB_CHUNK0 #define TBL_CHUNK1 DB_CHUNK1 #define TBL_CHUNK2 (CREATE_VIEW_ACL | SHOW_VIEW_ACL) #define TBL_CHUNK3 TRIGGER_ACL +#define TBL_CHUNK4 (DELETE_VERSIONING_ROWS_ACL) #define fix_rights_for_table(A) (((A) & TBL_CHUNK0) | \ (((A) << 4) & TBL_CHUNK1) | \ (((A) << 11) & TBL_CHUNK2) | \ - (((A) << 15) & TBL_CHUNK3)) + (((A) << 15) & TBL_CHUNK3) | \ + (((A) << 16) & TBL_CHUNK4)) #define get_rights_for_table(A) (((A) & TBL_CHUNK0) | \ (((A) & TBL_CHUNK1) >> 4) | \ (((A) & TBL_CHUNK2) >> 11) | \ - (((A) & TBL_CHUNK3) >> 15)) + (((A) & TBL_CHUNK3) >> 15) | \ + (((A) & TBL_CHUNK4) >> 16)) #define fix_rights_for_column(A) (((A) & 7) | (((A) & ~7) << 8)) #define get_rights_for_column(A) (((A) & 7) | ((A) >> 8)) #define fix_rights_for_procedure(A) ((((A) << 18) & EXECUTE_ACL) | \ @@ -176,6 +184,7 @@ enum mysql_db_table_field MYSQL_DB_FIELD_EXECUTE_PRIV, MYSQL_DB_FIELD_EVENT_PRIV, MYSQL_DB_FIELD_TRIGGER_PRIV, + MYSQL_DB_FIELD_DELETE_VERSIONING_ROWS_PRIV, MYSQL_DB_FIELD_COUNT }; diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 8dc6203ed86..2d56f66dac7 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -275,15 +275,8 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, // trx_sees() in InnoDB reads sys_trx_start if (!table->versioned_by_sql()) { - if (table_list->vers_conditions.type == FOR_SYSTEM_TIME_BETWEEN || - table_list->vers_conditions.type == FOR_SYSTEM_TIME_FROM_TO) - { - bitmap_set_bit(table->read_set, table->vers_start_field()->field_index); - } - else if (table_list->vers_conditions.type == FOR_SYSTEM_TIME_BEFORE) - { - bitmap_set_bit(table->read_set, table->vers_end_field()->field_index); - } + DBUG_ASSERT(table_list->vers_conditions.type == FOR_SYSTEM_TIME_BEFORE); + bitmap_set_bit(table->read_set, table->vers_end_field()->field_index); } } diff --git a/sql/sql_show.cc b/sql/sql_show.cc index d36d222fb07..30fd1b43e4c 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -564,6 +564,7 @@ static struct show_privileges_st sys_privileges[]= {"Create view", "Tables", "To create new views"}, {"Create user", "Server Admin", "To create new users"}, {"Delete", "Tables", "To delete existing rows"}, + {"Delete versioning rows", "Tables", "To delete versioning table historical rows"}, {"Drop", "Databases,Tables", "To drop databases, tables, and views"}, #ifdef HAVE_EVENT_SCHEDULER {"Event","Server Admin","To create, alter, drop and execute events"}, diff --git a/sql/sql_truncate.cc b/sql/sql_truncate.cc index b65818a2716..c428491cfa3 100644 --- a/sql/sql_truncate.cc +++ b/sql/sql_truncate.cc @@ -495,9 +495,12 @@ bool Sql_cmd_truncate_table::execute(THD *thd) TABLE_LIST *table= thd->lex->select_lex.table_list.first; DBUG_ENTER("Sql_cmd_truncate_table::execute"); - DBUG_ASSERT(table); if (table->vers_conditions) + { + if (check_one_table_access(thd, DELETE_VERSIONING_ROWS_ACL, table)) + DBUG_RETURN(res); DBUG_RETURN(mysql_delete(thd, table, NULL, NULL, -1, 0, NULL)); + } if (check_one_table_access(thd, DROP_ACL, table)) DBUG_RETURN(res); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 1f0ce5fd6a0..0770913e521 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -8903,12 +8903,6 @@ system_time_expr: { Lex->vers_conditions.init(FOR_SYSTEM_TIME_BETWEEN, $2, $3, $5, $6); } - | BEFORE_SYM - opt_trans_or_timestamp - simple_expr - { - Lex->vers_conditions.init(FOR_SYSTEM_TIME_BEFORE, $2, $3); - } ; select_option_list: @@ -13025,8 +13019,17 @@ opt_delete_option: | IGNORE_SYM { Lex->ignore= 1; } ; +truncate_end: + opt_lock_wait_timeout + | TO_SYM SYSTEM_TIME_SYM opt_trans_or_timestamp simple_expr + { + Lex->vers_conditions.init(FOR_SYSTEM_TIME_BEFORE, $3, $4); + Lex->last_table()->vers_conditions= Lex->vers_conditions; + } + ; + truncate: - TRUNCATE_SYM opt_table_sym + TRUNCATE_SYM { LEX* lex= Lex; lex->sql_command= SQLCOM_TRUNCATE; @@ -13037,15 +13040,13 @@ truncate: YYPS->m_lock_type= TL_WRITE; YYPS->m_mdl_type= MDL_EXCLUSIVE; } - table_name opt_for_system_time_clause opt_lock_wait_timeout + opt_table_sym table_name truncate_end { LEX* lex= thd->lex; DBUG_ASSERT(!lex->m_sql_cmd); lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_truncate_table(); if (lex->m_sql_cmd == NULL) MYSQL_YYABORT; - if ($5) - Lex->last_table()->vers_conditions= Lex->vers_conditions; } ; @@ -16076,6 +16077,7 @@ object_privilege: | EVENT_SYM { Lex->grant |= EVENT_ACL;} | TRIGGER_SYM { Lex->grant |= TRIGGER_ACL; } | CREATE TABLESPACE { Lex->grant |= CREATE_TABLESPACE_ACL; } + | DELETE_SYM VERSIONING_SYM ROWS_SYM { Lex->grant |= DELETE_VERSIONING_ROWS_ACL; } ; opt_and: diff --git a/sql/vtq.h b/sql/vtq.h old mode 100755 new mode 100644 -- cgit v1.2.1 From 7e764ae188efe3f866222eb393513bf30e2b75ab Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Wed, 13 Sep 2017 10:57:23 +0300 Subject: SQL: 1-row partition rotation fix [fixes #260] --- sql/ha_partition.cc | 15 ++++++++------- sql/ha_partition.h | 2 +- sql/sql_partition.cc | 22 +++++++++++++++++----- 3 files changed, 26 insertions(+), 13 deletions(-) (limited to 'sql') diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 68fffa24a02..efdbf6f7e18 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -3550,7 +3550,7 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) m_part_info->part_expr->get_monotonicity_info(); else if (m_part_info->list_of_part_fields) m_part_func_monotonicity_info= MONOTONIC_STRICT_INCREASING; - info(HA_STATUS_VARIABLE | HA_STATUS_CONST); + info(HA_STATUS_OPEN | HA_STATUS_VARIABLE | HA_STATUS_CONST); DBUG_RETURN(0); err_handler: @@ -6550,6 +6550,7 @@ int ha_partition::info(uint flag) { uint no_lock_flag= flag & HA_STATUS_NO_LOCK; uint extra_var_flag= flag & HA_STATUS_VARIABLE_EXTRA; + uint open_flag= flag & HA_STATUS_OPEN; DBUG_ENTER("ha_partition::info"); #ifndef DBUG_OFF @@ -6590,7 +6591,7 @@ int ha_partition::info(uint flag) do { file= *file_array; - file->info(HA_STATUS_AUTO | no_lock_flag); + file->info(HA_STATUS_AUTO | no_lock_flag | open_flag); set_if_bigger(auto_increment_value, file->stats.auto_increment_value); } while (*(++file_array)); @@ -6644,7 +6645,7 @@ int ha_partition::info(uint flag) i= bitmap_get_next_set(&m_part_info->read_partitions, i)) { file= m_file[i]; - file->info(HA_STATUS_VARIABLE | no_lock_flag | extra_var_flag); + file->info(HA_STATUS_VARIABLE | no_lock_flag | extra_var_flag | open_flag); stats.records+= file->stats.records; stats.deleted+= file->stats.deleted; stats.data_file_length+= file->stats.data_file_length; @@ -6725,7 +6726,7 @@ int ha_partition::info(uint flag) if (!(flag & HA_STATUS_VARIABLE) || !bitmap_is_set(&(m_part_info->read_partitions), (file_array - m_file))) - file->info(HA_STATUS_VARIABLE | no_lock_flag | extra_var_flag); + file->info(HA_STATUS_VARIABLE | no_lock_flag | extra_var_flag | open_flag); if (file->stats.records > max_records) { max_records= file->stats.records; @@ -6744,7 +6745,7 @@ int ha_partition::info(uint flag) this); file= m_file[handler_instance]; - file->info(HA_STATUS_CONST | no_lock_flag); + file->info(HA_STATUS_CONST | no_lock_flag | open_flag); stats.block_size= file->stats.block_size; stats.create_time= file->stats.create_time; ref_length= m_ref_length; @@ -6760,7 +6761,7 @@ int ha_partition::info(uint flag) Note: all engines does not support HA_STATUS_ERRKEY, so set errkey. */ file->errkey= errkey; - file->info(HA_STATUS_ERRKEY | no_lock_flag); + file->info(HA_STATUS_ERRKEY | no_lock_flag | open_flag); errkey= file->errkey; } if (flag & HA_STATUS_TIME) @@ -6777,7 +6778,7 @@ int ha_partition::info(uint flag) do { file= *file_array; - file->info(HA_STATUS_TIME | no_lock_flag); + file->info(HA_STATUS_TIME | no_lock_flag | open_flag); if (file->stats.update_time > stats.update_time) stats.update_time= file->stats.update_time; } while (*(++file_array)); diff --git a/sql/ha_partition.h b/sql/ha_partition.h index 0eb96aa2d00..df5a3544a30 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -1368,7 +1368,7 @@ public: { handler *file= m_file[part_id]; DBUG_ASSERT(bitmap_is_set(&(m_part_info->read_partitions), part_id)); - file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); + file->info(HA_STATUS_OPEN | HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); part_recs+= file->stats.records; } return part_recs; diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 08ba4d84d9b..051249db997 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -3426,11 +3426,21 @@ int vers_get_partition_id(partition_info *part_info, DBUG_ASSERT(part_info); Field *sys_trx_end= part_info->part_field_array[STAT_TRX_END]; DBUG_ASSERT(sys_trx_end); - DBUG_ASSERT(part_info->table); + TABLE *table= part_info->table; + DBUG_ASSERT(table); Vers_part_info *vers_info= part_info->vers_info; - DBUG_ASSERT(vers_info && vers_info->initialized()); - DBUG_ASSERT(sys_trx_end->table == part_info->table && part_info->table->versioned()); - DBUG_ASSERT(part_info->table->vers_end_field() == sys_trx_end); + DBUG_ASSERT(vers_info); + DBUG_ASSERT(vers_info->initialized()); + DBUG_ASSERT(sys_trx_end->table == table); + bool tmp_off= false; + if (!table->versioned() && table->file->native_versioned()) + { + // in copy_data_between_tables() versioning may be temporarily turned off + tmp_off= true; + table->s->versioned= true; + } + DBUG_ASSERT(table->versioned()); + DBUG_ASSERT(table->vers_end_field() == sys_trx_end); // new rows have NULL in sys_trx_end if (sys_trx_end->is_max() || sys_trx_end->is_null()) @@ -3440,7 +3450,6 @@ int vers_get_partition_id(partition_info *part_info, else // row is historical { THD *thd= current_thd; - TABLE *table= part_info->table; switch (thd->lex->sql_command) { @@ -3478,6 +3487,9 @@ int vers_get_partition_id(partition_info *part_info, *part_id= vers_info->hist_part->id; } + if (tmp_off) + table->s->versioned= false; + DBUG_PRINT("exit",("partition: %d", *part_id)); DBUG_RETURN(0); } -- cgit v1.2.1 From 9ba635fda3cddf94223ec4800858660f007b37fe Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Thu, 14 Sep 2017 13:56:24 +0300 Subject: SQL: VIEW with UNION fix [fixes #269] --- sql/sql_select.cc | 4 ++++ sql/sql_view.cc | 8 +++++--- 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'sql') diff --git a/sql/sql_select.cc b/sql/sql_select.cc index a5f1e3c9626..9a9dbb2893e 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -17079,6 +17079,10 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List &fields, share->row_start_field= sys_trx_start->field_index; share->row_end_field= sys_trx_end->field_index; } + else + { + DBUG_ASSERT(!sys_trx_start && !sys_trx_end); + } DBUG_ASSERT(fieldnr == (uint) (reg_field - table->field)); DBUG_ASSERT(field_count >= (uint) (reg_field - table->field)); diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 4626a0369fb..179026ce80a 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -453,10 +453,12 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, goto err; } + for (SELECT_LEX *sl= select_lex; sl; sl= sl->next_select()) { /* System Versioning: fix system fields of versioned view */ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wformat" #pragma GCC diagnostic ignored "-Wformat-extra-args" + // Similar logic as in mysql_derived_prepare() // Leading versioning table detected implicitly (first one selected) TABLE_LIST *impli_table= NULL; // Leading versioning table specified explicitly @@ -498,7 +500,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, /* Implicitly add versioning fields if needed */ Item *item; - List_iterator_fast it(select_lex->item_list); + List_iterator_fast it(sl->item_list); DBUG_ASSERT(table->alias); while ((item= it++)) @@ -577,9 +579,9 @@ expli_table_err: if (impli_table) { - if (!expli_start && select_lex->vers_push_field(thd, impli_table, impli_start)) + if (!expli_start && sl->vers_push_field(thd, impli_table, impli_start)) goto err; - if (!expli_end && select_lex->vers_push_field(thd, impli_table, impli_end)) + if (!expli_end && sl->vers_push_field(thd, impli_table, impli_end)) goto err; } #pragma GCC diagnostic pop -- cgit v1.2.1 From 6c9b71d734cf666e9ddb44bb6a7766c224dc90b3 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Wed, 20 Sep 2017 13:07:15 +0300 Subject: SQL: VTMD for OR REPLACE [fixes #270] --- sql/mysqld.h | 2 +- sql/sql_table.cc | 52 ++++++++++++++++++++++++++++++++-------------------- sql/vtmd.cc | 7 ++++--- 3 files changed, 37 insertions(+), 24 deletions(-) (limited to 'sql') diff --git a/sql/mysqld.h b/sql/mysqld.h index a30612067ba..47776b8288c 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -211,7 +211,7 @@ enum vers_hide_enum enum vers_alter_history_enum { - VERS_ALTER_HISTORY_KEEP, + VERS_ALTER_HISTORY_KEEP= 0, VERS_ALTER_HISTORY_SURVIVE, VERS_ALTER_HISTORY_DROP }; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 1831eecbd26..3866e661f72 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -2438,6 +2438,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, else { char *end; + int frm_delete_error= 0; /* It could happen that table's share in the table definition cache is the only thing that keeps the engine plugin loaded @@ -2476,7 +2477,8 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, // Remove extension for delete *(end= path + path_length - reg_ext_length)= '\0'; - if (thd->lex->sql_command == SQLCOM_DROP_TABLE && + if ((thd->lex->sql_command == SQLCOM_DROP_TABLE || + thd->lex->sql_command == SQLCOM_CREATE_TABLE) && thd->variables.vers_alter_history == VERS_ALTER_HISTORY_SURVIVE && table_type && table_type != view_pseudo_hton) { @@ -2493,29 +2495,33 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, drop_table: error= ha_delete_table(thd, table_type, path, db, table->table_name, !dont_log_query); + if (!error) + { + /* Delete the table definition file */ + strmov(end,reg_ext); + if (table_type && table_type != view_pseudo_hton && + table_type->discover_table) + { + /* + Table type is using discovery and may not need a .frm file. + Delete it silently if it exists + */ + (void) mysql_file_delete(key_file_frm, path, MYF(0)); + } + else if (mysql_file_delete(key_file_frm, path, + MYF(MY_WME))) + { + frm_delete_error= my_errno; + DBUG_ASSERT(frm_delete_error); + } + } } if (!error) { - int frm_delete_error, trigger_drop_error= 0; - /* Delete the table definition file */ - strmov(end,reg_ext); - if (table_type && table_type != view_pseudo_hton && - table_type->discover_table) - { - /* - Table type is using discovery and may not need a .frm file. - Delete it silently if it exists - */ - (void) mysql_file_delete(key_file_frm, path, MYF(0)); - frm_delete_error= 0; - } - else - frm_delete_error= mysql_file_delete(key_file_frm, path, - MYF(MY_WME)); - if (frm_delete_error) - frm_delete_error= my_errno; - else + int trigger_drop_error= 0; + + if (!frm_delete_error) { non_tmp_table_deleted= TRUE; trigger_drop_error= @@ -2534,7 +2540,10 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, if (!error && vtmd.exists) { + enum_sql_command sql_command= thd->lex->sql_command; + thd->lex->sql_command= SQLCOM_DROP_TABLE; error= vtmd.update(thd); + thd->lex->sql_command= sql_command; if (error) mysql_rename_table(table_type, table->db, vtmd.archive_name(), table->db, table->table_name, NO_FK_CHECKS); @@ -5081,6 +5090,9 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table, VTMD_table vtmd(*create_table); if (vtmd.update(thd)) { + thd->variables.vers_alter_history = VERS_ALTER_HISTORY_KEEP; + mysql_rm_table_no_locks(thd, create_table, 0, 0, 0, 0, 1, 1); + thd->variables.vers_alter_history = VERS_ALTER_HISTORY_SURVIVE; result= 1; goto err; } diff --git a/sql/vtmd.cc b/sql/vtmd.cc index 83a6fb512ef..58d9bb311bb 100644 --- a/sql/vtmd.cc +++ b/sql/vtmd.cc @@ -237,10 +237,9 @@ VTMD_table::update(THD *thd, const char* archive_name) { err: vtmd->file->print_error(error, MYF(0)); - goto quit; } - - result= false; + else + result= local_da.is_error(); } quit: @@ -436,6 +435,8 @@ VTMD_rename::try_rename(THD *thd, LString new_db, LString new_alias, const char if (lock_table_names(thd, &vtmd_tl, 0, thd->variables.lock_wait_timeout, 0)) return true; tdc_remove_table(thd, TDC_RT_REMOVE_ALL, about.db, vtmd_name, false); + if (local_da.is_error()) // just safety check + return true; bool rc= mysql_rename_table(hton, about.db, vtmd_name, new_db, vtmd_new_name, -- cgit v1.2.1 From 3b7c10f0b75744774f1d39a787be25e97d15272d Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Wed, 20 Sep 2017 14:32:48 +0300 Subject: Style: garbage Copy_field member --- sql/field.h | 1 - 1 file changed, 1 deletion(-) (limited to 'sql') diff --git a/sql/field.h b/sql/field.h index 219f88ced0a..dc2db41c43e 100644 --- a/sql/field.h +++ b/sql/field.h @@ -4258,7 +4258,6 @@ public: uint from_length,to_length; Field *from_field,*to_field; String tmp; // For items - MYSQL_TIME now; // Used for sys_trx_start Copy_field() {} ~Copy_field() {} -- cgit v1.2.1 From 7f2064780cb8960697bef17c7ae63be565690eec Mon Sep 17 00:00:00 2001 From: Eugene Kosov Date: Fri, 22 Sep 2017 22:09:41 +0300 Subject: SQL: versioning_alter_history values description --- sql/sys_vars.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 25437f7b3b4..3dc5706b9b2 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -409,7 +409,10 @@ static Sys_var_mybool Sys_vers_innodb_algorithm_simple( static const char *vers_alter_history_keywords[]= {"KEEP", "SURVIVE", "DROP", NULL}; static Sys_var_enum Sys_vers_alter_history( - "versioning_alter_history", "Versioning ALTER TABLE mode", + "versioning_alter_history", "Versioning ALTER TABLE mode. " + "KEEP: leave historical system rows as is on ALTER TABLE; " + "SURVIVE: use DDL survival feature; " + "DROP: delete historical system rows on ALTER TABLE", SESSION_VAR(vers_alter_history), CMD_LINE(OPT_ARG), vers_alter_history_keywords, DEFAULT(VERS_ALTER_HISTORY_KEEP)); -- cgit v1.2.1 From f79c4469ff8a17dee54f41786b76197dc2e17626 Mon Sep 17 00:00:00 2001 From: Eugene Kosov Date: Mon, 25 Sep 2017 11:29:16 +0300 Subject: SQL: helpers to get archive tables list [closes #199] --- sql/sql_show.cc | 2 +- sql/sql_show.h | 3 +++ sql/vtmd.cc | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ sql/vtmd.h | 4 ++++ 4 files changed, 79 insertions(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 30fd1b43e4c..ac340892505 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -4218,7 +4218,7 @@ int schema_tables_add(THD *thd, Dynamic_array *files, @retval 2 Not fatal error; Safe to ignore this file list */ -static int +int make_table_name_list(THD *thd, Dynamic_array *table_names, LEX *lex, LOOKUP_FIELD_VALUES *lookup_field_vals, LEX_STRING *db_name) diff --git a/sql/sql_show.h b/sql/sql_show.h index e93b855450c..03ff539e959 100644 --- a/sql/sql_show.h +++ b/sql/sql_show.h @@ -199,6 +199,9 @@ typedef struct st_lookup_field_values bool wild_table_value; } LOOKUP_FIELD_VALUES; +int make_table_name_list(THD *thd, Dynamic_array *table_names, + LEX *lex, LOOKUP_FIELD_VALUES *lookup_field_vals, + LEX_STRING *db_name); /* INFORMATION_SCHEMA: Execution plan for get_all_tables() call diff --git a/sql/vtmd.cc b/sql/vtmd.cc index 58d9bb311bb..aa9245570f4 100644 --- a/sql/vtmd.cc +++ b/sql/vtmd.cc @@ -6,6 +6,7 @@ #include "sql_select.h" #include "table_cache.h" // tdc_remove_table() #include "key.h" +#include "sql_show.h" LString VERS_VTMD_TEMPLATE(C_STRING_WITH_LEN("vtmd_template")); @@ -573,3 +574,73 @@ err: close_log_table(thd, &open_tables_backup); return error ? true : false; } + +Dynamic_array VTMD_table::get_archive_tables(THD *thd) +{ + Dynamic_array result; + + Dynamic_array vtmd_tables= get_vtmd_tables(thd); + for (uint i= 0; i < vtmd_tables.elements(); i++) + { + LEX_STRING table_name= *vtmd_tables.at(i); + + Open_tables_backup open_tables_backup; + TABLE_LIST table_list; + // Assume VTMD tables belongs to current db. + table_list.init_one_table(thd->db, strlen(thd->db), + LEX_STRING_WITH_LEN(table_name), table_name.str, + TL_READ); + + TABLE *table= open_log_table(thd, &table_list, &open_tables_backup); + if (!table) + return result; + + READ_RECORD read_record; + int error= 0; + SQL_SELECT *sql_select= make_select(table, 0, 0, NULL, NULL, 0, &error); + if (error) + goto error1; + if (error = init_read_record(&read_record, thd, table, sql_select, NULL, 1, 1, + false)) + goto error2; + + while (!(error= read_record.read_record(&read_record))) + { + Field *field= table->field[FLD_ARCHIVE_NAME]; + if (field->is_null()) + continue; + + String archive_name; + field->val_str(&archive_name); + archive_name.set_ascii(strmake_root(thd->mem_root, archive_name.c_ptr(), + archive_name.length()), + archive_name.length()); + result.push(archive_name); + } + + end_read_record(&read_record); + error2: + delete sql_select; + error1: + close_log_table(thd, &open_tables_backup); + + if (error) + break; + } + + return result; +} + +Dynamic_array get_vtmd_tables(THD *thd) +{ + // Note function retrieves table names from current db only. + LOOKUP_FIELD_VALUES lookup_field_values= { + *thd->make_lex_string(thd->db, strlen(thd->db)), + *thd->make_lex_string(C_STRING_WITH_LEN("%_vtmd")), false, true}; + + Dynamic_array table_names; + make_table_name_list(thd, &table_names, thd->lex, &lookup_field_values, + &lookup_field_values.db_value); + + return table_names; +} diff --git a/sql/vtmd.h b/sql/vtmd.h index 8588462f83f..8a838dc2f88 100644 --- a/sql/vtmd.h +++ b/sql/vtmd.h @@ -87,8 +87,12 @@ public: } bool find_archive_name(THD *thd, String &out); + + static Dynamic_array get_archive_tables(THD *thd); }; +Dynamic_array get_vtmd_tables(THD *thd); + class VTMD_exists : public VTMD_table { protected: -- cgit v1.2.1 From 79e17b26fb5a05048a9ba78f8db471c043d3e410 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Wed, 27 Sep 2017 10:24:40 +0300 Subject: SQL: VTMD_table::get_archive_tables() misc fixes --- sql/vtmd.cc | 67 ++++++++++++++++++++++++++++++++++--------------------------- sql/vtmd.h | 5 +---- 2 files changed, 38 insertions(+), 34 deletions(-) (limited to 'sql') diff --git a/sql/vtmd.cc b/sql/vtmd.cc index aa9245570f4..e0824957c1b 100644 --- a/sql/vtmd.cc +++ b/sql/vtmd.cc @@ -485,7 +485,8 @@ VTMD_rename::revert_rename(THD *thd, LString new_db) return rc; } -void VTMD_table::archive_name( +void +VTMD_table::archive_name( THD* thd, const char* table_name, char* new_name, @@ -497,7 +498,8 @@ void VTMD_table::archive_name( now.second, now.second_part); } -bool VTMD_table::find_archive_name(THD *thd, String &out) +bool +VTMD_table::find_archive_name(THD *thd, String &out) { String vtmd_name; if (about.vers_vtmd_name(vtmd_name)) @@ -575,11 +577,28 @@ err: return error ? true : false; } -Dynamic_array VTMD_table::get_archive_tables(THD *thd) +static +bool +get_vtmd_tables(THD *thd, Dynamic_array &table_names) { - Dynamic_array result; + // Note function retrieves table names from current db only. + LOOKUP_FIELD_VALUES lookup_field_values= { + *thd->make_lex_string(thd->db, strlen(thd->db)), + *thd->make_lex_string(C_STRING_WITH_LEN("%_vtmd")), false, true}; + + int res= make_table_name_list(thd, &table_names, thd->lex, &lookup_field_values, + &lookup_field_values.db_value); + + return res; +} + +bool +VTMD_table::get_archive_tables(THD *thd, Dynamic_array &result) +{ + Dynamic_array vtmd_tables; + if (get_vtmd_tables(thd, vtmd_tables)) + return true; - Dynamic_array vtmd_tables= get_vtmd_tables(thd); for (uint i= 0; i < vtmd_tables.elements(); i++) { LEX_STRING table_name= *vtmd_tables.at(i); @@ -593,16 +612,23 @@ Dynamic_array VTMD_table::get_archive_tables(THD *thd) TABLE *table= open_log_table(thd, &table_list, &open_tables_backup); if (!table) - return result; + return true; READ_RECORD read_record; int error= 0; SQL_SELECT *sql_select= make_select(table, 0, 0, NULL, NULL, 0, &error); if (error) - goto error1; - if (error = init_read_record(&read_record, thd, table, sql_select, NULL, 1, 1, - false)) - goto error2; + { + close_log_table(thd, &open_tables_backup); + return true; + } + error= init_read_record(&read_record, thd, table, sql_select, NULL, 1, 1, false); + if (error) + { + delete sql_select; + close_log_table(thd, &open_tables_backup); + return true; + } while (!(error= read_record.read_record(&read_record))) { @@ -619,28 +645,9 @@ Dynamic_array VTMD_table::get_archive_tables(THD *thd) } end_read_record(&read_record); - error2: delete sql_select; - error1: close_log_table(thd, &open_tables_backup); - - if (error) - break; } - return result; -} - -Dynamic_array get_vtmd_tables(THD *thd) -{ - // Note function retrieves table names from current db only. - LOOKUP_FIELD_VALUES lookup_field_values= { - *thd->make_lex_string(thd->db, strlen(thd->db)), - *thd->make_lex_string(C_STRING_WITH_LEN("%_vtmd")), false, true}; - - Dynamic_array table_names; - make_table_name_list(thd, &table_names, thd->lex, &lookup_field_values, - &lookup_field_values.db_value); - - return table_names; + return false; } diff --git a/sql/vtmd.h b/sql/vtmd.h index 8a838dc2f88..a7350d8b3fe 100644 --- a/sql/vtmd.h +++ b/sql/vtmd.h @@ -87,12 +87,9 @@ public: } bool find_archive_name(THD *thd, String &out); - - static Dynamic_array get_archive_tables(THD *thd); + bool get_archive_tables(THD *thd, Dynamic_array &result); }; -Dynamic_array get_vtmd_tables(THD *thd); - class VTMD_exists : public VTMD_table { protected: -- cgit v1.2.1 From e9e3cb0f6ef8b9a6a6f10b7006958d29a93a7eda Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Wed, 27 Sep 2017 14:29:25 +0300 Subject: SQL: VTMD for SHOW CREATE fixes [related to #125] --- sql/sql_show.cc | 25 +++--- sql/vtmd.cc | 274 +++++++++++++++++++++++++++----------------------------- sql/vtmd.h | 11 ++- 3 files changed, 151 insertions(+), 159 deletions(-) (limited to 'sql') diff --git a/sql/sql_show.cc b/sql/sql_show.cc index ac340892505..2ae9f27c6e9 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1270,23 +1270,22 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) */ MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint(); - TABLE_LIST tl; - bool versioned_query= - table_list->vers_conditions.type != FOR_SYSTEM_TIME_UNSPECIFIED; - String archive_name; - if (versioned_query) + TABLE_LIST archive; + bool versioned= table_list->vers_conditions; + if (versioned) { - DBUG_ASSERT(table_list->vers_conditions.type == FOR_SYSTEM_TIME_AS_OF); + String archive_name; + DBUG_ASSERT(table_list->vers_conditions == FOR_SYSTEM_TIME_AS_OF); VTMD_table vtmd(*table_list); if (vtmd.find_archive_name(thd, archive_name)) goto exit; - tl.init_one_table(table_list->db, table_list->db_length, archive_name.ptr(), + archive.init_one_table(table_list->db, table_list->db_length, archive_name.ptr(), archive_name.length(), archive_name.ptr(), TL_READ); - tl.alias= table_list->table_name; - tl.vers_force_alias= true; - table_list= &tl; + archive.alias= table_list->table_name; + archive.vers_force_alias= true; + table_list= &archive; } if (mysqld_show_create_get_fields(thd, table_list, &field_list, &buffer)) @@ -1302,9 +1301,7 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) protocol->store(table_list->view_name.str, system_charset_info); else { - if (versioned_query) - protocol->store(tl.alias, system_charset_info); - else if (table_list->schema_table) + if (table_list->schema_table) protocol->store(table_list->schema_table->table_name, system_charset_info); else @@ -1332,7 +1329,7 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) my_eof(thd); exit: - if (versioned_query) + if (versioned) { /* If commit fails, we should be able to reset the OK status. */ thd->get_stmt_da()->set_overwrite_status(true); diff --git a/sql/vtmd.cc b/sql/vtmd.cc index e0824957c1b..6ba55679ec4 100644 --- a/sql/vtmd.cc +++ b/sql/vtmd.cc @@ -54,17 +54,17 @@ VTMD_table::find_record(ulonglong sys_trx_end, bool &found) key_buf_t key; found= false; - DBUG_ASSERT(vtmd); + DBUG_ASSERT(vtmd.table); - if (key.allocate(vtmd->s->max_unique_length)) + if (key.allocate(vtmd.table->s->max_unique_length)) return true; DBUG_ASSERT(sys_trx_end); - vtmd->vers_end_field()->set_notnull(); - vtmd->vers_end_field()->store(sys_trx_end, true); - key_copy(key, vtmd->record[0], vtmd->key_info + IDX_TRX_END, 0); + vtmd.table->vers_end_field()->set_notnull(); + vtmd.table->vers_end_field()->store(sys_trx_end, true); + key_copy(key, vtmd.table->record[0], vtmd.table->key_info + IDX_TRX_END, 0); - error= vtmd->file->ha_index_read_idx_map(vtmd->record[1], IDX_TRX_END, + error= vtmd.table->file->ha_index_read_idx_map(vtmd.table->record[1], IDX_TRX_END, key, HA_WHOLE_KEY, HA_READ_KEY_EXACT); @@ -72,27 +72,59 @@ VTMD_table::find_record(ulonglong sys_trx_end, bool &found) { if (error == HA_ERR_RECORD_DELETED || error == HA_ERR_KEY_NOT_FOUND) return false; - vtmd->file->print_error(error, MYF(0)); + vtmd.table->file->print_error(error, MYF(0)); return true; } - restore_record(vtmd, record[1]); + restore_record(vtmd.table, record[1]); found= true; return false; } + +bool +VTMD_table::open(THD *thd, Local_da &local_da, bool *created) +{ + if (created) + *created= false; + + if (0 == vtmd_name.length() && about.vers_vtmd_name(vtmd_name)) + return true; + + while (true) // max 2 iterations + { + vtmd.init_one_table( + DB_WITH_LEN(about), + XSTRING_WITH_LEN(vtmd_name), + vtmd_name, + TL_WRITE_CONCURRENT_INSERT); + + TABLE *res= open_log_table(thd, &vtmd, &open_tables_backup); + if (res) + return false; + + if (created && !*created && local_da.is_error() && local_da.sql_errno() == ER_NO_SUCH_TABLE) + { + local_da.reset_diagnostics_area(); + if (create(thd)) + break; + *created= true; + } + else + break; + } + return true; +} + bool VTMD_table::update(THD *thd, const char* archive_name) { - TABLE_LIST vtmd_tl; bool result= true; - bool close_log= false; bool found= false; - bool created= false; + bool created; int error; size_t an_len= 0; - Open_tables_backup open_tables_backup; ulonglong save_thd_options; { Local_da local_da(thd, ER_VERS_VTMD_ERROR); @@ -100,34 +132,10 @@ VTMD_table::update(THD *thd, const char* archive_name) save_thd_options= thd->variables.option_bits; thd->variables.option_bits&= ~OPTION_BIN_LOG; - if (about.vers_vtmd_name(vtmd_name)) - goto quit; + if (open(thd, local_da, &created)) + goto open_error; - while (true) // max 2 iterations - { - vtmd_tl.init_one_table( - DB_WITH_LEN(about), - XSTRING_WITH_LEN(vtmd_name), - vtmd_name, - TL_WRITE_CONCURRENT_INSERT); - - vtmd= open_log_table(thd, &vtmd_tl, &open_tables_backup); - if (vtmd) - break; - - if (!created && local_da.is_error() && local_da.sql_errno() == ER_NO_SUCH_TABLE) - { - local_da.reset_diagnostics_area(); - if (create(thd)) - goto quit; - created= true; - continue; - } - goto quit; - } - close_log= true; - - if (!vtmd->versioned()) + if (!vtmd.table->versioned()) { my_message(ER_VERS_VTMD_ERROR, "VTMD is not versioned", MYF(0)); goto quit; @@ -136,64 +144,64 @@ VTMD_table::update(THD *thd, const char* archive_name) if (!created && find_record(ULONGLONG_MAX, found)) goto quit; - if ((error= vtmd->file->extra(HA_EXTRA_MARK_AS_LOG_TABLE))) + if ((error= vtmd.table->file->extra(HA_EXTRA_MARK_AS_LOG_TABLE))) { - vtmd->file->print_error(error, MYF(0)); + vtmd.table->file->print_error(error, MYF(0)); goto quit; } /* Honor next number columns if present */ - vtmd->next_number_field= vtmd->found_next_number_field; + vtmd.table->next_number_field= vtmd.table->found_next_number_field; - if (vtmd->s->fields != FIELD_COUNT) + if (vtmd.table->s->fields != FIELD_COUNT) { my_printf_error(ER_VERS_VTMD_ERROR, "`%s.%s` unexpected fields count: %d", MYF(0), - vtmd->s->db.str, vtmd->s->table_name.str, vtmd->s->fields); + vtmd.table->s->db.str, vtmd.table->s->table_name.str, vtmd.table->s->fields); goto quit; } if (archive_name) { an_len= strlen(archive_name); - vtmd->field[FLD_ARCHIVE_NAME]->store(archive_name, an_len, table_alias_charset); - vtmd->field[FLD_ARCHIVE_NAME]->set_notnull(); + vtmd.table->field[FLD_ARCHIVE_NAME]->store(archive_name, an_len, table_alias_charset); + vtmd.table->field[FLD_ARCHIVE_NAME]->set_notnull(); } else { - vtmd->field[FLD_ARCHIVE_NAME]->set_null(); + vtmd.table->field[FLD_ARCHIVE_NAME]->set_null(); } - vtmd->field[FLD_COL_RENAMES]->set_null(); + vtmd.table->field[FLD_COL_RENAMES]->set_null(); if (found) { if (thd->lex->sql_command == SQLCOM_CREATE_TABLE) { my_printf_error(ER_VERS_VTMD_ERROR, "`%s.%s` exists and not empty!", MYF(0), - vtmd->s->db.str, vtmd->s->table_name.str); + vtmd.table->s->db.str, vtmd.table->s->table_name.str); goto quit; } - vtmd->mark_columns_needed_for_update(); // not needed? + vtmd.table->mark_columns_needed_for_update(); // not needed? if (archive_name) { - vtmd->s->versioned= false; - error= vtmd->file->ha_update_row(vtmd->record[1], vtmd->record[0]); - vtmd->s->versioned= true; + vtmd.table->s->versioned= false; + error= vtmd.table->file->ha_update_row(vtmd.table->record[1], vtmd.table->record[0]); + vtmd.table->s->versioned= true; if (!error) { if (thd->lex->sql_command == SQLCOM_DROP_TABLE) { - error= vtmd->file->ha_delete_row(vtmd->record[0]); + error= vtmd.table->file->ha_delete_row(vtmd.table->record[0]); } else { DBUG_ASSERT(thd->lex->sql_command == SQLCOM_ALTER_TABLE); - ulonglong sys_trx_end= (ulonglong) vtmd->vers_start_field()->val_int(); - store_record(vtmd, record[1]); - vtmd->field[FLD_NAME]->store(TABLE_NAME_WITH_LEN(about), system_charset_info); - vtmd->field[FLD_NAME]->set_notnull(); - vtmd->field[FLD_ARCHIVE_NAME]->set_null(); - error= vtmd->file->ha_update_row(vtmd->record[1], vtmd->record[0]); + ulonglong sys_trx_end= (ulonglong) vtmd.table->vers_start_field()->val_int(); + store_record(vtmd.table, record[1]); + vtmd.table->field[FLD_NAME]->store(TABLE_NAME_WITH_LEN(about), system_charset_info); + vtmd.table->field[FLD_NAME]->set_notnull(); + vtmd.table->field[FLD_ARCHIVE_NAME]->set_null(); + error= vtmd.table->file->ha_update_row(vtmd.table->record[1], vtmd.table->record[0]); if (error) goto err; @@ -203,60 +211,58 @@ VTMD_table::update(THD *thd, const char* archive_name) bool found; if (find_record(sys_trx_end, found)) goto quit; - if (!found || !vtmd->field[FLD_ARCHIVE_NAME]->is_null()) + if (!found || !vtmd.table->field[FLD_ARCHIVE_NAME]->is_null()) break; - store_record(vtmd, record[1]); - vtmd->field[FLD_ARCHIVE_NAME]->store(archive_name, an_len, table_alias_charset); - vtmd->field[FLD_ARCHIVE_NAME]->set_notnull(); - vtmd->s->versioned= false; - error= vtmd->file->ha_update_row(vtmd->record[1], vtmd->record[0]); - vtmd->s->versioned= true; + store_record(vtmd.table, record[1]); + vtmd.table->field[FLD_ARCHIVE_NAME]->store(archive_name, an_len, table_alias_charset); + vtmd.table->field[FLD_ARCHIVE_NAME]->set_notnull(); + vtmd.table->s->versioned= false; + error= vtmd.table->file->ha_update_row(vtmd.table->record[1], vtmd.table->record[0]); + vtmd.table->s->versioned= true; if (error) goto err; - sys_trx_end= (ulonglong) vtmd->vers_start_field()->val_int(); + sys_trx_end= (ulonglong) vtmd.table->vers_start_field()->val_int(); } // while (true) } // else (thd->lex->sql_command != SQLCOM_DROP_TABLE) } // if (!error) } // if (archive_name) else { - vtmd->field[FLD_NAME]->store(TABLE_NAME_WITH_LEN(about), system_charset_info); - vtmd->field[FLD_NAME]->set_notnull(); - error= vtmd->file->ha_update_row(vtmd->record[1], vtmd->record[0]); + vtmd.table->field[FLD_NAME]->store(TABLE_NAME_WITH_LEN(about), system_charset_info); + vtmd.table->field[FLD_NAME]->set_notnull(); + error= vtmd.table->file->ha_update_row(vtmd.table->record[1], vtmd.table->record[0]); } } // if (found) else { - vtmd->field[FLD_NAME]->store(TABLE_NAME_WITH_LEN(about), system_charset_info); - vtmd->field[FLD_NAME]->set_notnull(); - vtmd->mark_columns_needed_for_insert(); // not needed? - error= vtmd->file->ha_write_row(vtmd->record[0]); + vtmd.table->field[FLD_NAME]->store(TABLE_NAME_WITH_LEN(about), system_charset_info); + vtmd.table->field[FLD_NAME]->set_notnull(); + vtmd.table->mark_columns_needed_for_insert(); // not needed? + error= vtmd.table->file->ha_write_row(vtmd.table->record[0]); } if (error) { err: - vtmd->file->print_error(error, MYF(0)); + vtmd.table->file->print_error(error, MYF(0)); } else result= local_da.is_error(); } quit: - if (close_log) - close_log_table(thd, &open_tables_backup); + close_log_table(thd, &open_tables_backup); +open_error: thd->variables.option_bits= save_thd_options; return result; } - bool VTMD_rename::move_archives(THD *thd, LString &new_db) { - TABLE_LIST vtmd_tl; - vtmd_tl.init_one_table( + vtmd.init_one_table( DB_WITH_LEN(about), XSTRING_WITH_LEN(vtmd_name), vtmd_name, @@ -269,66 +275,66 @@ VTMD_rename::move_archives(THD *thd, LString &new_db) Open_tables_backup open_tables_backup; key_buf_t key; - vtmd= open_log_table(thd, &vtmd_tl, &open_tables_backup); - if (!vtmd) + TABLE *res= open_log_table(thd, &vtmd, &open_tables_backup); + if (!res) return true; - if (key.allocate(vtmd->key_info[IDX_ARCHIVE_NAME].key_length)) + if (key.allocate(vtmd.table->key_info[IDX_ARCHIVE_NAME].key_length)) { close_log_table(thd, &open_tables_backup); return true; } - if ((error= vtmd->file->ha_start_keyread(IDX_ARCHIVE_NAME))) + if ((error= vtmd.table->file->ha_start_keyread(IDX_ARCHIVE_NAME))) goto err; end_keyread= true; - if ((error= vtmd->file->ha_index_init(IDX_ARCHIVE_NAME, true))) + if ((error= vtmd.table->file->ha_index_init(IDX_ARCHIVE_NAME, true))) goto err; index_end= true; - error= vtmd->file->ha_index_first(vtmd->record[0]); + error= vtmd.table->file->ha_index_first(vtmd.table->record[0]); while (!error) { - if (!vtmd->field[FLD_ARCHIVE_NAME]->is_null()) + if (!vtmd.table->field[FLD_ARCHIVE_NAME]->is_null()) { - vtmd->field[FLD_ARCHIVE_NAME]->val_str(&archive); + vtmd.table->field[FLD_ARCHIVE_NAME]->val_str(&archive); key_copy(key, - vtmd->record[0], - &vtmd->key_info[IDX_ARCHIVE_NAME], - vtmd->key_info[IDX_ARCHIVE_NAME].key_length, + vtmd.table->record[0], + &vtmd.table->key_info[IDX_ARCHIVE_NAME], + vtmd.table->key_info[IDX_ARCHIVE_NAME].key_length, false); - error= vtmd->file->ha_index_read_map( - vtmd->record[0], + error= vtmd.table->file->ha_index_read_map( + vtmd.table->record[0], key, - vtmd->key_info[IDX_ARCHIVE_NAME].ext_key_part_map, + vtmd.table->key_info[IDX_ARCHIVE_NAME].ext_key_part_map, HA_READ_PREFIX_LAST); if (!error) { if ((rc= move_table(thd, archive, new_db))) break; - error= vtmd->file->ha_index_next(vtmd->record[0]); + error= vtmd.table->file->ha_index_next(vtmd.table->record[0]); } } else { archive.length(0); - error= vtmd->file->ha_index_next(vtmd->record[0]); + error= vtmd.table->file->ha_index_next(vtmd.table->record[0]); } } if (error && error != HA_ERR_END_OF_FILE) { err: - vtmd->file->print_error(error, MYF(0)); + vtmd.table->file->print_error(error, MYF(0)); rc= true; } if (index_end) - vtmd->file->ha_index_end(); + vtmd.table->file->ha_index_end(); if (end_keyread) - vtmd->file->ha_end_keyread(); + vtmd.table->file->ha_end_keyread(); close_log_table(thd, &open_tables_backup); return rc; @@ -501,80 +507,64 @@ VTMD_table::archive_name( bool VTMD_table::find_archive_name(THD *thd, String &out) { - String vtmd_name; - if (about.vers_vtmd_name(vtmd_name)) - return true; - READ_RECORD info; - int error= 0; + int error; SQL_SELECT *select= NULL; COND *conds= NULL; List dummy; SELECT_LEX &select_lex= thd->lex->select_lex; - TABLE_LIST tl; - tl.init_one_table(about.db, about.db_length, vtmd_name.ptr(), - vtmd_name.length(), vtmd_name.ptr(), TL_READ); - - Open_tables_backup open_tables_backup; - if (!(vtmd= open_log_table(thd, &tl, &open_tables_backup))) - { - my_error(ER_VERS_VTMD_ERROR, MYF(0), "failed to open VTMD table"); + Local_da local_da(thd, ER_VERS_VTMD_ERROR); + if (open(thd, local_da)) return true; - } Name_resolution_context &ctx= thd->lex->select_lex.context; TABLE_LIST *table_list= ctx.table_list; TABLE_LIST *first_name_resolution_table= ctx.first_name_resolution_table; - table_map map = tl.table->map; - ctx.table_list= &tl; - ctx.first_name_resolution_table= &tl; - tl.table->map= 1; - - tl.vers_conditions= about.vers_conditions; - if ((error= vers_setup_select(thd, &tl, &conds, &select_lex)) || - (error= setup_conds(thd, &tl, dummy, &conds))) - { - my_error(ER_VERS_VTMD_ERROR, MYF(0), - "failed to setup conditions for querying VTMD table"); + table_map map = vtmd.table->map; + ctx.table_list= &vtmd; + ctx.first_name_resolution_table= &vtmd; + vtmd.table->map= 1; + + vtmd.vers_conditions= about.vers_conditions; + if ((error= vers_setup_select(thd, &vtmd, &conds, &select_lex)) || + (error= setup_conds(thd, &vtmd, dummy, &conds))) goto err; - } - select= make_select(tl.table, 0, 0, conds, NULL, 0, &error); + select= make_select(vtmd.table, 0, 0, conds, NULL, 0, &error); if (error) goto loc_err; - if ((error= - init_read_record(&info, thd, tl.table, select, NULL, 1, 1, false))) + + error= init_read_record(&info, thd, vtmd.table, select, NULL, + 1 /* use_record_cache */, true /* print_error */, + false /* disable_rr_cache */); + if (error) goto loc_err; while (!(error= info.read_record(&info)) && !thd->killed && !thd->is_error()) { if (select->skip_record(thd) > 0) { - tl.table->field[FLD_ARCHIVE_NAME]->val_str(&out); - - if (out.length() == 0) - { - // Handle AS OF NOW or just RENAMEd case - out.set(about.table_name, about.table_name_length, - system_charset_info); - } + vtmd.table->field[FLD_ARCHIVE_NAME]->val_str(&out); + if (out.length() == 0) // Handle AS OF NOW or RENAME TABLE case + out.set(about.table_name, about.table_name_length, system_charset_info); break; } } -loc_err: - if (error) - my_error(ER_VERS_VTMD_ERROR, MYF(0), "failed to query VTMD table"); + if (error < 0) + my_error(ER_NO_SUCH_TABLE, MYF(0), about.db, about.alias); +loc_err: end_read_record(&info); err: delete select; ctx.table_list= table_list; ctx.first_name_resolution_table= first_name_resolution_table; - tl.table->map= map; + vtmd.table->map= map; close_log_table(thd, &open_tables_backup); - return error ? true : false; + DBUG_ASSERT(!error || local_da.is_error()); + return error; } static diff --git a/sql/vtmd.h b/sql/vtmd.h index a7350d8b3fe..5a987723a47 100644 --- a/sql/vtmd.h +++ b/sql/vtmd.h @@ -48,8 +48,10 @@ class THD; class VTMD_table { + Open_tables_backup open_tables_backup; + protected: - TABLE *vtmd; + TABLE_LIST vtmd; const TABLE_LIST &about; SString_t vtmd_name; @@ -72,13 +74,16 @@ public: }; VTMD_table(TABLE_LIST &_about) : - vtmd(NULL), about(_about) - {} + { + vtmd.table= NULL; + } bool create(THD *thd); bool find_record(ulonglong sys_trx_end, bool &found); + bool open(THD *thd, Local_da &local_da, bool *created= NULL); bool update(THD *thd, const char* archive_name= NULL); + bool setup_select(THD *thd); static void archive_name(THD *thd, const char *table_name, char *new_name, size_t new_name_size); void archive_name(THD *thd, char *new_name, size_t new_name_size) -- cgit v1.2.1 From c5801dd67b86d2a3e27f05fcbdb01d20014019a2 Mon Sep 17 00:00:00 2001 From: Eugene Kosov Date: Wed, 27 Sep 2017 22:08:20 +0300 Subject: SQL: hide archive tables [closes #193] --- sql/sql_show.cc | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ sql/vtmd.cc | 25 ++++++++++++----------- sql/vtmd.h | 3 ++- 3 files changed, 77 insertions(+), 12 deletions(-) (limited to 'sql') diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 2ae9f27c6e9..b886f42580f 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -4863,6 +4863,59 @@ public: } }; +static bool get_all_archive_tables(THD *thd, + Dynamic_array &all_archive_tables) +{ + if (thd->variables.vers_hide == VERS_HIDE_NEVER) + return false; + + Dynamic_array all_db; + LOOKUP_FIELD_VALUES lookup_field_values= { + *thd->make_lex_string(C_STRING_WITH_LEN("%")), {NULL, 0}, true, false}; + if (make_db_list(thd, &all_db, &lookup_field_values)) + return true; + + LEX_STRING information_schema= {C_STRING_WITH_LEN("information_schema")}; + for (size_t i= 0; i < all_db.elements(); i++) + { + LEX_STRING db= *all_db.at(i); + if (db.length == information_schema.length && + !memcmp(db.str, information_schema.str, db.length)) + { + all_db.del(i); + break; + } + } + + for (size_t i= 0; i < all_db.elements(); i++) + { + LEX_STRING db_name= *all_db.at(i); + Dynamic_array archive_tables; + if (VTMD_table::get_archive_tables(thd, db_name.str, db_name.length, + archive_tables)) + return true; + for (size_t i= 0; i < archive_tables.elements(); i++) + if (all_archive_tables.push(archive_tables.at(i))) + return true; + } + + return false; +} + +static bool is_archive_table(const Dynamic_array &all_archive_tables, + LEX_STRING candidate) +{ + for (size_t i= 0; i < all_archive_tables.elements(); i++) + { + const String &archive_table= all_archive_tables.at(i); + if (candidate.length == archive_table.length() && + !memcmp(candidate.str, archive_table.ptr(), candidate.length)) + { + return true; + } + } + return false; +} /** @brief Fill I_S tables whose data are retrieved @@ -4905,6 +4958,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) #endif uint table_open_method= tables->table_open_method; bool can_deadlock; + Dynamic_array all_archive_tables; DBUG_ENTER("get_all_tables"); /* @@ -4967,6 +5021,10 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) if (make_db_list(thd, &db_names, &plan->lookup_field_vals)) goto err; + + if (get_all_archive_tables(thd, all_archive_tables)) + goto err; + for (size_t i=0; i < db_names.elements(); i++) { LEX_STRING *db_name= db_names.at(i); @@ -4992,6 +5050,9 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) LEX_STRING *table_name= table_names.at(i); DBUG_ASSERT(table_name->length <= NAME_LEN); + if (is_archive_table(all_archive_tables, *table_name)) + continue; + #ifndef NO_EMBEDDED_ACCESS_CHECKS if (!(thd->col_access & TABLE_ACLS)) { diff --git a/sql/vtmd.cc b/sql/vtmd.cc index 6ba55679ec4..679a70e3e80 100644 --- a/sql/vtmd.cc +++ b/sql/vtmd.cc @@ -569,24 +569,26 @@ err: static bool -get_vtmd_tables(THD *thd, Dynamic_array &table_names) +get_vtmd_tables(THD *thd, const char *db, + size_t db_length, Dynamic_array &table_names) { - // Note function retrieves table names from current db only. LOOKUP_FIELD_VALUES lookup_field_values= { - *thd->make_lex_string(thd->db, strlen(thd->db)), + *thd->make_lex_string(db, db_length), *thd->make_lex_string(C_STRING_WITH_LEN("%_vtmd")), false, true}; - int res= make_table_name_list(thd, &table_names, thd->lex, &lookup_field_values, - &lookup_field_values.db_value); + int res= + make_table_name_list(thd, &table_names, thd->lex, &lookup_field_values, + &lookup_field_values.db_value); return res; } bool -VTMD_table::get_archive_tables(THD *thd, Dynamic_array &result) +VTMD_table::get_archive_tables(THD *thd, const char *db, size_t db_length, + Dynamic_array &result) { Dynamic_array vtmd_tables; - if (get_vtmd_tables(thd, vtmd_tables)) + if (get_vtmd_tables(thd, db, db_length, vtmd_tables)) return true; for (uint i= 0; i < vtmd_tables.elements(); i++) @@ -595,10 +597,8 @@ VTMD_table::get_archive_tables(THD *thd, Dynamic_array &result) Open_tables_backup open_tables_backup; TABLE_LIST table_list; - // Assume VTMD tables belongs to current db. - table_list.init_one_table(thd->db, strlen(thd->db), - LEX_STRING_WITH_LEN(table_name), table_name.str, - TL_READ); + table_list.init_one_table(db, db_length, LEX_STRING_WITH_LEN(table_name), + table_name.str, TL_READ); TABLE *table= open_log_table(thd, &table_list, &open_tables_backup); if (!table) @@ -633,6 +633,9 @@ VTMD_table::get_archive_tables(THD *thd, Dynamic_array &result) archive_name.length()); result.push(archive_name); } + // check for EOF + if (!thd->is_error()) + error= 0; end_read_record(&read_record); delete sql_select; diff --git a/sql/vtmd.h b/sql/vtmd.h index 5a987723a47..a2b010ece7d 100644 --- a/sql/vtmd.h +++ b/sql/vtmd.h @@ -92,7 +92,8 @@ public: } bool find_archive_name(THD *thd, String &out); - bool get_archive_tables(THD *thd, Dynamic_array &result); + static bool get_archive_tables(THD *thd, const char *db, size_t db_length, + Dynamic_array &result); }; class VTMD_exists : public VTMD_table -- cgit v1.2.1 From 5e42511ce169d6883499d00072c309647f303aee Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Thu, 28 Sep 2017 13:16:04 +0300 Subject: SQL: SELECT from archive table [closes #127] --- sql/sql_parse.cc | 16 ++++++++++++++++ sql/sql_show.cc | 13 ++++++++----- sql/vtmd.cc | 21 +++++++++++++++++++-- sql/vtmd.h | 2 +- 4 files changed, 44 insertions(+), 8 deletions(-) (limited to 'sql') diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index cc0dcf9a3a8..69938946678 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -113,6 +113,7 @@ #include "wsrep_mysqld.h" #include "wsrep_thd.h" +#include "vtmd.h" static void wsrep_mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state, @@ -6373,6 +6374,21 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables) if (check_dependencies_in_with_clauses(lex->with_clauses_list)) return 1; + if (thd->variables.vers_alter_history == VERS_ALTER_HISTORY_SURVIVE) + { + for (TABLE_LIST *table= all_tables; table; table= table->next_local) + { + if (table->vers_conditions) + { + VTMD_exists vtmd(*table); + if (vtmd.check_exists(thd)) + return 1; + if (vtmd.exists && vtmd.setup_select(thd)) + return 1; + } + } + } + if (!(res= open_and_lock_tables(thd, all_tables, TRUE, 0))) { if (lex->describe) diff --git a/sql/sql_show.cc b/sql/sql_show.cc index b886f42580f..eb5bcc300bf 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1280,12 +1280,15 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) if (vtmd.find_archive_name(thd, archive_name)) goto exit; - archive.init_one_table(table_list->db, table_list->db_length, archive_name.ptr(), - archive_name.length(), archive_name.ptr(), TL_READ); + if (archive_name.length() > 0) + { + archive.init_one_table(table_list->db, table_list->db_length, archive_name.ptr(), + archive_name.length(), archive_name.ptr(), TL_READ); - archive.alias= table_list->table_name; - archive.vers_force_alias= true; - table_list= &archive; + archive.alias= table_list->table_name; + archive.vers_force_alias= true; + table_list= &archive; + } } if (mysqld_show_create_get_fields(thd, table_list, &field_list, &buffer)) diff --git a/sql/vtmd.cc b/sql/vtmd.cc index 679a70e3e80..1ed9014331c 100644 --- a/sql/vtmd.cc +++ b/sql/vtmd.cc @@ -7,6 +7,8 @@ #include "table_cache.h" // tdc_remove_table() #include "key.h" #include "sql_show.h" +#include "sql_parse.h" +#include "sql_lex.h" LString VERS_VTMD_TEMPLATE(C_STRING_WITH_LEN("vtmd_template")); @@ -546,8 +548,6 @@ VTMD_table::find_archive_name(THD *thd, String &out) if (select->skip_record(thd) > 0) { vtmd.table->field[FLD_ARCHIVE_NAME]->val_str(&out); - if (out.length() == 0) // Handle AS OF NOW or RENAME TABLE case - out.set(about.table_name, about.table_name_length, system_charset_info); break; } } @@ -644,3 +644,20 @@ VTMD_table::get_archive_tables(THD *thd, const char *db, size_t db_length, return false; } + +bool VTMD_table::setup_select(THD* thd) +{ + SString archive_name; + if (find_archive_name(thd, archive_name)) + return true; + + if (archive_name.length() == 0) + return false; + + about.table_name= (char *) thd->memdup(archive_name.c_ptr_safe(), archive_name.length() + 1); + about.table_name_length= archive_name.length(); + DBUG_ASSERT(!about.mdl_request.ticket); + about.mdl_request.init(MDL_key::TABLE, about.db, about.table_name, + about.mdl_request.type, about.mdl_request.duration); + return false; +} diff --git a/sql/vtmd.h b/sql/vtmd.h index a2b010ece7d..a89cfd446ba 100644 --- a/sql/vtmd.h +++ b/sql/vtmd.h @@ -52,7 +52,7 @@ class VTMD_table protected: TABLE_LIST vtmd; - const TABLE_LIST &about; + TABLE_LIST &about; SString_t vtmd_name; private: -- cgit v1.2.1 From 9062385c20b62a7231b256f3f7af44ea9b1f5ad9 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Fri, 29 Sep 2017 17:51:10 +0300 Subject: SQL: invalidate current SP at archive substitution [closes #127] Related to #125 --- sql/sp_cache.cc | 4 ++++ sql/sp_cache.h | 1 + sql/sp_head.cc | 10 +--------- sql/sp_rcontext.h | 5 +---- sql/sql_parse.cc | 5 +++++ sql/sql_show.cc | 13 +------------ sql/vtmd.cc | 9 +++++++++ 7 files changed, 22 insertions(+), 25 deletions(-) (limited to 'sql') diff --git a/sql/sp_cache.cc b/sql/sp_cache.cc index b80cde335db..634a084d1fb 100644 --- a/sql/sp_cache.cc +++ b/sql/sp_cache.cc @@ -238,6 +238,10 @@ void sp_cache_flush_obsolete(sp_cache **cp, sp_head **sp) } } +void sp_cache_flush(sp_cache *cp, sp_head *sp) +{ + cp->remove(sp); +} /** Return the current global version of the cache. diff --git a/sql/sp_cache.h b/sql/sp_cache.h index b21d4c4bf25..c776df263ce 100644 --- a/sql/sp_cache.h +++ b/sql/sp_cache.h @@ -62,6 +62,7 @@ void sp_cache_insert(sp_cache **cp, sp_head *sp); sp_head *sp_cache_lookup(sp_cache **cp, sp_name *name); void sp_cache_invalidate(); void sp_cache_flush_obsolete(sp_cache **cp, sp_head **sp); +void sp_cache_flush(sp_cache *cp, sp_head *sp); ulong sp_cache_version(); void sp_cache_enforce_limit(sp_cache *cp, ulong upper_limit_for_elements); diff --git a/sql/sp_head.cc b/sql/sp_head.cc index f470ca3f283..d3310578225 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -1589,10 +1589,7 @@ sp_head::execute_trigger(THD *thd, goto err_with_cleanup; } -#ifndef DBUG_OFF nctx->sp= this; -#endif - thd->spcont= nctx; err_status= execute(thd, FALSE); @@ -1713,9 +1710,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, */ thd->restore_active_arena(&call_arena, &backup_arena); -#ifndef DBUG_OFF nctx->sp= this; -#endif /* Pass arguments. */ for (arg_no= 0; arg_no < argcount; arg_no++) @@ -1919,9 +1914,7 @@ sp_head::execute_procedure(THD *thd, List *args) DBUG_RETURN(TRUE); } -#ifndef DBUG_OFF octx->sp= 0; -#endif thd->spcont= octx; /* set callers_arena to thd, for upper-level function to work */ @@ -1934,9 +1927,8 @@ sp_head::execute_procedure(THD *thd, List *args) thd->spcont= save_spcont; DBUG_RETURN(TRUE); } -#ifndef DBUG_OFF + nctx->sp= this; -#endif if (params > 0) { diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h index 98464518787..a5e5df407ca 100644 --- a/sql/sp_rcontext.h +++ b/sql/sp_rcontext.h @@ -176,11 +176,8 @@ public: /// of the client/server protocol. bool end_partial_result_set; -#ifndef DBUG_OFF - /// The stored program for which this runtime context is created. Used for - /// checking if correct runtime context is used for variable handling. + /// The stored program for which this runtime context is created. sp_head *sp; -#endif ///////////////////////////////////////////////////////////////////////// // SP-variables. diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 69938946678..2541ecb1c53 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -5810,6 +5810,11 @@ end_with_restore_list: if (do_execute_sp(thd, sp)) goto error; + + if (sp->sp_cache_version() == ULONG_MAX) + { + sp_cache_flush(thd->sp_proc_cache, sp); + } } break; } diff --git a/sql/sql_show.cc b/sql/sql_show.cc index eb5bcc300bf..5f407e728e1 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1274,21 +1274,10 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) bool versioned= table_list->vers_conditions; if (versioned) { - String archive_name; DBUG_ASSERT(table_list->vers_conditions == FOR_SYSTEM_TIME_AS_OF); VTMD_table vtmd(*table_list); - if (vtmd.find_archive_name(thd, archive_name)) + if (vtmd.setup_select(thd)) goto exit; - - if (archive_name.length() > 0) - { - archive.init_one_table(table_list->db, table_list->db_length, archive_name.ptr(), - archive_name.length(), archive_name.ptr(), TL_READ); - - archive.alias= table_list->table_name; - archive.vers_force_alias= true; - table_list= &archive; - } } if (mysqld_show_create_get_fields(thd, table_list, &field_list, &buffer)) diff --git a/sql/vtmd.cc b/sql/vtmd.cc index 1ed9014331c..2821b2226c7 100644 --- a/sql/vtmd.cc +++ b/sql/vtmd.cc @@ -9,6 +9,8 @@ #include "sql_show.h" #include "sql_parse.h" #include "sql_lex.h" +#include "sp_head.h" +#include "sp_rcontext.h" LString VERS_VTMD_TEMPLATE(C_STRING_WITH_LEN("vtmd_template")); @@ -659,5 +661,12 @@ bool VTMD_table::setup_select(THD* thd) DBUG_ASSERT(!about.mdl_request.ticket); about.mdl_request.init(MDL_key::TABLE, about.db, about.table_name, about.mdl_request.type, about.mdl_request.duration); + about.vers_force_alias= true; + // Since we modified SELECT_LEX::table_list, we need to invalidate current SP + if (thd->spcont) + { + DBUG_ASSERT(thd->spcont->sp); + thd->spcont->sp->set_sp_cache_version(ULONG_MAX); + } return false; } -- cgit v1.2.1 From 1d056f5abcfffe768474ae7860485c5e9ad3644e Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 3 Oct 2017 00:31:44 +0300 Subject: SQL: not a VTMD table warning [related to #199] --- sql/share/errmsg-utf8.txt | 7 +++++-- sql/sql_base.cc | 29 +++++++++++++++++++++++++---- sql/table.h | 6 ++++++ sql/vtmd.cc | 21 +++++++++++++++++++-- 4 files changed, 55 insertions(+), 8 deletions(-) (limited to 'sql') diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index c551bcef809..e1bfa9e9172 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7581,10 +7581,13 @@ ER_VERS_NO_TRX_ID eng "TRX_ID %lu not found in VTQ" ER_WRONG_TABLESPACE_NAME 42000 - eng "Incorrect tablespace name `%-.192s`" + eng "Incorrect tablespace name `%-.192s`" ER_VERS_ALTER_SYSTEM_FIELD eng "Can not change system versioning field '%s'" ER_VERS_SYS_FIELD_NOT_HIDDEN - eng "System versioning field '%s' is not hidden" + eng "System versioning field '%s' is not hidden" + +ER_NOT_LOG_TABLE + eng "Table `%s.%s` is not a log table" diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 27bf5ab6931..fce479740f2 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -8723,10 +8723,31 @@ open_log_table(THD *thd, TABLE_LIST *one_table, Open_tables_backup *backup) if ((table= open_ltable(thd, one_table, one_table->lock_type, flags))) { - DBUG_ASSERT(table->s->table_category == TABLE_CATEGORY_LOG); - /* Make sure all columns get assigned to a default value */ - table->use_all_columns(); - DBUG_ASSERT(table->no_replicate); + if (table->s->table_category == TABLE_CATEGORY_LOG) + { + /* Make sure all columns get assigned to a default value */ + table->use_all_columns(); + DBUG_ASSERT(table->no_replicate); + } + else + { + my_error(ER_NOT_LOG_TABLE, MYF(0), table->s->db.str, table->s->table_name.str); + int error= 0; + if (table->current_lock != F_UNLCK) + { + table->current_lock= F_UNLCK; + error= table->file->ha_external_lock(thd, F_UNLCK); + } + if (error) + table->file->print_error(error, MYF(0)); + else + { + tc_release_table(table); + thd->reset_open_tables_state(thd); + thd->restore_backup_open_tables_state(backup); + table= NULL; + } + } } else thd->restore_backup_open_tables_state(backup); diff --git a/sql/table.h b/sql/table.h index d857ba4752b..2e618e26361 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1530,6 +1530,12 @@ public: return s->versioned && file->native_versioned(); } + bool vers_vtmd() const + { + DBUG_ASSERT(s); + return s->versioned && s->vtmd; + } + Field *vers_start_field() const { DBUG_ASSERT(s && s->versioned); diff --git a/sql/vtmd.cc b/sql/vtmd.cc index 2821b2226c7..0f008fc1e91 100644 --- a/sql/vtmd.cc +++ b/sql/vtmd.cc @@ -593,6 +593,7 @@ VTMD_table::get_archive_tables(THD *thd, const char *db, size_t db_length, if (get_vtmd_tables(thd, db, db_length, vtmd_tables)) return true; + Local_da local_da(thd, ER_VERS_VTMD_ERROR); for (uint i= 0; i < vtmd_tables.elements(); i++) { LEX_STRING table_name= *vtmd_tables.at(i); @@ -603,8 +604,24 @@ VTMD_table::get_archive_tables(THD *thd, const char *db, size_t db_length, table_name.str, TL_READ); TABLE *table= open_log_table(thd, &table_list, &open_tables_backup); - if (!table) - return true; + if (!table || !table->vers_vtmd()) + { + if (table) + close_log_table(thd, &open_tables_backup); + else + { + if (local_da.is_error() && local_da.sql_errno() == ER_NOT_LOG_TABLE) + local_da.reset_diagnostics_area(); + else + return true; + } + push_warning_printf( + thd, Sql_condition::WARN_LEVEL_WARN, + ER_VERS_VTMD_ERROR, + "Table `%s.%s` is not a VTMD table", + db, table_name.str); + continue; + } READ_RECORD read_record; int error= 0; -- cgit v1.2.1 From d7a484b04feb184cf697122f63f821f338c35769 Mon Sep 17 00:00:00 2001 From: Eugene Kosov Date: Wed, 4 Oct 2017 13:36:31 +0300 Subject: SQL: segfault after make_select() in VTMD --- sql/vtmd.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/vtmd.cc b/sql/vtmd.cc index 0f008fc1e91..a1d7375dfef 100644 --- a/sql/vtmd.cc +++ b/sql/vtmd.cc @@ -547,7 +547,7 @@ VTMD_table::find_archive_name(THD *thd, String &out) while (!(error= info.read_record(&info)) && !thd->killed && !thd->is_error()) { - if (select->skip_record(thd) > 0) + if (!select || select->skip_record(thd) > 0) { vtmd.table->field[FLD_ARCHIVE_NAME]->val_str(&out); break; -- cgit v1.2.1 From 17bd486f361a150cb6e4c0320c1fa8e344beb76b Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 17 Oct 2017 17:20:46 +0300 Subject: SQL: thd_start_utime() fix [fixes #284] --- sql/sql_class.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 4226da573cb..4a780812ec4 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -4644,7 +4644,7 @@ extern "C" time_t thd_start_time(const MYSQL_THD thd) of the current query. */ extern "C" unsigned long long thd_start_utime(const MYSQL_THD thd) { - return thd->start_utime; + return thd->start_time * 1000000 + thd->start_time_sec_part; } -- cgit v1.2.1 From ce66d5b2a53d76d286e8443807c4ebd7743cc354 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 17 Oct 2017 17:23:59 +0300 Subject: SQL: assertion in partition_info::vers_trx_id_to_ts() Related to #283 --- sql/partition_info.cc | 1 + 1 file changed, 1 insertion(+) (limited to 'sql') diff --git a/sql/partition_info.cc b/sql/partition_info.cc index af97791ab80..f2e3c12aa4a 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -3383,6 +3383,7 @@ static bool has_same_column_order(List *create_list, bool partition_info::vers_trx_id_to_ts(THD* thd, Field* in_trx_id, Field_timestamp& out_ts) { + DBUG_ASSERT(table); handlerton *hton= plugin_hton(table->s->db_plugin); DBUG_ASSERT(hton); ulonglong trx_id= in_trx_id->val_int(); -- cgit v1.2.1