diff options
author | unknown <igor@olga.mysql.com> | 2007-06-03 22:52:02 -0700 |
---|---|---|
committer | unknown <igor@olga.mysql.com> | 2007-06-03 22:52:02 -0700 |
commit | 68daddf309728fd3e7adb728f28fe773952f4fda (patch) | |
tree | 9bfac4f842371d84eebd379e361ab3df1e3bf0f0 /sql | |
parent | f42bcd7774b09cd05fcb4a9e29496de5b8ebb33a (diff) | |
parent | c4a58a684686ad91dd6aa19b80ca1cb9f8abee07 (diff) | |
download | mariadb-git-68daddf309728fd3e7adb728f28fe773952f4fda.tar.gz |
Merge olga.mysql.com:/home/igor/mysql-5.1
into olga.mysql.com:/home/igor/mysql-5.1-opt-merge
mysql-test/r/alter_table.result:
Auto merged
mysql-test/r/create.result:
Auto merged
mysql-test/r/innodb_mysql.result:
Auto merged
mysql-test/r/sp.result:
Auto merged
mysql-test/r/user_var.result:
Auto merged
mysql-test/t/alter_table.test:
Auto merged
mysql-test/t/sp.test:
Auto merged
mysql-test/t/strict.test:
Auto merged
mysql-test/t/subselect3.test:
Auto merged
sql/field.h:
Auto merged
sql/handler.h:
Auto merged
sql/item_func.cc:
Auto merged
sql/item_func.h:
Auto merged
sql/mysqld.cc:
Auto merged
sql/sp_head.cc:
Auto merged
sql/sql_base.cc:
Auto merged
sql/sql_class.h:
Auto merged
sql/sql_select.cc:
Auto merged
sql/sql_update.cc:
Auto merged
mysql-test/r/ps_2myisam.result:
Manual merge.
mysql-test/r/ps_3innodb.result:
Manual merge.
mysql-test/r/ps_4heap.result:
Manual merge.
mysql-test/r/ps_5merge.result:
Manual merge.
mysql-test/r/ps_7ndb.result:
Manual merge.
sql/sql_table.cc:
Manual merge.
sql/sql_view.cc:
Manual merge.
Diffstat (limited to 'sql')
-rw-r--r-- | sql/field.cc | 2 | ||||
-rw-r--r-- | sql/field.h | 7 | ||||
-rw-r--r-- | sql/field_conv.cc | 14 | ||||
-rw-r--r-- | sql/handler.h | 10 | ||||
-rw-r--r-- | sql/item_cmpfunc.cc | 7 | ||||
-rw-r--r-- | sql/item_func.cc | 69 | ||||
-rw-r--r-- | sql/item_func.h | 13 | ||||
-rw-r--r-- | sql/item_subselect.cc | 81 | ||||
-rw-r--r-- | sql/item_sum.cc | 14 | ||||
-rw-r--r-- | sql/item_timefunc.cc | 21 | ||||
-rw-r--r-- | sql/my_decimal.h | 10 | ||||
-rw-r--r-- | sql/mysqld.cc | 5 | ||||
-rw-r--r-- | sql/sp_head.cc | 3 | ||||
-rw-r--r-- | sql/sql_base.cc | 2 | ||||
-rw-r--r-- | sql/sql_class.h | 5 | ||||
-rw-r--r-- | sql/sql_select.cc | 143 | ||||
-rw-r--r-- | sql/sql_table.cc | 53 | ||||
-rw-r--r-- | sql/sql_update.cc | 143 | ||||
-rw-r--r-- | sql/sql_view.cc | 127 | ||||
-rw-r--r-- | sql/table.cc | 79 | ||||
-rw-r--r-- | sql/table.h | 2 |
21 files changed, 609 insertions, 201 deletions
diff --git a/sql/field.cc b/sql/field.cc index 6fd88e4e1ac..29882d1b8ed 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -7878,6 +7878,8 @@ int Field_enum::store(const char *from,uint length,CHARSET_INFO *cs) tmp=0; set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); } + if (!table->in_use->count_cuted_fields) + err= 0; } else set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); diff --git a/sql/field.h b/sql/field.h index 54a061276b6..6c77eded2d1 100644 --- a/sql/field.h +++ b/sql/field.h @@ -744,7 +744,7 @@ public: void sort_string(uchar *buff,uint length); uint32 pack_length() const { return 4; } void sql_type(String &str) const; - uint32 max_display_length() { return 11; } + uint32 max_display_length() { return MY_INT32_NUM_DECIMAL_DIGITS; } }; @@ -1173,6 +1173,11 @@ public: class Field_varstring :public Field_longstr { public: + /* + The maximum space available in a Field_varstring, in bytes. See + length_bytes. + */ + static const uint MAX_SIZE= UINT_MAX16; /* Store number of bytes used to store length (1 or 2) */ uint32 length_bytes; Field_varstring(uchar *ptr_arg, diff --git a/sql/field_conv.cc b/sql/field_conv.cc index e920fe70f26..63810c8b113 100644 --- a/sql/field_conv.cc +++ b/sql/field_conv.cc @@ -534,7 +534,21 @@ void Copy_field::set(uchar *to,Field *from) } +/* + To do: + + If 'save\ is set to true and the 'from' is a blob field, do_copy is set to + do_save_blob rather than do_conv_blob. The only differences between them + appears to be: + - do_save_blob allocates and uses an intermediate buffer before calling + Field_blob::store. Is this in order to trigger the call to + well_formed_copy_nchars, by changing the pointer copy->tmp.ptr()? + That call will take place anyway in all known cases. + + - The above causes a truncation to MAX_FIELD_WIDTH. Is this the intended + effect? Truncation is handled by well_formed_copy_nchars anyway. + */ void Copy_field::set(Field *to,Field *from,bool save) { if (to->type() == MYSQL_TYPE_NULL) diff --git a/sql/handler.h b/sql/handler.h index 952e7351a68..6a16dd96f49 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -852,7 +852,15 @@ public: ulonglong max_index_file_length; ulonglong delete_length; /* Free bytes */ ulonglong auto_increment_value; - ha_rows records; /* Estimated records in table */ + /* + The number of records in the table. + 0 - means the table has exactly 0 rows + other - if (table_flags() & HA_STATS_RECORDS_IS_EXACT) + the value is the exact number of records in the table + else + it is an estimate + */ + ha_rows records; ha_rows deleted; /* Deleted records */ ulong mean_rec_length; /* physical reclength */ time_t create_time; /* When table was created */ diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 6842febea4d..b8117f53d6b 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -819,7 +819,12 @@ get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg, { value= item->val_int(); *is_null= item->null_value; - if (item->field_type() == MYSQL_TYPE_DATE) + /* + Item_date_add_interval may return MYSQL_TYPE_STRING as the result + field type. To detect that the DATE value has been returned we + compare it with 1000000L - any DATE value should be less than it. + */ + if (item->field_type() == MYSQL_TYPE_DATE || value < 100000000L) value*= 1000000L; } else diff --git a/sql/item_func.cc b/sql/item_func.cc index 27500b259e0..b0346cc5224 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -993,6 +993,8 @@ longlong Item_func_unsigned::val_int() my_decimal tmp, *dec= args[0]->val_decimal(&tmp); if (!(null_value= args[0]->null_value)) my_decimal2int(E_DEC_FATAL_ERROR, dec, 1, &value); + else + value= 0; return value; } else if (args[0]->cast_to_int_type() != STRING_RESULT || @@ -3797,6 +3799,23 @@ Item_func_set_user_var::fix_length_and_dec() /* + Mark field in read_map + + NOTES + This is used by filesort to register used fields in a a temporary + column read set or to register used fields in a view +*/ + +bool Item_func_set_user_var::register_field_in_read_map(uchar *arg) +{ + TABLE *table= (TABLE *) arg; + if (result_field->table == table || !table) + bitmap_set_bit(result_field->table->read_set, result_field->field_index); + return 0; +} + + +/* Set value to user variable. SYNOPSYS @@ -4032,7 +4051,8 @@ bool Item_func_set_user_var::check(bool use_result_field) { DBUG_ENTER("Item_func_set_user_var::check"); - DBUG_ASSERT(!use_result_field || result_field); + if (use_result_field && !result_field) + use_result_field= FALSE; switch (cached_result_type) { case REAL_RESULT: @@ -4176,6 +4196,40 @@ my_decimal *Item_func_set_user_var::val_decimal(my_decimal *val) } +double Item_func_set_user_var::val_result() +{ + DBUG_ASSERT(fixed == 1); + check(TRUE); + update(); // Store expression + return entry->val_real(&null_value); +} + +longlong Item_func_set_user_var::val_int_result() +{ + DBUG_ASSERT(fixed == 1); + check(TRUE); + update(); // Store expression + return entry->val_int(&null_value); +} + +String *Item_func_set_user_var::str_result(String *str) +{ + DBUG_ASSERT(fixed == 1); + check(TRUE); + update(); // Store expression + return entry->val_str(&null_value, str, decimals); +} + + +my_decimal *Item_func_set_user_var::val_decimal_result(my_decimal *val) +{ + DBUG_ASSERT(fixed == 1); + check(TRUE); + update(); // Store expression + return entry->val_decimal(&null_value, val); +} + + void Item_func_set_user_var::print(String *str) { str->append(STRING_WITH_LEN("(@")); @@ -4258,9 +4312,11 @@ void Item_func_set_user_var::make_field(Send_field *tmp_field) TRUE Error */ -int Item_func_set_user_var::save_in_field(Field *field, bool no_conversions) +int Item_func_set_user_var::save_in_field(Field *field, bool no_conversions, + bool can_use_result_field) { - bool use_result_field= (result_field && result_field != field); + bool use_result_field= (!can_use_result_field ? 0 : + (result_field && result_field != field)); int error; /* Update the value of the user variable */ @@ -5189,10 +5245,11 @@ Item_func_sp::func_name() const { THD *thd= current_thd; /* Calculate length to avoid reallocation of string for sure */ - uint len= ((m_name->m_explicit_name ? m_name->m_db.length : 0 + + uint len= (((m_name->m_explicit_name ? m_name->m_db.length : 0) + m_name->m_name.length)*2 + //characters*quoting 2 + // ` and ` - 1 + // . + (m_name->m_explicit_name ? + 3 : 0) + // '`', '`' and '.' for the db 1 + // end of string ALIGN_SIZE(1)); // to avoid String reallocation String qname((char *)alloc_root(thd->mem_root, len), len, @@ -5322,6 +5379,8 @@ Item_func_sp::execute() { null_value= 1; context->process_error(thd); + if (thd->killed) + thd->send_kill_message(); return TRUE; } diff --git a/sql/item_func.h b/sql/item_func.h index 60205170166..8fc68f93e12 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -1236,6 +1236,10 @@ public: longlong val_int(); String *val_str(String *str); my_decimal *val_decimal(my_decimal *); + double val_result(); + longlong val_int_result(); + String *str_result(String *str); + my_decimal *val_decimal_result(my_decimal *); bool update_hash(void *ptr, uint length, enum Item_result type, CHARSET_INFO *cs, Derivation dv, bool unsigned_arg); bool send(Protocol *protocol, String *str_arg); @@ -1248,7 +1252,14 @@ public: void print(String *str); void print_as_stmt(String *str); const char *func_name() const { return "set_user_var"; } - int save_in_field(Field *field, bool no_conversions); + int save_in_field(Field *field, bool no_conversions, + bool can_use_result_field); + int save_in_field(Field *field, bool no_conversions) + { + return save_in_field(field, no_conversions, 1); + } + void save_org_in_field(Field *field) { (void)save_in_field(field, 1, 0); } + bool register_field_in_read_map(uchar *arg); }; diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 9494d1dc2ee..000abb313a0 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -818,6 +818,11 @@ bool Item_in_subselect::val_bool() if (exec()) { reset(); + /* + Must mark the IN predicate as NULL so as to make sure an enclosing NOT + predicate will return FALSE. See the comments in + subselect_uniquesubquery_engine::copy_ref_key for further details. + */ null_value= 1; return 0; } @@ -1979,10 +1984,38 @@ int subselect_uniquesubquery_engine::scan_table() DESCRIPTION Copy ref key and check for null parts in it. + Depending on the nullability and conversion problems this function + recognizes and processes the following states : + 1. Partial match on top level. This means IN has a value of FALSE + regardless of the data in the subquery table. + Detected by finding a NULL in the left IN operand of a top level + expression. + We may actually skip reading the subquery, so return TRUE to skip + the table scan in subselect_uniquesubquery_engine::exec and make + the value of the IN predicate a NULL (that is equal to FALSE on + top level). + 2. No exact match when IN is nested inside another predicate. + Detected by finding a NULL in the left IN operand when IN is not + a top level predicate. + We cannot have an exact match. But we must proceed further with a + table scan to find out if it's a partial match (and IN has a value + of NULL) or no match (and IN has a value of FALSE). + So we return FALSE to continue with the scan and see if there are + any record that would constitute a partial match (as we cannot + determine that from the index). + 3. Error converting the left IN operand to the column type of the + right IN operand. This counts as no match (and IN has the value of + FALSE). We mark the subquery table cursor as having no more rows + (to ensure that the processing that follows will not find a match) + and return FALSE, so IN is not treated as returning NULL. + RETURN - FALSE - ok, index lookup key without keys copied. - TRUE - an error occured while copying the key + FALSE - The value of the IN predicate is not known. Proceed to find the + value of the IN predicate using the determined values of + null_keypart and table->status. + TRUE - IN predicate has a value of NULL. Stop the processing right there + and return NULL to the outer predicates. */ bool subselect_uniquesubquery_engine::copy_ref_key() @@ -2002,13 +2035,37 @@ bool subselect_uniquesubquery_engine::copy_ref_key() function. */ null_keypart= (*copy)->null_key; - bool top_level= ((Item_in_subselect *) item)->is_top_level_item(); - if (null_keypart && !top_level) - break; - if ((tab->ref.key_err) & 1 || (null_keypart && top_level)) + if (null_keypart) + { + bool top_level= ((Item_in_subselect *) item)->is_top_level_item(); + if (top_level) + { + /* Partial match on top level */ + DBUG_RETURN(1); + } + else + { + /* No exact match when IN is nested inside another predicate */ + break; + } + } + + /* + Check if the error is equal to STORE_KEY_FATAL. This is not expressed + using the store_key::store_key_result enum because ref.key_err is a + boolean and we want to detect both TRUE and STORE_KEY_FATAL from the + space of the union of the values of [TRUE, FALSE] and + store_key::store_key_result. + TODO: fix the variable an return types. + */ + if (tab->ref.key_err & 1) { + /* + Error converting the left IN operand to the column type of the right + IN operand. + */ tab->table->status= STATUS_NOT_FOUND; - DBUG_RETURN(1); + break; } } DBUG_RETURN(0); @@ -2051,10 +2108,20 @@ int subselect_uniquesubquery_engine::exec() int error; TABLE *table= tab->table; empty_result_set= TRUE; + table->status= 0; /* TODO: change to use of 'full_scan' here? */ if (copy_ref_key()) DBUG_RETURN(1); + if (table->status) + { + /* + We know that there will be no rows even if we scan. + Can be set in copy_ref_key. + */ + ((Item_in_subselect *) item)->value= 0; + DBUG_RETURN(0); + } if (null_keypart) DBUG_RETURN(scan_table()); diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 69148d81b9a..08ce47573e7 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -431,7 +431,7 @@ Field *Item_sum::create_tmp_field(bool group, TABLE *table, break; case STRING_RESULT: if (max_length/collation.collation->mbmaxlen <= 255 || - convert_blob_length >=UINT_MAX16 || + convert_blob_length > Field_varstring::MAX_SIZE || !convert_blob_length) return make_string_field(table); field= new Field_varstring(convert_blob_length, maybe_null, @@ -3290,14 +3290,20 @@ bool Item_func_group_concat::setup(THD *thd) tmp_table_param->force_copy_fields= force_copy_fields; DBUG_ASSERT(table == 0); /* + Currently we have to force conversion of BLOB values to VARCHAR's + if we are to store them in TREE objects used for ORDER BY and + DISTINCT. This leads to truncation if the BLOB's size exceeds + Field_varstring::MAX_SIZE. + */ + if (arg_count_order > 0 || distinct) + set_if_smaller(tmp_table_param->convert_blob_length, + Field_varstring::MAX_SIZE); + /* We have to create a temporary table to get descriptions of fields (types, sizes and so on). Note that in the table, we first have the ORDER BY fields, then the field list. - - We need to set set_sum_field in true for storing value of blob in buffer - of a record instead of a pointer of one. */ if (!(table= create_tmp_table(thd, tmp_table_param, all_fields, (ORDER*) 0, 0, TRUE, diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 3bc63eaf5f9..6f38a1acc67 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -2058,27 +2058,6 @@ bool Item_date_add_interval::get_date(MYSQL_TIME *ltime, uint fuzzy_date) if ((null_value= date_add_interval(ltime, int_type, interval))) return 1; - - /* Adjust cached_field_type according to the detected type. */ - if (cached_field_type == MYSQL_TYPE_STRING) - { - switch (ltime->time_type) - { - case MYSQL_TIMESTAMP_DATE: - cached_field_type= MYSQL_TYPE_DATE; - break; - case MYSQL_TIMESTAMP_DATETIME: - cached_field_type= MYSQL_TYPE_DATETIME; - break; - case MYSQL_TIMESTAMP_TIME: - cached_field_type= MYSQL_TYPE_TIME; - break; - default: - /* Shouldn't get here. */ - DBUG_ASSERT(0); - break; - } - } return 0; } diff --git a/sql/my_decimal.h b/sql/my_decimal.h index d6cefb3e080..f9ba99a4509 100644 --- a/sql/my_decimal.h +++ b/sql/my_decimal.h @@ -36,13 +36,17 @@ C_MODE_END /* maximum length of buffer in our big digits (uint32) */ #define DECIMAL_BUFF_LENGTH 9 + +/* the number of digits that my_decimal can possibly contain */ +#define DECIMAL_MAX_POSSIBLE_PRECISION (DECIMAL_BUFF_LENGTH * 9) + /* maximum guaranteed precision of number in decimal digits (number of our digits * number of decimal digits in one our big digit - number of decimal - digits in one our big digit decreased on 1 (because we always put decimal + digits in one our big digit decreased by 1 (because we always put decimal point on the border of our big digits)) */ -#define DECIMAL_MAX_PRECISION ((DECIMAL_BUFF_LENGTH * 9) - 8*2) +#define DECIMAL_MAX_PRECISION (DECIMAL_MAX_POSSIBLE_PRECISION - 8*2) #define DECIMAL_MAX_SCALE 30 #define DECIMAL_NOT_SPECIFIED 31 @@ -50,7 +54,7 @@ C_MODE_END maximum length of string representation (number of maximum decimal digits + 1 position for sign + 1 position for decimal point) */ -#define DECIMAL_MAX_STR_LENGTH (DECIMAL_MAX_PRECISION + 2) +#define DECIMAL_MAX_STR_LENGTH (DECIMAL_MAX_POSSIBLE_PRECISION + 2) /* maximum size of packet length */ diff --git a/sql/mysqld.cc b/sql/mysqld.cc index be704625e92..8442d67515b 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -3813,6 +3813,11 @@ we force server id to 2, but this MySQL server will not act as a slave."); freopen(log_error_file,"a+",stderr); FreeConsole(); // Remove window } + else + { + /* Don't show error dialog box when on foreground: it stops the server */ + SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS); + } #endif /* diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 3d5f2f0f50d..bbf281caee0 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -1342,6 +1342,9 @@ err_with_cleanup: free_root(&call_mem_root, MYF(0)); thd->spcont= octx; + if (thd->killed) + thd->send_kill_message(); + DBUG_RETURN(err_status); } diff --git a/sql/sql_base.cc b/sql/sql_base.cc index b21dc9d47f8..26d23d1ea57 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -6811,7 +6811,7 @@ fill_record(THD *thd, Field **ptr, List<Item> &values, bool ignore_errors) table= (*ptr)->table; table->auto_increment_field_not_null= FALSE; } - while ((field = *ptr++)) + while ((field = *ptr++) && !thd->net.report_error) { value=v++; table= field->table; diff --git a/sql/sql_class.h b/sql/sql_class.h index 7729b597098..d9dc4117319 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -2308,6 +2308,11 @@ class multi_update :public select_result_interceptor List <Item> *fields, *values; List <Item> **fields_for_table, **values_for_table; uint table_count; + /* + List of tables referenced in the CHECK OPTION condition of + the updated view excluding the updated table. + */ + List <TABLE> unupdated_check_opt_tables; Copy_field *copy_field; enum enum_duplicates handle_duplicates; bool do_update, trans_safe; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index c6ef16d46a3..cb988049b4c 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -49,15 +49,15 @@ static int sort_keyuse(KEYUSE *a,KEYUSE *b); static void set_position(JOIN *join,uint index,JOIN_TAB *table,KEYUSE *key); static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse, table_map used_tables); -static void choose_plan(JOIN *join,table_map join_tables); +static bool choose_plan(JOIN *join,table_map join_tables); static void best_access_path(JOIN *join, JOIN_TAB *s, THD *thd, table_map remaining_tables, uint idx, double record_count, double read_time); static void optimize_straight_join(JOIN *join, table_map join_tables); -static void greedy_search(JOIN *join, table_map remaining_tables, +static bool greedy_search(JOIN *join, table_map remaining_tables, uint depth, uint prune_level); -static void best_extension_by_limited_search(JOIN *join, +static bool best_extension_by_limited_search(JOIN *join, table_map remaining_tables, uint idx, double record_count, double read_time, uint depth, @@ -69,7 +69,7 @@ static int join_tab_cmp_straight(const void* ptr1, const void* ptr2); TODO: 'find_best' is here only temporarily until 'greedy_search' is tested and approved. */ -static void find_best(JOIN *join,table_map rest_tables,uint index, +static bool find_best(JOIN *join,table_map rest_tables,uint index, double record_count,double read_time); static uint cache_record_length(JOIN *join,uint index); static double prev_record_reads(JOIN *join, uint idx, table_map found_ref); @@ -2755,7 +2755,8 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables, COND *conds, if (join->const_tables != join->tables) { optimize_keyuse(join, keyuse_array); - choose_plan(join, all_table_map & ~join->const_table_map); + if (choose_plan(join, all_table_map & ~join->const_table_map)) + DBUG_RETURN(TRUE); } else { @@ -4404,11 +4405,12 @@ best_access_path(JOIN *join, the array 'join->best_positions', and the cost of the plan in 'join->best_read'. - RETURN - None + RETURN VALUES + FALSE ok + TRUE Fatal error */ -static void +static bool choose_plan(JOIN *join, table_map join_tables) { uint search_depth= join->thd->variables.optimizer_search_depth; @@ -4441,14 +4443,16 @@ choose_plan(JOIN *join, table_map join_tables) the greedy version. Will be removed when greedy_search is approved. */ join->best_read= DBL_MAX; - find_best(join, join_tables, join->const_tables, 1.0, 0.0); + if (find_best(join, join_tables, join->const_tables, 1.0, 0.0)) + DBUG_RETURN(TRUE); } else { if (search_depth == 0) /* Automatically determine a reasonable value for 'search_depth' */ search_depth= determine_search_depth(join); - greedy_search(join, join_tables, search_depth, prune_level); + if (greedy_search(join, join_tables, search_depth, prune_level)) + DBUG_RETURN(TRUE); } } @@ -4456,7 +4460,7 @@ choose_plan(JOIN *join, table_map join_tables) Store the cost of this query into a user variable */ join->thd->status_var.last_query_cost= join->best_read; - DBUG_VOID_RETURN; + DBUG_RETURN(FALSE); } @@ -4684,11 +4688,12 @@ optimize_straight_join(JOIN *join, table_map join_tables) In the future, 'greedy_search' might be extended to support other implementations of 'best_extension', e.g. some simpler quadratic procedure. - RETURN - None + RETURN VALUES + FALSE ok + TRUE Fatal error */ -static void +static bool greedy_search(JOIN *join, table_map remaining_tables, uint search_depth, @@ -4710,8 +4715,9 @@ greedy_search(JOIN *join, do { /* Find the extension of the current QEP with the lowest cost */ join->best_read= DBL_MAX; - best_extension_by_limited_search(join, remaining_tables, idx, record_count, - read_time, search_depth, prune_level); + if (best_extension_by_limited_search(join, remaining_tables, idx, record_count, + read_time, search_depth, prune_level)) + DBUG_RETURN(TRUE); if (size_remain <= search_depth) { @@ -4722,7 +4728,7 @@ greedy_search(JOIN *join, DBUG_EXECUTE("opt", print_plan(join, join->tables, record_count, read_time, read_time, "optimal");); - DBUG_VOID_RETURN; + DBUG_RETURN(FALSE); } /* select the first table in the optimal extension as most promising */ @@ -4867,11 +4873,12 @@ greedy_search(JOIN *join, The parameter 'search_depth' provides control over the recursion depth, and thus the size of the resulting optimal plan. - RETURN - None + RETURN VALUES + FALSE ok + TRUE Fatal error */ -static void +static bool best_extension_by_limited_search(JOIN *join, table_map remaining_tables, uint idx, @@ -4880,11 +4887,12 @@ best_extension_by_limited_search(JOIN *join, uint search_depth, uint prune_level) { + DBUG_ENTER("best_extension_by_limited_search"); + THD *thd= join->thd; if (thd->killed) // Abort - return; + DBUG_RETURN(TRUE); - DBUG_ENTER("best_extension_by_limited_search"); DBUG_EXECUTE("opt", print_plan(join, idx, read_time, record_count, idx, "SOFAR:");); @@ -4966,15 +4974,14 @@ best_extension_by_limited_search(JOIN *join, if ( (search_depth > 1) && (remaining_tables & ~real_table_bit) ) { /* Recursively expand the current partial plan */ swap_variables(JOIN_TAB*, join->best_ref[idx], *pos); - best_extension_by_limited_search(join, - remaining_tables & ~real_table_bit, - idx + 1, - current_record_count, - current_read_time, - search_depth - 1, - prune_level); - if (thd->killed) - DBUG_VOID_RETURN; + if (best_extension_by_limited_search(join, + remaining_tables & ~real_table_bit, + idx + 1, + current_record_count, + current_read_time, + search_depth - 1, + prune_level)) + DBUG_RETURN(TRUE); swap_variables(JOIN_TAB*, join->best_ref[idx], *pos); } else @@ -5003,19 +5010,26 @@ best_extension_by_limited_search(JOIN *join, restore_prev_nj_state(s); } } - DBUG_VOID_RETURN; + DBUG_RETURN(FALSE); } /* TODO: this function is here only temporarily until 'greedy_search' is tested and accepted. + + RETURN VALUES + FALSE ok + TRUE Fatal error */ -static void +static bool find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, double read_time) { + DBUG_ENTER("find_best"); THD *thd= join->thd; + if (thd->killed) + DBUG_RETURN(TRUE); if (!rest_tables) { DBUG_PRINT("best",("read_time: %g record_count: %g",read_time, @@ -5032,10 +5046,10 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, sizeof(POSITION)*idx); join->best_read= read_time - 0.001; } - return; + DBUG_RETURN(FALSE); } if (read_time+record_count/(double) TIME_FOR_COMPARE >= join->best_read) - return; /* Found better before */ + DBUG_RETURN(FALSE); /* Found better before */ JOIN_TAB *s; double best_record_count=DBL_MAX,best_read_time=DBL_MAX; @@ -5068,10 +5082,9 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, best_read_time=current_read_time; } swap_variables(JOIN_TAB*, join->best_ref[idx], *pos); - find_best(join,rest_tables & ~real_table_bit,idx+1, - current_record_count,current_read_time); - if (thd->killed) - return; + if (find_best(join,rest_tables & ~real_table_bit,idx+1, + current_record_count,current_read_time)) + DBUG_RETURN(TRUE); swap_variables(JOIN_TAB*, join->best_ref[idx], *pos); } restore_prev_nj_state(s); @@ -5079,6 +5092,7 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, break; // Don't test all combinations } } + DBUG_RETURN(FALSE); } @@ -6099,13 +6113,39 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) } /* - Push down all predicates from on expressions. - Each of these predicated are guarded by a variable + Push down conditions from all on expressions. + Each of these conditions are guarded by a variable that turns if off just before null complemented row for - outer joins is formed. Thus, the predicates from an + outer joins is formed. Thus, the condition from an 'on expression' are guaranteed not to be checked for the null complemented row. */ + + /* First push down constant conditions from on expressions */ + for (JOIN_TAB *join_tab= join->join_tab+join->const_tables; + join_tab < join->join_tab+join->tables ; join_tab++) + { + if (*join_tab->on_expr_ref) + { + JOIN_TAB *cond_tab= join_tab->first_inner; + COND *tmp= make_cond_for_table(*join_tab->on_expr_ref, + join->const_table_map, + (table_map) 0); + if (!tmp) + continue; + tmp= new Item_func_trig_cond(tmp, &cond_tab->not_null_compl); + if (!tmp) + DBUG_RETURN(1); + tmp->quick_fix_field(); + cond_tab->select_cond= !cond_tab->select_cond ? tmp : + new Item_cond_and(cond_tab->select_cond,tmp); + if (!cond_tab->select_cond) + DBUG_RETURN(1); + cond_tab->select_cond->quick_fix_field(); + } + } + + /* Push down non-constant conditions from on expressions */ JOIN_TAB *last_tab= tab; while (first_inner_tab && first_inner_tab->last_inner == last_tab) { @@ -6553,7 +6593,6 @@ void JOIN::cleanup(bool full) for (tab= join_tab, end= tab+tables; tab != end; tab++) tab->cleanup(); table= 0; - tables= 0; } else { @@ -8992,7 +9031,7 @@ Field *create_tmp_field_from_field(THD *thd, Field *org_field, Make sure that the blob fits into a Field_varstring which has 2-byte lenght. */ - if (convert_blob_length && convert_blob_length < UINT_MAX16 && + if (convert_blob_length && convert_blob_length <= Field_varstring::MAX_SIZE && (org_field->flags & BLOB_FLAG)) new_field= new Field_varstring(convert_blob_length, org_field->maybe_null(), @@ -9060,8 +9099,13 @@ static Field *create_tmp_field_from_item(THD *thd, Item *item, TABLE *table, item->name, item->decimals, TRUE); break; case INT_RESULT: - /* Select an integer type with the minimal fit precision */ - if (item->max_length > MY_INT32_NUM_DECIMAL_DIGITS) + /* + Select an integer type with the minimal fit precision. + MY_INT32_NUM_DECIMAL_DIGITS is sign inclusive, don't consider the sign. + Values with MY_INT32_NUM_DECIMAL_DIGITS digits may or may not fit into + Field_long : make them Field_longlong. + */ + if (item->max_length >= (MY_INT32_NUM_DECIMAL_DIGITS - 1)) new_field=new Field_longlong(item->max_length, maybe_null, item->name, item->unsigned_flag); else @@ -9085,7 +9129,8 @@ static Field *create_tmp_field_from_item(THD *thd, Item *item, TABLE *table, 2-byte lenght. */ else if (item->max_length/item->collation.collation->mbmaxlen > 255 && - convert_blob_length < UINT_MAX16 && convert_blob_length) + convert_blob_length <= Field_varstring::MAX_SIZE && + convert_blob_length) new_field= new Field_varstring(convert_blob_length, maybe_null, item->name, table->s, item->collation.collation); @@ -15531,8 +15576,8 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, break; } } - if (tab->next_select == sub_select_cache) - extra.append(STRING_WITH_LEN("; Using join cache")); + if (i > 0 && tab[-1].next_select == sub_select_cache) + extra.append(STRING_WITH_LEN("; Using join buffer")); /* Skip initial "; "*/ const char *str= extra.ptr(); diff --git a/sql/sql_table.cc b/sql/sql_table.cc index bb0f7649272..3d8c0875452 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -37,7 +37,8 @@ static int copy_data_between_tables(TABLE *from,TABLE *to, List<create_field> &create, bool ignore, uint order_num, ORDER *order, ha_rows *copied,ha_rows *deleted, - enum enum_enable_or_disable keys_onoff); + enum enum_enable_or_disable keys_onoff, + bool error_if_not_empty); static bool prepare_blob_field(THD *thd, create_field *sql_field); static bool check_engine(THD *, const char *, HA_CREATE_INFO *); @@ -6069,14 +6070,18 @@ view_err: need_copy_table= ALTER_TABLE_DATA_CHANGED; else { + enum_compare_tables_result need_copy_table_res; /* Check how much the tables differ. */ if (compare_tables(table, alter_info, create_info, order_num, - &need_copy_table, + &need_copy_table_res, &key_info_buffer, &index_drop_buffer, &index_drop_count, &index_add_buffer, &index_add_count)) goto err; + + if (need_copy_table == ALTER_TABLE_METADATA_ONLY) + need_copy_table= need_copy_table_res; } /* @@ -6311,7 +6316,8 @@ view_err: error= copy_data_between_tables(table, new_table, alter_info->create_list, ignore, order_num, order, &copied, &deleted, - alter_info->keys_onoff); + alter_info->keys_onoff, + error_if_not_empty); } else { @@ -6663,6 +6669,38 @@ err1: VOID(quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP)); err: + /* + No default value was provided for a DATE/DATETIME field, the + current sql_mode doesn't allow the '0000-00-00' value and + the table to be altered isn't empty. + Report error here. + */ + if (error_if_not_empty && thd->row_count) + { + const char *f_val= 0; + enum enum_mysql_timestamp_type t_type= MYSQL_TIMESTAMP_DATE; + switch (new_datetime_field->sql_type) + { + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_NEWDATE: + f_val= "0000-00-00"; + t_type= MYSQL_TIMESTAMP_DATE; + break; + case MYSQL_TYPE_DATETIME: + f_val= "0000-00-00 00:00:00"; + t_type= MYSQL_TIMESTAMP_DATETIME; + break; + default: + /* Shouldn't get here. */ + DBUG_ASSERT(0); + } + bool save_abort_on_warning= thd->abort_on_warning; + thd->abort_on_warning= TRUE; + make_truncated_value_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, + f_val, strlength(f_val), t_type, + new_datetime_field->field_name); + thd->abort_on_warning= save_abort_on_warning; + } if (name_lock) { pthread_mutex_lock(&LOCK_open); @@ -6692,7 +6730,8 @@ copy_data_between_tables(TABLE *from,TABLE *to, uint order_num, ORDER *order, ha_rows *copied, ha_rows *deleted, - enum enum_enable_or_disable keys_onoff) + enum enum_enable_or_disable keys_onoff, + bool error_if_not_empty) { int error; Copy_field *copy,*copy_end; @@ -6804,6 +6843,12 @@ copy_data_between_tables(TABLE *from,TABLE *to, break; } thd->row_count++; + /* Return error if source table isn't empty. */ + if (error_if_not_empty) + { + error= 1; + break; + } if (to->next_number_field) { if (auto_increment_field_copied) diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 66f69ae0421..d9bf6b4d918 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -1124,6 +1124,7 @@ int multi_update::prepare(List<Item> ¬_used_values, List_iterator_fast<Item> field_it(*fields); List_iterator_fast<Item> value_it(*values); uint i, max_fields; + uint leaf_table_count= 0; DBUG_ENTER("multi_update::prepare"); thd->count_cuted_fields= CHECK_FIELD_WARN; @@ -1157,6 +1158,7 @@ int multi_update::prepare(List<Item> ¬_used_values, { /* TODO: add support of view of join support */ TABLE *table=table_ref->table; + leaf_table_count++; if (tables_to_update & table->map) { TABLE_LIST *tl= (TABLE_LIST*) thd->memdup((char*) table_ref, @@ -1218,7 +1220,7 @@ int multi_update::prepare(List<Item> ¬_used_values, /* Allocate copy fields */ max_fields=0; for (i=0 ; i < table_count ; i++) - set_if_bigger(max_fields, fields_for_table[i]->elements); + set_if_bigger(max_fields, fields_for_table[i]->elements + leaf_table_count); copy_field= new Copy_field[max_fields]; DBUG_RETURN(thd->is_fatal_error != 0); } @@ -1309,13 +1311,22 @@ multi_update::initialize_tables(JOIN *join) trans_safe= transactional_tables= main_table->file->has_transactions(); table_to_update= 0; + /* Any update has at least one pair (field, value) */ + DBUG_ASSERT(fields->elements); + /* + Only one table may be modified by UPDATE of an updatable view. + For an updatable view first_table_for_update indicates this + table. + For a regular multi-update it refers to some updated table. + */ + TABLE *first_table_for_update= ((Item_field *) fields->head())->field->table; + /* Create a temporary table for keys to all tables, except main table */ for (table_ref= update_tables; table_ref; table_ref= table_ref->next_local) { TABLE *table=table_ref->table; uint cnt= table_ref->shared; - Item_field *ifield; - List<Item> temp_fields= *fields_for_table[cnt]; + List<Item> temp_fields; ORDER group; TMP_TABLE_PARAM *tmp_param; @@ -1332,27 +1343,56 @@ multi_update::initialize_tables(JOIN *join) } table->prepare_for_position(); + if (table == first_table_for_update && table_ref->check_option) + { + table_map unupdated_tables= table_ref->check_option->used_tables() & + ~first_table_for_update->map; + for (TABLE_LIST *tbl_ref =leaves; + unupdated_tables && tbl_ref; + tbl_ref= tbl_ref->next_leaf) + { + if (unupdated_tables & tbl_ref->table->map) + unupdated_tables&= ~tbl_ref->table->map; + else + continue; + if (unupdated_check_opt_tables.push_back(tbl_ref->table)) + DBUG_RETURN(1); + } + } + tmp_param= tmp_table_param+cnt; + /* Create a temporary table to store all fields that are changed for this table. The first field in the temporary table is a pointer to the - original row so that we can find and update it + original row so that we can find and update it. For the updatable + VIEW a few following fields are rowids of tables used in the CHECK + OPTION condition. */ - /* ok to be on stack as this is not referenced outside of this func */ - Field_string offset(table->file->ref_length, 0, "offset", - &my_charset_bin); - offset.init(table); - /* - The field will be converted to varstring when creating tmp table if - table to be updated was created by mysql 4.1. Deny this. - */ - offset.can_alter_field_type= 0; - if (!(ifield= new Item_field(((Field *) &offset)))) - DBUG_RETURN(1); - ifield->maybe_null= 0; - if (temp_fields.push_front(ifield)) - DBUG_RETURN(1); + List_iterator_fast<TABLE> tbl_it(unupdated_check_opt_tables); + TABLE *tbl= table; + do + { + Field_string *field= new Field_string(tbl->file->ref_length, 0, + tbl->alias, &my_charset_bin); + if (!field) + DBUG_RETURN(1); + field->init(tbl); + /* + The field will be converted to varstring when creating tmp table if + table to be updated was created by mysql 4.1. Deny this. + */ + field->can_alter_field_type= 0; + Item_field *ifield= new Item_field((Field *) field); + if (!ifield) + DBUG_RETURN(1); + ifield->maybe_null= 0; + if (temp_fields.push_back(ifield)) + DBUG_RETURN(1); + } while ((tbl= tbl_it++)); + + temp_fields.concat(fields_for_table[cnt]); /* Make an unique key over the first field to avoid duplicated updates */ bzero((char*) &group, sizeof(group)); @@ -1504,11 +1544,26 @@ bool multi_update::send_data(List<Item> ¬_used_values) { int error; TABLE *tmp_table= tmp_tables[offset]; - table->file->position(table->record[0]); - fill_record(thd, tmp_table->field+1, *values_for_table[offset], 1); - /* Store pointer to row */ - memcpy((char*) tmp_table->field[0]->ptr, - (char*) table->file->ref, table->file->ref_length); + /* + For updatable VIEW store rowid of the updated table and + rowids of tables used in the CHECK OPTION condition. + */ + uint field_num= 0; + List_iterator_fast<TABLE> tbl_it(unupdated_check_opt_tables); + TABLE *tbl= table; + do + { + tbl->file->position(tbl->record[0]); + memcpy((char*) tmp_table->field[field_num]->ptr, + (char*) tbl->file->ref, tbl->file->ref_length); + field_num++; + } while ((tbl= tbl_it++)); + + /* Store regular updated fields in the row. */ + fill_record(thd, + tmp_table->field + 1 + unupdated_check_opt_tables.elements, + *values_for_table[offset], 1); + /* Write row, ignoring duplicated updates to a row */ error= tmp_table->file->ha_write_row(tmp_table->record[0]); if (error != HA_ERR_FOUND_DUPP_KEY && error != HA_ERR_FOUND_DUPP_UNIQUE) @@ -1558,9 +1613,10 @@ void multi_update::send_error(uint errcode,const char *err) int multi_update::do_updates(bool from_send_error) { TABLE_LIST *cur_table; - int local_error; + int local_error= 0; ha_rows org_updated; TABLE *table, *tmp_table; + List_iterator_fast<TABLE> check_opt_it(unupdated_check_opt_tables); DBUG_ENTER("do_updates"); do_update= 0; // Don't retry this function @@ -1568,8 +1624,8 @@ int multi_update::do_updates(bool from_send_error) DBUG_RETURN(0); for (cur_table= update_tables; cur_table; cur_table= cur_table->next_local) { - uchar *ref_pos; bool can_compare_record; + uint offset= cur_table->shared; table = cur_table->table; if (table == table_to_update) @@ -1580,11 +1636,20 @@ int multi_update::do_updates(bool from_send_error) (void) table->file->ha_rnd_init(0); table->file->extra(HA_EXTRA_NO_CACHE); + check_opt_it.rewind(); + while(TABLE *tbl= check_opt_it++) + { + if (tbl->file->ha_rnd_init(1)) + goto err; + tbl->file->extra(HA_EXTRA_CACHE); + } + /* Setup copy functions to copy fields from temporary table */ - List_iterator_fast<Item> field_it(*fields_for_table[cur_table->shared]); - Field **field= tmp_table->field+1; // Skip row pointer + List_iterator_fast<Item> field_it(*fields_for_table[offset]); + Field **field= tmp_table->field + + 1 + unupdated_check_opt_tables.elements; // Skip row pointers Copy_field *copy_field_ptr= copy_field, *copy_field_end; for ( ; *field ; field++) { @@ -1601,7 +1666,6 @@ int multi_update::do_updates(bool from_send_error) bitmap_is_subset(table->write_set, table->read_set)); - ref_pos= tmp_table->field[0]->ptr; for (;;) { if (thd->killed && trans_safe) @@ -1614,8 +1678,20 @@ int multi_update::do_updates(bool from_send_error) continue; // May happen on dup key goto err; } - if ((local_error= table->file->rnd_pos(table->record[0], ref_pos))) - goto err; + + /* call rnd_pos() using rowids from temporary table */ + check_opt_it.rewind(); + TABLE *tbl= table; + uint field_num= 0; + do + { + if((local_error= + tbl->file->rnd_pos(tbl->record[0], + (uchar *) tmp_table->field[field_num]->ptr))) + goto err; + field_num++; + } while((tbl= check_opt_it++)); + table->status|= STATUS_UPDATED; store_record(table,record[1]); @@ -1666,6 +1742,10 @@ int multi_update::do_updates(bool from_send_error) } (void) table->file->ha_rnd_end(); (void) tmp_table->file->ha_rnd_end(); + check_opt_it.rewind(); + while (TABLE *tbl= check_opt_it++) + tbl->file->ha_rnd_end(); + } DBUG_RETURN(0); @@ -1679,6 +1759,9 @@ err: err2: (void) table->file->ha_rnd_end(); (void) tmp_table->file->ha_rnd_end(); + check_opt_it.rewind(); + while (TABLE *tbl= check_opt_it++) + tbl->file->ha_rnd_end(); if (updated != org_updated) { diff --git a/sql/sql_view.cc b/sql/sql_view.cc index c86083e52fc..8b0092feac8 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -692,6 +692,7 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, char dir_buff[FN_REFLEN], path_buff[FN_REFLEN]; const char *endp; LEX_STRING dir, file, path; + int error= 0; DBUG_ENTER("mysql_register_view"); /* print query */ @@ -702,9 +703,56 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, lex->unit.print(&str); thd->variables.sql_mode|= sql_mode; } - str.append('\0'); DBUG_PRINT("info", ("View: %s", str.ptr())); + /* fill structure */ + view->query.str= str.c_ptr_safe(); + view->query.length= str.length(); + view->source.str= thd->query + thd->lex->create_view_select_start; + endp= view->source.str; + endp= skip_rear_comments(endp, thd->query + thd->query_length); + view->source.length= endp - view->source.str; + view->file_version= 1; + view->calc_md5(md5); + view->md5.str= md5; + view->md5.length= 32; + can_be_merged= lex->can_be_merged(); + if (lex->create_view_algorithm == VIEW_ALGORITHM_MERGE && + !lex->can_be_merged()) + { + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_VIEW_MERGE, + ER(ER_WARN_VIEW_MERGE)); + lex->create_view_algorithm= VIEW_ALGORITHM_UNDEFINED; + } + view->algorithm= lex->create_view_algorithm; + view->definer.user= lex->definer->user; + view->definer.host= lex->definer->host; + view->view_suid= lex->create_view_suid; + view->with_check= lex->create_view_check; + if ((view->updatable_view= (can_be_merged && + view->algorithm != VIEW_ALGORITHM_TMPTABLE))) + { + /* TODO: change here when we will support UNIONs */ + for (TABLE_LIST *tbl= (TABLE_LIST *)lex->select_lex.table_list.first; + tbl; + tbl= tbl->next_local) + { + if ((tbl->view && !tbl->updatable_view) || tbl->schema_table) + { + view->updatable_view= 0; + break; + } + for (TABLE_LIST *up= tbl; up; up= up->embedding) + { + if (up->outer_join) + { + view->updatable_view= 0; + goto loop_out; + } + } + } + } +loop_out: /* print file name */ dir.length= build_table_filename(dir_buff, sizeof(dir_buff), view->db, "", "", 0); @@ -736,16 +784,21 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, if (mode == VIEW_CREATE_NEW) { my_error(ER_TABLE_EXISTS_ERROR, MYF(0), view->alias); - DBUG_RETURN(-1); + error= -1; + goto err; } if (!(parser= sql_parse_prepare(&path, thd->mem_root, 0))) - DBUG_RETURN(1); + { + error= 1; + goto err; + } if (!parser->ok() || !is_equal(&view_type, parser->type())) { my_error(ER_WRONG_OBJECT, MYF(0), view->db, view->table_name, "VIEW"); - DBUG_RETURN(-1); + error= -1; + goto err; } /* @@ -758,7 +811,8 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, view_parameters + revision_number_position, 1, &file_parser_dummy_hook)) { - DBUG_RETURN(thd->net.report_error? -1 : 0); + error= thd->net.report_error? -1 : 0; + goto err; } } else @@ -766,58 +820,11 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, if (mode == VIEW_ALTER) { my_error(ER_NO_SUCH_TABLE, MYF(0), view->db, view->alias); - DBUG_RETURN(-1); - } - } - } - /* fill structure */ - view->query.str= (char*)str.ptr(); - view->query.length= str.length()-1; // we do not need last \0 - view->source.str= thd->query + thd->lex->create_view_select_start; - endp= view->source.str; - endp= skip_rear_comments(thd->charset(), endp, thd->query + thd->query_length); - view->source.length= endp - view->source.str; - view->file_version= 1; - view->calc_md5(md5); - view->md5.str= md5; - view->md5.length= 32; - can_be_merged= lex->can_be_merged(); - if (lex->create_view_algorithm == VIEW_ALGORITHM_MERGE && - !lex->can_be_merged()) - { - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_VIEW_MERGE, - ER(ER_WARN_VIEW_MERGE)); - lex->create_view_algorithm= VIEW_ALGORITHM_UNDEFINED; - } - view->algorithm= lex->create_view_algorithm; - view->definer.user= lex->definer->user; - view->definer.host= lex->definer->host; - view->view_suid= lex->create_view_suid; - view->with_check= lex->create_view_check; - if ((view->updatable_view= (can_be_merged && - view->algorithm != VIEW_ALGORITHM_TMPTABLE))) - { - /* TODO: change here when we will support UNIONs */ - for (TABLE_LIST *tbl= (TABLE_LIST *)lex->select_lex.table_list.first; - tbl; - tbl= tbl->next_local) - { - if ((tbl->view && !tbl->updatable_view) || tbl->schema_table) - { - view->updatable_view= 0; - break; - } - for (TABLE_LIST *up= tbl; up; up= up->embedding) - { - if (up->outer_join) - { - view->updatable_view= 0; - goto loop_out; - } + error= -1; + goto err; } } } -loop_out: /* Check that table of main select do not used in subqueries. @@ -842,15 +849,23 @@ loop_out: !view->updatable_view) { my_error(ER_VIEW_NONUPD_CHECK, MYF(0), view->db, view->table_name); - DBUG_RETURN(-1); + error= -1; + goto err; } if (sql_create_definition_file(&dir, &file, view_file_type, (uchar*)view, view_parameters, num_view_backups)) { - DBUG_RETURN(thd->net.report_error? -1 : 1); + error= thd->net.report_error? -1 : 1; + goto err; } DBUG_RETURN(0); +err: + view->query.str= NULL; + view->query.length= 0; + view->md5.str= NULL; + view->md5.length= 0; + DBUG_RETURN(error); } diff --git a/sql/table.cc b/sql/table.cc index a70b5bd4bef..745f3a2a34e 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -2914,6 +2914,47 @@ bool st_table_list::prep_where(THD *thd, Item **conds, /* + Merge ON expressions for a view + + SYNOPSIS + merge_on_conds() + thd thread handle + table table for the VIEW + is_cascaded TRUE <=> merge ON expressions from underlying views + + DESCRIPTION + This function returns the result of ANDing the ON expressions + of the given view and all underlying views. The ON expressions + of the underlying views are added only if is_cascaded is TRUE. + + RETURN + Pointer to the built expression if there is any. + Otherwise and in the case of a failure NULL is returned. +*/ + +static Item * +merge_on_conds(THD *thd, TABLE_LIST *table, bool is_cascaded) +{ + DBUG_ENTER("merge_on_conds"); + + Item *cond= NULL; + DBUG_PRINT("info", ("alias: %s", table->alias)); + if (table->on_expr) + cond= table->on_expr->copy_andor_structure(thd); + if (!table->nested_join) + DBUG_RETURN(cond); + List_iterator<TABLE_LIST> li(table->nested_join->join_list); + while (TABLE_LIST *tbl= li++) + { + if (tbl->view && !is_cascaded) + continue; + cond= and_conds(cond, merge_on_conds(thd, tbl, is_cascaded)); + } + DBUG_RETURN(cond); +} + + +/* Prepare check option expression of table SYNOPSIS @@ -2929,8 +2970,8 @@ bool st_table_list::prep_where(THD *thd, Item **conds, VIEW_CHECK_LOCAL option. NOTE - This method build check options for every call - (usual execution or every SP/PS call) + This method builds check option condition to use it later on + every call (usual execution or every SP/PS call). This method have to be called after WHERE preparation (st_table_list::prep_where) @@ -2942,38 +2983,42 @@ bool st_table_list::prep_where(THD *thd, Item **conds, bool st_table_list::prep_check_option(THD *thd, uint8 check_opt_type) { DBUG_ENTER("st_table_list::prep_check_option"); + bool is_cascaded= check_opt_type == VIEW_CHECK_CASCADED; for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local) { /* see comment of check_opt_type parameter */ - if (tbl->view && - tbl->prep_check_option(thd, - ((check_opt_type == VIEW_CHECK_CASCADED) ? - VIEW_CHECK_CASCADED : - VIEW_CHECK_NONE))) - { + if (tbl->view && tbl->prep_check_option(thd, (is_cascaded ? + VIEW_CHECK_CASCADED : + VIEW_CHECK_NONE))) DBUG_RETURN(TRUE); - } } - if (check_opt_type) + if (check_opt_type && !check_option_processed) { - Item *item= 0; + Query_arena *arena= thd->stmt_arena, backup; + arena= thd->activate_stmt_arena_if_needed(&backup); // For easier test + if (where) { DBUG_ASSERT(where->fixed); - item= where->copy_andor_structure(thd); + check_option= where->copy_andor_structure(thd); } - if (check_opt_type == VIEW_CHECK_CASCADED) + if (is_cascaded) { for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local) { if (tbl->check_option) - item= and_conds(item, tbl->check_option); + check_option= and_conds(check_option, tbl->check_option); } } - if (item) - thd->change_item_tree(&check_option, item); + check_option= and_conds(check_option, + merge_on_conds(thd, this, is_cascaded)); + + if (arena) + thd->restore_active_arena(arena, &backup); + check_option_processed= TRUE; + } if (check_option) @@ -3078,7 +3123,7 @@ void st_table_list::cleanup_items() check CHECK OPTION condition SYNOPSIS - check_option() + st_table_list::view_check_option() ignore_failure ignore check option fail RETURN diff --git a/sql/table.h b/sql/table.h index c4c7da7d0c2..77807530ef1 100644 --- a/sql/table.h +++ b/sql/table.h @@ -911,6 +911,8 @@ typedef struct st_table_list bool compact_view_format; /* Use compact format for SHOW CREATE VIEW */ /* view where processed */ bool where_processed; + /* TRUE <=> VIEW CHECK OPTION expression has been processed */ + bool check_option_processed; /* FRMTYPE_ERROR if any type is acceptable */ enum frm_type_enum required_type; handlerton *db_type; /* table_type for handler */ |