diff options
author | unknown <monty@mishka.local> | 2004-12-22 13:54:39 +0200 |
---|---|---|
committer | unknown <monty@mishka.local> | 2004-12-22 13:54:39 +0200 |
commit | bb2d3eaa30a3881927142c985fb637aca06d9823 (patch) | |
tree | 8e5f08b17957f896c2d4e931e60b57d99c260ed6 /sql | |
parent | 52d080f09979deb2063a8010feee3b925a3d7938 (diff) | |
parent | 5c79810a6d3ca19254c310f1519664729367b647 (diff) | |
download | mariadb-git-bb2d3eaa30a3881927142c985fb637aca06d9823.tar.gz |
Merge with 4.1
BitKeeper/etc/ignore:
auto-union
BitKeeper/etc/logging_ok:
auto-union
Build-tools/Do-compile:
Auto merged
VC++Files/sql/mysqld.dsp:
Auto merged
client/Makefile.am:
Auto merged
client/mysql.cc:
Auto merged
BitKeeper/deleted/.del-acinclude.m4~f4ab416bac5003:
Auto merged
client/mysqltest.c:
Auto merged
include/my_base.h:
Auto merged
innobase/dict/dict0dict.c:
Auto merged
innobase/dict/dict0load.c:
Auto merged
innobase/include/dict0dict.h:
Auto merged
innobase/include/row0mysql.h:
Auto merged
innobase/os/os0file.c:
Auto merged
innobase/srv/srv0srv.c:
Auto merged
libmysql/libmysql.c:
Auto merged
myisam/mi_check.c:
Auto merged
myisam/mi_rnext_same.c:
Auto merged
myisam/mi_write.c:
Auto merged
myisam/sort.c:
Auto merged
mysql-test/mysql-test-run.sh:
Auto merged
mysql-test/r/ctype_ucs.result:
Auto merged
mysql-test/r/ctype_ujis.result:
Auto merged
mysql-test/r/gis-rtree.result:
Auto merged
mysql-test/r/group_by.result:
Auto merged
mysql-test/r/merge.result:
Auto merged
mysql-test/r/metadata.result:
Auto merged
mysql-test/r/ndb_alter_table.result:
Auto merged
mysql-test/r/ps_1general.result:
Auto merged
mysql-test/r/insert_update.result:
Auto merged
mysql-test/r/timezone2.result:
Auto merged
mysql-test/r/type_enum.result:
Auto merged
mysql-test/r/variables.result:
Auto merged
mysql-test/t/ctype_ucs.test:
Auto merged
mysql-test/t/merge.test:
Auto merged
mysql-test/t/ps_1general.test:
Auto merged
mysql-test/t/subselect.test:
Auto merged
mysql-test/t/system_mysql_db_fix.test:
Auto merged
mysql-test/t/variables.test:
Auto merged
ndb/include/ndbapi/NdbConnection.hpp:
Auto merged
ndb/include/ndbapi/NdbDictionary.hpp:
Auto merged
ndb/src/common/util/version.c:
Auto merged
ndb/src/kernel/blocks/dbacc/DbaccInit.cpp:
Auto merged
ndb/src/kernel/blocks/dbacc/Makefile.am:
Auto merged
ndb/src/kernel/blocks/dbdict/Dbdict.cpp:
Auto merged
ndb/src/kernel/blocks/dbdih/DbdihMain.cpp:
Auto merged
ndb/src/kernel/blocks/dblqh/DblqhInit.cpp:
Auto merged
ndb/src/kernel/blocks/dbtc/DbtcMain.cpp:
Auto merged
ndb/src/kernel/blocks/dbtux/Dbtux.hpp:
Auto merged
ndb/src/ndbapi/NdbBlob.cpp:
Auto merged
ndb/src/ndbapi/NdbConnection.cpp:
Auto merged
ndb/src/ndbapi/NdbDictionary.cpp:
Auto merged
ndb/src/ndbapi/NdbDictionaryImpl.cpp:
Auto merged
ndb/src/ndbapi/NdbDictionaryImpl.hpp:
Auto merged
ndb/src/ndbapi/NdbOperationExec.cpp:
Auto merged
ndb/src/ndbapi/NdbScanOperation.cpp:
Auto merged
ndb/test/ndbapi/Makefile.am:
Auto merged
scripts/make_win_src_distribution.sh:
Auto merged
scripts/mysql_install_db.sh:
Auto merged
sql/field.cc:
Auto merged
sql/ha_innodb.cc:
Auto merged
sql/ha_ndbcluster.cc:
Auto merged
sql/ha_ndbcluster.h:
Auto merged
sql/handler.cc:
Auto merged
sql/item_cmpfunc.cc:
Auto merged
sql/item_create.h:
Auto merged
sql/item_func.cc:
Auto merged
sql/item_geofunc.cc:
Auto merged
sql/item_row.cc:
Auto merged
sql/item_strfunc.cc:
Auto merged
sql/item_strfunc.h:
Auto merged
sql/item_sum.cc:
Auto merged
sql/item_sum.h:
Auto merged
sql/log.cc:
Auto merged
sql/log_event.cc:
Auto merged
sql/mysqld.cc:
Auto merged
sql/net_serv.cc:
Auto merged
sql/password.c:
Auto merged
sql/protocol.cc:
Auto merged
sql/repl_failsafe.cc:
Auto merged
sql/set_var.cc:
Auto merged
sql/slave.cc:
Auto merged
sql/sql_acl.cc:
Auto merged
sql/sql_cache.cc:
Auto merged
sql/sql_class.cc:
Auto merged
sql/sql_derived.cc:
Auto merged
sql/sql_do.cc:
Auto merged
sql/sql_handler.cc:
Auto merged
sql/sql_help.cc:
Auto merged
sql/sql_lex.cc:
Auto merged
sql/sql_lex.h:
Auto merged
sql/sql_repl.cc:
Auto merged
sql/sql_show.cc:
Auto merged
sql/sql_union.cc:
Auto merged
sql/examples/ha_archive.cc:
Auto merged
sql/strfunc.cc:
Auto merged
sql/table.cc:
Auto merged
sql/table.h:
Auto merged
sql/tztime.h:
Auto merged
sql/udf_example.cc:
Auto merged
sql/unireg.cc:
Auto merged
Makefile.am:
Simple merge
client/mysqldump.c:
Simple merge
configure.in:
Simple merge
libmysqld/lib_sql.cc:
Automatic merge
mysql-test/r/func_str.result:
Automatic merge
mysql-test/r/grant.result:
simple merge
mysql-test/r/multi_update.result:
automatc merge
mysql-test/r/ps.result:
automatic merge
mysql-test/r/ps_2myisam.result:
Automatic merge
mysql-test/r/ps_3innodb.result:
Automatic merge
mysql-test/r/ps_4heap.result:
Automatic merge
mysql-test/r/ps_5merge.result:
Automatic merge
mysql-test/r/ps_6bdb.result:
Automatic merge
mysql-test/r/ps_7ndb.result:
Automatic merge
mysql-test/r/show_check.result:
Automatic merge
mysql-test/r/subselect.result:
Automatic merge
mysql-test/t/grant.test:
Automatic merge
mysql-test/t/multi_update.test:
Automatic merge
mysql-test/t/ps.test:
Automatic merge
mysql-test/t/show_check.test:
Automatic merge
ndb/docs/wl2077.txt:
merge
ndb/src/mgmsrv/main.cpp:
merge
scripts/mysql_fix_privilege_tables.sh:
merge
sql/item.cc:
Merge (difficult)
sql/item.h:
simple merge
sql/item_cmpfunc.h:
Automatic merge
sql/item_subselect.cc:
Simple merge
sql/item_subselect.h:
Automatic merge
sql/mysql_priv.h:
Simple merge
sql/slave.h:
Automatic merge
sql/sql_base.cc:
Removed code that was backported to 4.1
sql/sql_class.h:
Merge (some code moved to sql_insert.cc)
sql/sql_db.cc:
simple merge
sql/sql_insert.cc:
Merge (difficult as logic had changed both in 4.1 and 5.0)
Some coded moved here from sql_class.h
sql/sql_parse.cc:
Merge (difficult)
sql/sql_prepare.cc:
Simple merge
sql/sql_select.cc:
Automatic merge
sql/sql_table.cc:
Simple merge
sql/sql_update.cc:
Difficult merge because of different logic for multi-updates
sql/sql_yacc.yy:
Simple merge
tests/client_test.c:
Simple merge
Diffstat (limited to 'sql')
58 files changed, 818 insertions, 343 deletions
diff --git a/sql/examples/ha_archive.cc b/sql/examples/ha_archive.cc index f754793e319..f79ca903e7a 100644 --- a/sql/examples/ha_archive.cc +++ b/sql/examples/ha_archive.cc @@ -520,6 +520,7 @@ error: int ha_archive::write_row(byte * buf) { z_off_t written; + Field_blob **field; DBUG_ENTER("ha_archive::write_row"); if (share->crashed) @@ -540,7 +541,7 @@ int ha_archive::write_row(byte * buf) We should probably mark the table as damagaged if the record is written but the blob fails. */ - for (Field_blob **field=table->blob_field ; *field ; field++) + for (field= table->blob_field ; *field ; field++) { char *ptr; uint32 size= (*field)->get_length(); diff --git a/sql/field.cc b/sql/field.cc index ebeee476985..0f1faccfe42 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -2197,7 +2197,7 @@ int Field_longlong::store(double nr) res= LONGLONG_MIN; error= (nr < (double) LONGLONG_MIN); } - else if (nr >= (double) LONGLONG_MAX) + else if (nr >= (double) (ulonglong) LONGLONG_MAX) { res= LONGLONG_MAX; error= (nr > (double) LONGLONG_MAX); @@ -5919,8 +5919,7 @@ int Field_enum::store(const char *from,uint length,CHARSET_INFO *cs) } /* Remove end space */ - while (length > 0 && my_isspace(system_charset_info,from[length-1])) - length--; + length= field_charset->cset->lengthsp(field_charset, from, length); uint tmp=find_type2(typelib, from, length, field_charset); if (!tmp) { @@ -6022,7 +6021,7 @@ String *Field_enum::val_str(String *val_buffer __attribute__((unused)), val_ptr->set("", 0, field_charset); else val_ptr->set((const char*) typelib->type_names[tmp-1], - (uint) strlen(typelib->type_names[tmp-1]), + typelib->type_lengths[tmp-1], field_charset); return val_ptr; } @@ -6059,13 +6058,14 @@ void Field_enum::sql_type(String &res) const res.append("enum("); bool flag=0; - for (const char **pos= typelib->type_names; *pos; pos++) + uint *len= typelib->type_lengths; + for (const char **pos= typelib->type_names; *pos; pos++, len++) { uint dummy_errors; if (flag) res.append(','); /* convert to res.charset() == utf8, then quote */ - enum_item.copy(*pos, strlen(*pos), charset(), res.charset(), &dummy_errors); + enum_item.copy(*pos, *len, charset(), res.charset(), &dummy_errors); append_unescaped(&res, enum_item.ptr(), enum_item.length()); flag= 1; } @@ -6144,14 +6144,15 @@ String *Field_set::val_str(String *val_buffer, uint bitnr=0; val_buffer->length(0); + val_buffer->set_charset(field_charset); while (tmp && bitnr < (uint) typelib->count) { if (tmp & 1) { if (val_buffer->length()) - val_buffer->append(field_separator); + val_buffer->append(&field_separator, 1, &my_charset_latin1); String str(typelib->type_names[bitnr], - (uint) strlen(typelib->type_names[bitnr]), + typelib->type_lengths[bitnr], field_charset); val_buffer->append(str); } @@ -6171,13 +6172,14 @@ void Field_set::sql_type(String &res) const res.append("set("); bool flag=0; - for (const char **pos= typelib->type_names; *pos; pos++) + uint *len= typelib->type_lengths; + for (const char **pos= typelib->type_names; *pos; pos++, len++) { uint dummy_errors; if (flag) res.append(','); /* convert to res.charset() == utf8, then quote */ - set_item.copy(*pos, strlen(*pos), charset(), res.charset(), &dummy_errors); + set_item.copy(*pos, *len, charset(), res.charset(), &dummy_errors); append_unescaped(&res, set_item.ptr(), set_item.length()); flag= 1; } diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index f838bfcd8c3..7320665dc49 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -32,6 +32,10 @@ #include <ndbapi/NdbApi.hpp> #include <ndbapi/NdbScanFilter.hpp> +// options from from mysqld.cc +extern my_bool opt_ndb_optimized_node_selection; +extern const char *opt_ndbcluster_connectstring; + // Default value for parallelism static const int parallelism= 240; @@ -39,9 +43,6 @@ static const int parallelism= 240; // createable against NDB from this handler static const int max_transactions= 256; -// connectstring to cluster if given by mysqld -const char *ndbcluster_connectstring= 0; - static const char *ha_ndb_ext=".ndb"; #define NDB_HIDDEN_PRIMARY_KEY_LENGTH 8 @@ -192,7 +193,7 @@ int execute_no_commit_ie(ha_ndbcluster *h, NdbConnection *trans) if (m_batch_execute) return 0; #endif - return trans->execute(NoCommit,IgnoreError,h->m_force_send); + return trans->execute(NoCommit, AO_IgnoreError,h->m_force_send); } /* @@ -3638,9 +3639,13 @@ int ha_ndbcluster::create_index(const char *name, int ha_ndbcluster::rename_table(const char *from, const char *to) { + NDBDICT *dict; char new_tabname[FN_HEADLEN]; + const NDBTAB *orig_tab; + int result; DBUG_ENTER("ha_ndbcluster::rename_table"); + DBUG_PRINT("info", ("Renaming %s to %s", from, to)); set_dbname(from); set_tabname(from); set_tabname(to, new_tabname); @@ -3648,14 +3653,20 @@ int ha_ndbcluster::rename_table(const char *from, const char *to) if (check_ndb_connection()) DBUG_RETURN(my_errno= HA_ERR_NO_CONNECTION); + dict= m_ndb->getDictionary(); + if (!(orig_tab= dict->getTable(m_tabname))) + ERR_RETURN(dict->getNdbError()); - int result= alter_table_name(m_tabname, new_tabname); - if (result == 0) + m_table= (void *)orig_tab; + // Change current database to that of target table + set_dbname(to); + m_ndb->setDatabaseName(m_dbname); + if (!(result= alter_table_name(new_tabname))) { - set_tabname(to); - handler::rename_table(from, to); + // Rename .ndb file + result= handler::rename_table(from, to); } - + DBUG_RETURN(result); } @@ -3664,19 +3675,16 @@ int ha_ndbcluster::rename_table(const char *from, const char *to) Rename a table in NDB Cluster using alter table */ -int ha_ndbcluster::alter_table_name(const char *from, const char *to) +int ha_ndbcluster::alter_table_name(const char *to) { - NDBDICT *dict= m_ndb->getDictionary(); - const NDBTAB *orig_tab; + NDBDICT * dict= m_ndb->getDictionary(); + const NDBTAB *orig_tab= (const NDBTAB *) m_table; + int ret; DBUG_ENTER("alter_table_name_table"); - DBUG_PRINT("enter", ("Renaming %s to %s", from, to)); - if (!(orig_tab= dict->getTable(from))) - ERR_RETURN(dict->getNdbError()); - - NdbDictionary::Table copy_tab= dict->getTableForAlteration(from); - copy_tab.setName(to); - if (dict->alterTable(copy_tab) != 0) + NdbDictionary::Table new_tab= *orig_tab; + new_tab.setName(to); + if (dict->alterTable(new_tab) != 0) ERR_RETURN(dict->getNdbError()); m_table= NULL; @@ -3699,7 +3707,7 @@ int ha_ndbcluster::delete_table(const char *name) if (check_ndb_connection()) DBUG_RETURN(HA_ERR_NO_CONNECTION); - + // Remove .ndb file handler::delete_table(name); DBUG_RETURN(drop_table()); } @@ -3958,6 +3966,7 @@ Ndb* check_ndb_in_thd(THD* thd) } + int ha_ndbcluster::check_ndb_connection() { THD* thd= current_thd; @@ -4240,15 +4249,19 @@ bool ndbcluster_init() int res; DBUG_ENTER("ndbcluster_init"); // Set connectstring if specified - if (ndbcluster_connectstring != 0) - DBUG_PRINT("connectstring", ("%s", ndbcluster_connectstring)); + if (opt_ndbcluster_connectstring != 0) + DBUG_PRINT("connectstring", ("%s", opt_ndbcluster_connectstring)); if ((g_ndb_cluster_connection= - new Ndb_cluster_connection(ndbcluster_connectstring)) == 0) + new Ndb_cluster_connection(opt_ndbcluster_connectstring)) == 0) { - DBUG_PRINT("error",("Ndb_cluster_connection(%s)",ndbcluster_connectstring)); + DBUG_PRINT("error",("Ndb_cluster_connection(%s)", + opt_ndbcluster_connectstring)); goto ndbcluster_init_error; } + g_ndb_cluster_connection->set_optimized_node_selection + (opt_ndb_optimized_node_selection); + // Create a Ndb object to open the connection to NDB g_ndb= new Ndb(g_ndb_cluster_connection, "sys"); g_ndb->getDictionary()->set_local_table_data_size(sizeof(Ndb_table_local_info)); @@ -4263,7 +4276,7 @@ bool ndbcluster_init() DBUG_PRINT("info",("NDBCLUSTER storage engine at %s on port %d", g_ndb_cluster_connection->get_connected_host(), g_ndb_cluster_connection->get_connected_port())); - g_ndb->waitUntilReady(10); + g_ndb_cluster_connection->wait_until_ready(10,0); } else if(res == 1) { diff --git a/sql/ha_ndbcluster.h b/sql/ha_ndbcluster.h index 2f18a52b8e9..ca3fa929674 100644 --- a/sql/ha_ndbcluster.h +++ b/sql/ha_ndbcluster.h @@ -148,7 +148,7 @@ class ha_ndbcluster: public handler uint8 table_cache_type(); private: - int alter_table_name(const char *from, const char *to); + int alter_table_name(const char *to); int drop_table(); int create_index(const char *name, KEY *key_info, bool unique); int create_ordered_index(const char *name, KEY *key_info); diff --git a/sql/handler.cc b/sql/handler.cc index e43f2c2e888..d6ce85f6a13 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -1232,6 +1232,9 @@ void handler::print_error(int error, myf errflag) textno=ER_DUP_KEY; break; } + case HA_ERR_NULL_IN_SPATIAL: + textno= ER_UNKNOWN_ERROR; + DBUG_VOID_RETURN; case HA_ERR_FOUND_DUPP_UNIQUE: textno=ER_DUP_UNIQUE; break; @@ -1345,7 +1348,8 @@ uint handler::get_dup_key(int error) { DBUG_ENTER("handler::get_dup_key"); table->file->errkey = (uint) -1; - if (error == HA_ERR_FOUND_DUPP_KEY || error == HA_ERR_FOUND_DUPP_UNIQUE) + if (error == HA_ERR_FOUND_DUPP_KEY || error == HA_ERR_FOUND_DUPP_UNIQUE || + error == HA_ERR_NULL_IN_SPATIAL) info(HA_STATUS_ERRKEY | HA_STATUS_NO_LOCK); DBUG_RETURN(table->file->errkey); } diff --git a/sql/item.cc b/sql/item.cc index 9117105f26e..ee77f2d43c0 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -73,7 +73,7 @@ Item::Item(): } /* - Constructor used by Item_field, Item_ref & agregate (sum) functions. + Constructor used by Item_field, Item_*_ref & agregate (sum) functions. Used for duplicating lists in processing queries with temporary tables */ @@ -171,7 +171,7 @@ Item_ident::Item_ident(const char *db_name_par,const char *table_name_par, } -/* Constructor used by Item_field & Item_ref (see Item comment) */ +/* Constructor used by Item_field & Item_*_ref (see Item comment) */ Item_ident::Item_ident(THD *thd, Item_ident *item) :Item(thd, item), @@ -1780,16 +1780,13 @@ resolve_ref_in_select_and_group(THD *thd, Item_ident *ref, SELECT_LEX *select) } return (select->ref_pointer_array + counter); } - else if (group_by_ref) + if (group_by_ref) return group_by_ref; - else - { - DBUG_ASSERT(FALSE); - return NULL; /* So there is no compiler warning. */ - } + DBUG_ASSERT(FALSE); + return NULL; /* So there is no compiler warning. */ } - else - return (Item**) not_found_item; + + return (Item**) not_found_item; } @@ -1847,6 +1844,7 @@ resolve_ref_in_select_and_group(THD *thd, Item_ident *ref, SELECT_LEX *select) bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference) { + enum_parsing_place place= NO_MATTER; DBUG_ASSERT(fixed == 0); if (!field) // If field is not checked { @@ -1885,13 +1883,14 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference) /* Search in the tables of the FROM clause of the outer select. */ table_list= outer_sel->get_table_list(); if (outer_sel->resolve_mode == SELECT_LEX::INSERT_MODE && table_list) + { /* It is a primary INSERT st_select_lex => do not resolve against the first table. */ table_list= table_list->next_local; - - enum_parsing_place place= prev_subselect_item->parsing_place; + } + place= prev_subselect_item->parsing_place; /* Check table fields only if the subquery is used somewhere out of HAVING, or the outer SELECT does not use grouping (i.e. tables are @@ -1926,7 +1925,6 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference) /* Search in the SELECT and GROUP lists of the outer select. */ if (outer_sel->resolve_mode == SELECT_LEX::SELECT_MODE) - { if (!(ref= resolve_ref_in_select_and_group(thd, this, outer_sel))) return TRUE; /* Some error occured (e.g. ambigous names). */ if (ref != not_found_item) @@ -1968,10 +1966,23 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference) } else if (ref != not_found_item) { + Item *save; + Item_ref *rf; + /* Should have been checked in resolve_ref_in_select_and_group(). */ DBUG_ASSERT(*ref && (*ref)->fixed); - - Item_ref *rf= new Item_ref(ref, (char *)table_name, (char *)field_name); + /* + Here, a subset of actions performed by Item_ref::set_properties + is not enough. So we pass ptr to NULL into Item_[direct]_ref + constructor, so no initialization is performed, and call + fix_fields() below. + */ + save= *ref; + *ref= NULL; // Don't call set_properties() + rf= (place == IN_HAVING ? + new Item_ref(ref, (char*) table_name, (char*) field_name) : + new Item_direct_ref(ref, (char*) table_name, (char*) field_name)); + *ref= save; if (!rf) return TRUE; thd->change_item_tree(reference, rf); @@ -2859,6 +2870,7 @@ bool Item_field::send(Protocol *protocol, String *buffer) bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference) { DBUG_ASSERT(fixed == 0); + enum_parsing_place place= NO_MATTER; SELECT_LEX *current_sel= thd->lex->current_select; if (!ref) @@ -2919,7 +2931,7 @@ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference) */ table_list= table_list->next_local; - enum_parsing_place place= prev_subselect_item->parsing_place; + place= prev_subselect_item->parsing_place; /* Check table fields only if the subquery is used somewhere out of HAVING or the outer SELECT does not use grouping (i.e. tables are @@ -3037,6 +3049,16 @@ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference) "forward reference in item list")); return TRUE; } + + set_properties(); + + if (ref && (*ref)->check_cols(1)) + return 1; + return 0; +} + +void Item_ref::set_properties() +{ max_length= (*ref)->max_length; maybe_null= (*ref)->maybe_null; decimals= (*ref)->decimals; @@ -3047,10 +3069,6 @@ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference) else alias_name_used= TRUE; // it is not field, so it is was resolved by alias fixed= 1; - - if (ref && (*ref)->check_cols(1)) - return TRUE; - return FALSE; } @@ -3156,7 +3174,7 @@ bool Item_default_value::fix_fields(THD *thd, fixed= 1; return FALSE; } - if (arg->fix_fields(thd, table_list, &arg)) + if (!arg->fixed && arg->fix_fields(thd, table_list, &arg)) return TRUE; if (arg->type() == REF_ITEM) @@ -3207,7 +3225,7 @@ bool Item_insert_value::fix_fields(THD *thd, Item **items) { DBUG_ASSERT(fixed == 0); - if (arg->fix_fields(thd, table_list, &arg)) + if (!arg->fixed && arg->fix_fields(thd, table_list, &arg)) return TRUE; if (arg->type() == REF_ITEM) diff --git a/sql/item.h b/sql/item.h index d5361bdcc8a..5a760db23f5 100644 --- a/sql/item.h +++ b/sql/item.h @@ -941,7 +941,7 @@ class Item_empty_string :public Item_string public: Item_empty_string(const char *header,uint length, CHARSET_INFO *cs= NULL) : Item_string("",0, cs ? cs : &my_charset_bin) - { name=(char*) header; max_length=length;} + { name=(char*) header; max_length= cs ? length * cs->mbmaxlen : length; } void make_field(Send_field *field); }; @@ -1010,14 +1010,36 @@ public: class Item_ref :public Item_ident { +protected: + void set_properties(); public: Field *result_field; /* Save result here */ Item **ref; Item_ref(const char *db_par, const char *table_name_par, const char *field_name_par) :Item_ident(db_par, table_name_par, field_name_par), result_field(0), ref(0) {} + /* + This constructor is used in two scenarios: + A) *item = NULL + No initialization is performed, fix_fields() call will be necessary. + + B) *item points to an Item this Item_ref will refer to. This is + used for GROUP BY. fix_fields() will not be called in this case, + so we call set_properties to make this item "fixed". set_properties + performs a subset of action Item_ref::fix_fields does, and this subset + is enough for Item_ref's used in GROUP BY. + + TODO we probably fix a superset of problems like in BUG#6658. Check this + with Bar, and if we have a more broader set of problems like this. + */ Item_ref(Item **item, const char *table_name_par, const char *field_name_par) - :Item_ident(NullS, table_name_par, field_name_par), result_field(0), ref(item) {} + :Item_ident(NullS, table_name_par, field_name_par), result_field(0), ref(item) + { + DBUG_ASSERT(item); + if (*item) + set_properties(); + } + /* Constructor need to process subselect with temporary tables (see Item) */ Item_ref(THD *thd, Item_ref *item) :Item_ident(thd, item), result_field(item->result_field), ref(item->ref) {} enum Type type() const { return REF_ITEM; } @@ -1025,29 +1047,34 @@ public: { return ref && (*ref)->eq(item, binary_cmp); } double val_real() { + DBUG_ASSERT(fixed); double tmp=(*ref)->val_result(); null_value=(*ref)->null_value; return tmp; } longlong val_int() { + DBUG_ASSERT(fixed); longlong tmp=(*ref)->val_int_result(); null_value=(*ref)->null_value; return tmp; } String *val_str(String* tmp) { + DBUG_ASSERT(fixed); tmp=(*ref)->str_result(tmp); null_value=(*ref)->null_value; return tmp; } bool is_null() { + DBUG_ASSERT(fixed); (void) (*ref)->val_int_result(); return (*ref)->null_value; } bool get_date(TIME *ltime,uint fuzzydate) { + DBUG_ASSERT(fixed); return (null_value=(*ref)->get_date_result(ltime,fuzzydate)); } double val_result(); @@ -1079,7 +1106,52 @@ public: void cleanup(); }; + +/* + The same as Item_ref, but get value from val_* family of method to get + value of item on which it referred instead of result* family. +*/ +class Item_direct_ref :public Item_ref +{ +public: + Item_direct_ref(Item **item, const char *table_name_par, + const char *field_name_par) + :Item_ref(item, table_name_par, field_name_par) {} + /* Constructor need to process subselect with temporary tables (see Item) */ + Item_direct_ref(THD *thd, Item_direct_ref *item) : Item_ref(thd, item) {} + + double val() + { + double tmp=(*ref)->val(); + null_value=(*ref)->null_value; + return tmp; + } + longlong val_int() + { + longlong tmp=(*ref)->val_int(); + null_value=(*ref)->null_value; + return tmp; + } + String *val_str(String* tmp) + { + tmp=(*ref)->val_str(tmp); + null_value=(*ref)->null_value; + return tmp; + } + bool is_null() + { + (void) (*ref)->val_int(); + return (*ref)->null_value; + } + bool get_date(TIME *ltime,uint fuzzydate) + { + return (null_value=(*ref)->get_date(ltime,fuzzydate)); + } +}; + + class Item_in_subselect; + class Item_ref_null_helper: public Item_ref { protected: @@ -1101,9 +1173,9 @@ class Item_null_helper :public Item_ref_null_helper public: Item_null_helper(Item_in_subselect* master, Item *item, const char *table_name_par, const char *field_name_par) - :Item_ref_null_helper(master, &store, table_name_par, field_name_par), + :Item_ref_null_helper(master, &item, table_name_par, field_name_par), store(item) - {} + { ref= &store; } void print(String *str); }; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 90eee9e76d1..a118de6fdb9 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -106,7 +106,7 @@ longlong Item_func_not::val_int() DBUG_ASSERT(fixed == 1); double value= args[0]->val_real(); null_value=args[0]->null_value; - return !null_value && value == 0 ? 1 : 0; + return ((!null_value && value == 0) ? 1 : 0); } /* @@ -117,13 +117,23 @@ longlong Item_func_not_all::val_int() { DBUG_ASSERT(fixed == 1); double value= args[0]->val_real(); - if (abort_on_null) - { - null_value= 0; - return (args[0]->null_value || value == 0) ? 1 : 0; - } + + /* + return TRUE if there was records in underlaying select in max/min + optimisation (ALL subquery) + */ + if (empty_underlying_subquery()) + return 1; + null_value= args[0]->null_value; - return (!null_value && value == 0) ? 1 : 0; + return ((!null_value && value == 0) ? 1 : 0); +} + + +bool Item_func_not_all::empty_underlying_subquery() +{ + return ((test_sum_item && !test_sum_item->any_value()) || + (test_sub_item && !test_sub_item->any_value())); } void Item_func_not_all::print(String *str) @@ -134,6 +144,30 @@ void Item_func_not_all::print(String *str) args[0]->print(str); } + +/* + Special NOP (No OPeration) for ALL subquery it is like Item_func_not_all + (return TRUE if underlaying sudquery do not return rows) but if subquery + returns some rows it return same value as argument (TRUE/FALSE). +*/ + +longlong Item_func_nop_all::val_int() +{ + DBUG_ASSERT(fixed == 1); + double value= args[0]->val(); + + /* + return FALSE if there was records in underlaying select in max/min + optimisation (SAME/ANY subquery) + */ + if (empty_underlying_subquery()) + return 0; + + null_value= args[0]->null_value; + return (null_value || value == 0) ? 0 : 1; +} + + /* Convert a constant expression or string to an integer. This is done when comparing DATE's of different formats and @@ -2053,6 +2087,7 @@ void Item_cond::split_sum_func(THD *thd, Item **ref_pointer_array, { Item **ref= li.ref(); uint el= fields.elements; + ref_pointer_array[el]= item; Item *new_item= new Item_ref(ref_pointer_array + el, 0, item->name); fields.push_front(item); ref_pointer_array[el]= item; @@ -2404,8 +2439,10 @@ bool Item_func_regex::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) { DBUG_ASSERT(fixed == 0); - if (args[0]->fix_fields(thd, tables, args) || args[0]->check_cols(1) || - args[1]->fix_fields(thd,tables, args + 1) || args[1]->check_cols(1)) + if ((!args[0]->fixed && + args[0]->fix_fields(thd, tables, args)) || args[0]->check_cols(1) || + (!args[1]->fixed && + args[1]->fix_fields(thd,tables, args + 1)) || args[1]->check_cols(1)) return TRUE; /* purecov: inspected */ with_sum_func=args[0]->with_sum_func || args[1]->with_sum_func; max_length= 1; diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 6a7e037bed1..a6fe9e44a3d 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -231,6 +231,7 @@ public: Item *neg_transformer(THD *thd); }; +class Item_maxmin_subselect; /* The class Item_func_trig_cond is used for guarded predicates @@ -267,19 +268,40 @@ public: class Item_func_not_all :public Item_func_not { + /* allow to check presence od values in max/min optimisation */ + Item_sum_hybrid *test_sum_item; + Item_maxmin_subselect *test_sub_item; + bool abort_on_null; public: bool show; - Item_func_not_all(Item *a) :Item_func_not(a), abort_on_null(0), show(0) {} + Item_func_not_all(Item *a) + :Item_func_not(a), test_sum_item(0), test_sub_item(0), abort_on_null(0), + show(0) + {} virtual void top_level_item() { abort_on_null= 1; } bool top_level() { return abort_on_null; } longlong val_int(); enum Functype functype() const { return NOT_ALL_FUNC; } const char *func_name() const { return "<not>"; } void print(String *str); + void set_sum_test(Item_sum_hybrid *item) { test_sum_item= item; }; + void set_sub_test(Item_maxmin_subselect *item) { test_sub_item= item; }; + bool empty_underlying_subquery(); }; + +class Item_func_nop_all :public Item_func_not_all +{ +public: + + Item_func_nop_all(Item *a) :Item_func_not_all(a) {} + longlong val_int(); + const char *func_name() const { return "<nop>"; } +}; + + class Item_func_eq :public Item_bool_rowready_func2 { public: diff --git a/sql/item_create.h b/sql/item_create.h index d48aed5284a..1be33fef257 100644 --- a/sql/item_create.h +++ b/sql/item_create.h @@ -86,7 +86,7 @@ Item *create_func_soundex(Item* a); Item *create_func_space(Item *); Item *create_func_sqrt(Item* a); Item *create_func_strcmp(Item* a, Item *b); -Item *create_func_tan(Item* a);; +Item *create_func_tan(Item* a); Item *create_func_time_format(Item *a, Item *b); Item *create_func_time_to_sec(Item* a); Item *create_func_to_days(Item* a); diff --git a/sql/item_func.cc b/sql/item_func.cc index aba53b9b397..e2a9071324b 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -394,8 +394,8 @@ void Item_func::split_sum_func(THD *thd, Item **ref_pointer_array, else if (item->used_tables() || item->type() == SUM_FUNC_ITEM) { uint el= fields.elements; + ref_pointer_array[el]= item; Item *new_item= new Item_ref(ref_pointer_array + el, 0, item->name); - new_item->collation.set(item->collation); fields.push_front(item); ref_pointer_array[el]= item; thd->change_item_tree(arg, new_item); @@ -855,9 +855,25 @@ longlong Item_func_neg::val_int() void Item_func_neg::fix_length_and_dec() { + enum Item_result arg_result= args[0]->result_type(); + enum Item::Type arg_type= args[0]->type(); decimals=args[0]->decimals; max_length=args[0]->max_length; hybrid_type= REAL_RESULT; + + /* + We need to account for added '-' in the following cases: + A) argument is a real or integer positive constant - in this case + argument's max_length is set to actual number of bytes occupied, and not + maximum number of bytes real or integer may require. Note that all + constants are non negative so we don't need to account for removed '-'. + B) argument returns a string. + */ + if (arg_result == STRING_RESULT || + (arg_type == REAL_ITEM && ((Item_real*)args[0])->value >= 0) || + (arg_type == INT_ITEM && ((Item_int*)args[0])->value > 0)) + max_length++; + if (args[0]->result_type() == INT_RESULT) { /* @@ -1742,7 +1758,8 @@ udf_handler::fix_fields(THD *thd, TABLE_LIST *tables, Item_result_field *func, arg != arg_end ; arg++,i++) { - if ((*arg)->fix_fields(thd, tables, arg)) + if (!(*arg)->fixed && + (*arg)->fix_fields(thd, tables, arg)) DBUG_RETURN(1); // we can't assign 'item' before, because fix_fields() can change arg Item *item= *arg; diff --git a/sql/item_geofunc.cc b/sql/item_geofunc.cc index 163260f6428..1a8cb50081b 100644 --- a/sql/item_geofunc.cc +++ b/sql/item_geofunc.cc @@ -24,7 +24,6 @@ #include "mysql_priv.h" #ifdef HAVE_SPATIAL -#include "sql_acl.h" #include <m_ctype.h> void Item_geometry_func::fix_length_and_dec() diff --git a/sql/item_row.cc b/sql/item_row.cc index 0c7eae9d920..62f31186b09 100644 --- a/sql/item_row.cc +++ b/sql/item_row.cc @@ -95,6 +95,7 @@ void Item_row::split_sum_func(THD *thd, Item **ref_pointer_array, else if ((*arg)->used_tables() || (*arg)->type() == SUM_FUNC_ITEM) { uint el= fields.elements; + ref_pointer_array[el]=*arg; Item *new_item= new Item_ref(ref_pointer_array + el, 0, (*arg)->name); fields.push_front(*arg); ref_pointer_array[el]= *arg; diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index bf172e1744d..c271ff43fa3 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -25,7 +25,6 @@ #endif #include "mysql_priv.h" -#include "sql_acl.h" #include <m_ctype.h> #ifdef HAVE_OPENSSL #include <openssl/des.h> @@ -955,8 +954,9 @@ String *Item_func_left::val_str(String *str) if (res->length() <= (uint) length || res->length() <= (char_pos= res->charpos(length))) return res; - str_value.set(*res, 0, char_pos); - return &str_value; + + tmp_value.set(*res, 0, char_pos); + return &tmp_value; } @@ -1748,6 +1748,7 @@ void Item_func_make_set::split_sum_func(THD *thd, Item **ref_pointer_array, else if (item->used_tables() || item->type() == SUM_FUNC_ITEM) { uint el= fields.elements; + ref_pointer_array[el]=item; Item *new_item= new Item_ref(ref_pointer_array + el, 0, item->name); fields.push_front(item); ref_pointer_array[el]= item; @@ -2332,17 +2333,6 @@ String *Item_func_hex::val_str(String *str) return &tmp_value; } -inline int hexchar_to_int(char c) -{ - if (c <= '9' && c >= '0') - return c-'0'; - c|=32; - if (c <= 'f' && c >= 'a') - return c-'a'+10; - return -1; -} - - /* Convert given hex string to a binary string */ String *Item_func_unhex::val_str(String *str) diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index 647cf022d79..e322e5616a1 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -162,6 +162,7 @@ public: class Item_func_left :public Item_str_func { + String tmp_value; public: Item_func_left(Item *a,Item *b) :Item_str_func(a,b) {} String *val_str(String *); @@ -396,7 +397,8 @@ public: bool fix_fields(THD *thd, TABLE_LIST *tlist, Item **ref) { DBUG_ASSERT(fixed == 0); - return (item->fix_fields(thd, tlist, &item) || + return (!item->fixed && + item->fix_fields(thd, tlist, &item) || item->check_cols(1) || Item_func::fix_fields(thd, tlist, ref)); } diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 304c3ed4bbd..064744158d1 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -155,6 +155,8 @@ bool Item_subselect::fix_fields(THD *thd_param, TABLE_LIST *tables, Item **ref) // did we changed top item of WHERE condition if (unit->outer_select()->where == (*ref)) unit->outer_select()->where= substitution; // correct WHERE for PS + else if (unit->outer_select()->having == (*ref)) + unit->outer_select()->having= substitution; // correct HAVING for PS (*ref)= substitution; substitution->name= name; @@ -272,7 +274,7 @@ Item_maxmin_subselect::Item_maxmin_subselect(THD *thd_param, Item_subselect *parent, st_select_lex *select_lex, bool max_arg) - :Item_singlerow_subselect() + :Item_singlerow_subselect(), was_values(TRUE) { DBUG_ENTER("Item_maxmin_subselect::Item_maxmin_subselect"); max= max_arg; @@ -297,12 +299,31 @@ Item_maxmin_subselect::Item_maxmin_subselect(THD *thd_param, DBUG_VOID_RETURN; } +void Item_maxmin_subselect::cleanup() +{ + DBUG_ENTER("Item_maxmin_subselect::cleanup"); + Item_singlerow_subselect::cleanup(); + + /* + By default it is TRUE to avoid TRUE reporting by + Item_func_not_all/Item_func_nop_all if this item was never called. + + Engine exec() set it to FALSE by reset_value_registration() call. + select_max_min_finder_subselect::send_data() set it back to TRUE if some + value will be found. + */ + was_values= TRUE; + DBUG_VOID_RETURN; +} + + void Item_maxmin_subselect::print(String *str) { str->append(max?"<max>":"<min>", 5); Item_singlerow_subselect::print(str); } + void Item_singlerow_subselect::reset() { null_value= 1; @@ -310,6 +331,7 @@ void Item_singlerow_subselect::reset() value->null_value= 1; } + Item_subselect::trans_res Item_singlerow_subselect::select_transformer(JOIN *join) { @@ -525,7 +547,7 @@ bool Item_in_subselect::test_limit(SELECT_LEX_UNIT *unit) Item_in_subselect::Item_in_subselect(Item * left_exp, st_select_lex *select_lex): - Item_exists_subselect(), transformed(0), upper_not(0) + Item_exists_subselect(), transformed(0), upper_item(0) { DBUG_ENTER("Item_in_subselect::Item_in_subselect"); left_expr= left_exp; @@ -687,7 +709,7 @@ Item_in_subselect::single_value_transformer(JOIN *join, NULL/IS NOT NULL functions). If so, we rewrite ALL/ANY with NOT EXISTS later in this method. */ - if ((abort_on_null || (upper_not && upper_not->top_level())) && + if ((abort_on_null || (upper_item && upper_item->top_level())) && !select_lex->master_unit()->uncacheable && !func->eqne_op()) { if (substitution) @@ -701,7 +723,7 @@ Item_in_subselect::single_value_transformer(JOIN *join, !select_lex->with_sum_func && !(select_lex->next_select())) { - Item *item; + Item_sum_hybrid *item; if (func->l_op()) { /* @@ -718,6 +740,8 @@ Item_in_subselect::single_value_transformer(JOIN *join, */ item= new Item_sum_min(*select_lex->ref_pointer_array); } + if (upper_item) + upper_item->set_sum_test(item); *select_lex->ref_pointer_array= item; { List_iterator<Item> it(select_lex->item_list); @@ -738,15 +762,19 @@ Item_in_subselect::single_value_transformer(JOIN *join, } else { + Item_maxmin_subselect *item; // remove LIMIT placed by ALL/ANY subquery select_lex->master_unit()->global_parameters->select_limit= HA_POS_ERROR; - subs= new Item_maxmin_subselect(thd, this, select_lex, func->l_op()); + subs= item= new Item_maxmin_subselect(thd, this, select_lex, func->l_op()); + if (upper_item) + upper_item->set_sub_test(item); } // left expression belong to outer select SELECT_LEX *current= thd->lex->current_select, *up; thd->lex->current_select= up= current->return_after_parsing(); - if (left_expr->fix_fields(thd, up->get_table_list(), &left_expr)) + if (!left_expr->fixed && + left_expr->fix_fields(thd, up->get_table_list(), &left_expr)) { thd->lex->current_select= current; goto err; @@ -777,9 +805,9 @@ Item_in_subselect::single_value_transformer(JOIN *join, As far as Item_ref_in_optimizer do not substitude itself on fix_fields we can use same item for all selects. */ - expr= new Item_ref((Item**)optimizer->get_cache(), - (char *)"<no matter>", - (char *)in_left_expr_name); + expr= new Item_direct_ref((Item**)optimizer->get_cache(), + (char *)"<no matter>", + (char *)in_left_expr_name); unit->uncacheable|= UNCACHEABLE_DEPENDENT; } @@ -966,9 +994,10 @@ Item_in_subselect::row_value_transformer(JOIN *join) (char *) "<no matter>", (char *) "<list ref>"); func= - eq_creator.create(new Item_ref((*optimizer->get_cache())->addr(i), - (char *)"<no matter>", - (char *)in_left_expr_name), + eq_creator.create(new Item_direct_ref((*optimizer->get_cache())-> + addr(i), + (char *)"<no matter>", + (char *)in_left_expr_name), func); item= and_items(item, func); } @@ -1041,8 +1070,8 @@ Item_subselect::trans_res Item_allany_subselect::select_transformer(JOIN *join) { transformed= 1; - if (upper_not) - upper_not->show= 1; + if (upper_item) + upper_item->show= 1; return single_value_transformer(join, func); } @@ -1241,6 +1270,7 @@ int subselect_single_select_engine::exec() } if (!executed) { + item->reset_value_registration(); join->exec(); executed= 1; thd->where= save_where; diff --git a/sql/item_subselect.h b/sql/item_subselect.h index d0ff3654e48..53fe21a9bb6 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -93,7 +93,7 @@ public: return null_value; } bool fix_fields(THD *thd, TABLE_LIST *tables, Item **ref); - bool exec(); + virtual bool exec(); virtual void fix_length_and_dec(); table_map used_tables() const; bool const_item() const; @@ -109,6 +109,11 @@ public: engine_changed= 1; return eng == 0; } + /* + Used by max/min subquery to initialize value presence registration + mechanism. Engine call this method before rexecution query. + */ + virtual void reset_value_registration() {} enum_parsing_place place() { return parsing_place; } friend class select_subselect; @@ -151,13 +156,20 @@ public: }; /* used in static ALL/ANY optimisation */ +class select_max_min_finder_subselect; class Item_maxmin_subselect :public Item_singlerow_subselect { +protected: bool max; + bool was_values; // Set if we have found at least one row public: Item_maxmin_subselect(THD *thd, Item_subselect *parent, st_select_lex *select_lex, bool max); void print(String *str); + void cleanup(); + bool any_value() { return was_values; } + void register_value() { was_values= TRUE; } + void reset_value_registration() { was_values= FALSE; } }; /* exists subselect */ @@ -205,11 +217,11 @@ protected: bool abort_on_null; bool transformed; public: - Item_func_not_all *upper_not; // point on NOT before ALL subquery + Item_func_not_all *upper_item; // point on NOT/NOP before ALL/SOME subquery Item_in_subselect(Item * left_expr, st_select_lex *select_lex); Item_in_subselect() - :Item_exists_subselect(), abort_on_null(0), transformed(0), upper_not(0) + :Item_exists_subselect(), abort_on_null(0), transformed(0), upper_item(0) {} subs_type substype() { return IN_SUBS; } @@ -249,7 +261,7 @@ public: st_select_lex *select_lex, bool all); // only ALL subquery has upper not - subs_type substype() { return upper_not?ALL_SUBS:ANY_SUBS; } + subs_type substype() { return all?ALL_SUBS:ANY_SUBS; } trans_res select_transformer(JOIN *join); void print(String *str); }; diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 949545bcdb0..92c91b2e866 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -656,9 +656,24 @@ void Item_sum_hybrid::cleanup() DBUG_ENTER("Item_sum_hybrid::cleanup"); Item_sum::cleanup(); used_table_cache= ~(table_map) 0; + + /* + by default it is TRUE to avoid TRUE reporting by + Item_func_not_all/Item_func_nop_all if this item was never called. + + no_rows_in_result() set it to FALSE if was not results found. + If some results found it will be left unchanged. + */ + was_values= TRUE; DBUG_VOID_RETURN; } +void Item_sum_hybrid::no_rows_in_result() +{ + Item_sum::no_rows_in_result(); + was_values= FALSE; +} + Item *Item_sum_min::copy_or_same(THD* thd) { @@ -2017,7 +2032,9 @@ Item_func_group_concat::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) for (i=0 ; i < arg_count ; i++) { - if (args[i]->fix_fields(thd, tables, args + i) || args[i]->check_cols(1)) + if ((!args[i]->fixed && + args[i]->fix_fields(thd, tables, args + i)) || + args[i]->check_cols(1)) return TRUE; if (i < arg_count_field) maybe_null|= args[i]->maybe_null; diff --git a/sql/item_sum.h b/sql/item_sum.h index c1352f7ae7d..157791722f6 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -442,18 +442,20 @@ class Item_sum_hybrid :public Item_sum enum_field_types hybrid_field_type; int cmp_sign; table_map used_table_cache; + bool was_values; // Set if we have found at least one row (for max/min only) public: Item_sum_hybrid(Item *item_par,int sign) :Item_sum(item_par), sum(0.0), sum_int(0), hybrid_type(INT_RESULT), hybrid_field_type(FIELD_TYPE_LONGLONG), - cmp_sign(sign), used_table_cache(~(table_map) 0) + cmp_sign(sign), used_table_cache(~(table_map) 0), was_values(TRUE) { collation.set(&my_charset_bin); } Item_sum_hybrid(THD *thd, Item_sum_hybrid *item): Item_sum(thd, item), value(item->value), sum(item->sum), sum_int(item->sum_int), hybrid_type(item->hybrid_type), hybrid_field_type(item->hybrid_field_type),cmp_sign(item->cmp_sign), - used_table_cache(item->used_table_cache) + used_table_cache(item->used_table_cache), + was_values(TRUE) { collation.set(item->collation); } bool fix_fields(THD *, TABLE_LIST *, Item **); table_map used_tables() const { return used_table_cache; } @@ -473,6 +475,8 @@ class Item_sum_hybrid :public Item_sum void min_max_update_real_field(); void min_max_update_int_field(); void cleanup(); + bool any_value() { return was_values; } + void no_rows_in_result(); }; diff --git a/sql/log.cc b/sql/log.cc index c659a3ede2e..5d56fefa26a 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -23,7 +23,6 @@ #endif #include "mysql_priv.h" -#include "sql_acl.h" #include "sql_repl.h" #include "ha_innodb.h" // necessary to cut the binlog when crash recovery diff --git a/sql/log_event.cc b/sql/log_event.cc index 760436592b9..581d3ef0d21 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -438,6 +438,9 @@ int Log_event::exec_event(struct st_relay_log_info* rli) Note that Rotate_log_event::exec_event() does not call this function, so there is no chance that a fake rotate event resets last_master_timestamp. + Note that we update without mutex (probably ok - except in some very + rare cases, only consequence is that value may take some time to + display in Seconds_Behind_Master - not critical). */ rli->last_master_timestamp= when; } @@ -2126,7 +2129,9 @@ Load_log_event::Load_log_event(THD *thd_arg, sql_exchange *ex, List<Item> &fields_arg, enum enum_duplicates handle_dup, bool using_trans) - :Log_event(thd_arg, 0, using_trans), thread_id(thd_arg->thread_id), + :Log_event(thd_arg, !thd_arg->tmp_table_used ? + 0 : LOG_EVENT_THREAD_SPECIFIC_F, using_trans), + thread_id(thd_arg->thread_id), slave_proxy_id(thd_arg->variables.pseudo_thread_id), num_fields(0),fields(0), field_lens(0),field_block_len(0), @@ -2329,6 +2334,9 @@ void Load_log_event::print(FILE* file, bool short_form, LAST_EVENT_INFO* last_ev commented ? "# " : "", db); + if (flags & LOG_EVENT_THREAD_SPECIFIC_F) + fprintf(file,"%sSET @@session.pseudo_thread_id=%lu;\n", + commented ? "# " : "", (ulong)thread_id); fprintf(file, "%sLOAD DATA ", commented ? "# " : ""); if (check_fname_outside_temp_buf()) diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 2fc82e05f31..c346de98615 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -410,7 +410,6 @@ inline THD *_current_thd(void) #include "sql_udf.h" class user_var_entry; #include "item.h" -#include "tztime.h" typedef Comp_creator* (*chooser_compare_func_creator)(bool invert); /* sql_parse.cc */ void free_items(Item *item); @@ -428,7 +427,6 @@ bool multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count); bool mysql_multi_update_prepare(THD *thd); bool mysql_multi_delete_prepare(THD *thd); bool mysql_insert_select_prepare(THD *thd); -bool insert_select_precheck(THD *thd, TABLE_LIST *tables); bool update_precheck(THD *thd, TABLE_LIST *tables); bool delete_precheck(THD *thd, TABLE_LIST *tables); bool insert_precheck(THD *thd, TABLE_LIST *tables); @@ -436,6 +434,8 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *create_table); Item *negate_expression(THD *thd, Item *expr); #include "sql_class.h" +#include "sql_acl.h" +#include "tztime.h" #include "opt_range.h" #ifdef HAVE_QUERY_CACHE @@ -1322,6 +1322,23 @@ inline void setup_table_map(TABLE *table, TABLE_LIST *table_list, uint tablenr) /* + SYNOPSYS + hexchar_to_int() + convert a hex digit into number +*/ + +inline int hexchar_to_int(char c) +{ + if (c <= '9' && c >= '0') + return c-'0'; + c|=32; + if (c <= 'f' && c >= 'a') + return c-'a'+10; + return -1; +} + + +/* Some functions that are different in the embedded library and the normal server */ diff --git a/sql/mysqld.cc b/sql/mysqld.cc index c5698469341..203be4b96a4 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -17,7 +17,6 @@ #include "mysql_priv.h" #include <m_ctype.h> #include <my_dir.h> -#include "sql_acl.h" #include "slave.h" #include "sql_repl.h" #include "repl_failsafe.h" @@ -55,6 +54,11 @@ #endif #ifdef HAVE_NDBCLUSTER_DB #define OPT_NDBCLUSTER_DEFAULT 0 +#ifdef NDB_SHM_TRANSPORTER +#define OPT_NDB_SHM_DEFAULT 1 +#else +#define OPT_NDB_SHM_DEFAULT 0 +#endif #else #define OPT_NDBCLUSTER_DEFAULT 0 #endif @@ -289,6 +293,10 @@ my_bool opt_safe_user_create = 0, opt_no_mix_types = 0; my_bool opt_show_slave_auth_info, opt_sql_bin_update = 0; my_bool opt_log_slave_updates= 0; my_bool opt_console= 0, opt_bdb, opt_innodb, opt_isam, opt_ndbcluster; +#ifdef HAVE_NDBCLUSTER_DB +const char *opt_ndbcluster_connectstring= 0; +my_bool opt_ndb_shm, opt_ndb_optimized_node_selection; +#endif my_bool opt_readonly, use_temp_pool, relay_log_purge; my_bool opt_sync_bdb_logs, opt_sync_frm; my_bool opt_secure_auth= 0; @@ -4108,6 +4116,7 @@ enum options_mysqld OPT_INNODB, OPT_ISAM, OPT_NDBCLUSTER, OPT_NDB_CONNECTSTRING, OPT_NDB_USE_EXACT_COUNT, OPT_NDB_FORCE_SEND, OPT_NDB_AUTOINCREMENT_PREFETCH_SZ, + OPT_NDB_SHM, OPT_NDB_OPTIMIZED_NODE_SELECTION, OPT_SKIP_SAFEMALLOC, OPT_TEMP_POOL, OPT_TX_ISOLATION, OPT_SKIP_STACK_TRACE, OPT_SKIP_SYMLINKS, @@ -4577,24 +4586,46 @@ Disable with --skip-ndbcluster (will save memory).", #ifdef HAVE_NDBCLUSTER_DB {"ndb-connectstring", OPT_NDB_CONNECTSTRING, "Connect string for ndbcluster.", - (gptr*) &ndbcluster_connectstring, (gptr*) &ndbcluster_connectstring, + (gptr*) &opt_ndbcluster_connectstring, + (gptr*) &opt_ndbcluster_connectstring, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"ndb_autoincrement_prefetch_sz", OPT_NDB_AUTOINCREMENT_PREFETCH_SZ, - "Specify number of autoincrement values that are prefetched", + {"ndb-autoincrement-prefetch-sz", OPT_NDB_AUTOINCREMENT_PREFETCH_SZ, + "Specify number of autoincrement values that are prefetched.", (gptr*) &global_system_variables.ndb_autoincrement_prefetch_sz, (gptr*) &global_system_variables.ndb_autoincrement_prefetch_sz, 0, GET_INT, REQUIRED_ARG, 32, 1, 256, 0, 0, 0}, + {"ndb-force-send", OPT_NDB_FORCE_SEND, + "Force send of buffers to ndb immediately without waiting for " + "other threads.", + (gptr*) &global_system_variables.ndb_force_send, + (gptr*) &global_system_variables.ndb_force_send, + 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0}, {"ndb_force_send", OPT_NDB_FORCE_SEND, - "Force send of buffers to ndb immediately without waiting for other threads", + "same as --ndb-force-send.", (gptr*) &global_system_variables.ndb_force_send, (gptr*) &global_system_variables.ndb_force_send, 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0}, + {"ndb-use-exact-count", OPT_NDB_USE_EXACT_COUNT, + "Use exact records count during query planning and for fast " + "select count(*), disable for faster queries.", + (gptr*) &global_system_variables.ndb_use_exact_count, + (gptr*) &global_system_variables.ndb_use_exact_count, + 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0}, {"ndb_use_exact_count", OPT_NDB_USE_EXACT_COUNT, - "Use exact records count during query planning and for " - "fast select count(*)", + "same as --ndb-use-exact-count.", (gptr*) &global_system_variables.ndb_use_exact_count, (gptr*) &global_system_variables.ndb_use_exact_count, 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0}, + {"ndb-shm", OPT_NDB_SHM, + "Use shared memory connections when available.", + (gptr*) &opt_ndb_shm, + (gptr*) &opt_ndb_shm, + 0, GET_BOOL, OPT_ARG, OPT_NDB_SHM_DEFAULT, 0, 0, 0, 0, 0}, + {"ndb-optimized-node-selection", OPT_NDB_OPTIMIZED_NODE_SELECTION, + "Select nodes for transactions in a more optimal way.", + (gptr*) &opt_ndb_optimized_node_selection, + (gptr*) &opt_ndb_optimized_node_selection, + 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0}, #endif {"new", 'n', "Use very new possible 'unsafe' functions.", (gptr*) &global_system_variables.new_mode, @@ -4895,7 +4926,8 @@ log and this option does nothing anymore.", (gptr*) &delayed_queue_size, (gptr*) &delayed_queue_size, 0, GET_ULONG, REQUIRED_ARG, DELAYED_QUEUE_SIZE, 1, ~0L, 0, 1, 0}, {"expire_logs_days", OPT_EXPIRE_LOGS_DAYS, - "Binary logs will be rotated after expire-log-days days ", + "If non-zero, binary logs will be purged after expire_logs_days " + "days; possible purges happen at startup and at binary log rotation.", (gptr*) &expire_logs_days, (gptr*) &expire_logs_days, 0, GET_ULONG, REQUIRED_ARG, 0, 0, 99, 0, 1, 0}, @@ -5141,7 +5173,7 @@ The minimum value for this variable is 4096.", "Default pointer size to be used for MyISAM tables.", (gptr*) &myisam_data_pointer_size, (gptr*) &myisam_data_pointer_size, 0, GET_ULONG, REQUIRED_ARG, - 4, 2, 7, 0, 1, 0}, + 4, 2, 8, 0, 1, 0}, {"myisam_max_extra_sort_file_size", OPT_MYISAM_MAX_EXTRA_SORT_FILE_SIZE, "Used to help MySQL to decide when to use the slow but safe key cache index create method.", (gptr*) &global_system_variables.myisam_max_extra_sort_file_size, diff --git a/sql/net_serv.cc b/sql/net_serv.cc index 899733d8639..3bec00a5177 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -169,8 +169,8 @@ my_bool net_realloc(NET *net, ulong length) if (length >= net->max_packet_size) { - DBUG_PRINT("error",("Packet too large. Max sixe: %lu", - net->max_packet_size)); + DBUG_PRINT("error", ("Packet too large. Max size: %lu", + net->max_packet_size)); net->error= 1; net->report_error= 1; net->last_errno= ER_NET_PACKET_TOO_LARGE; diff --git a/sql/password.c b/sql/password.c index e2f7e14d39f..79675ade30b 100644 --- a/sql/password.c +++ b/sql/password.c @@ -211,12 +211,13 @@ check_scramble_323(const char *scrambled, const char *message, ulong hash_message[2]; char buff[16],*to,extra; /* Big enough for check */ const char *pos; - + hash_password(hash_message, message, SCRAMBLE_LENGTH_323); randominit(&rand_st,hash_pass[0] ^ hash_message[0], hash_pass[1] ^ hash_message[1]); to=buff; - for (pos=scrambled ; *pos ; pos++) + DBUG_ASSERT(sizeof(buff) > SCRAMBLE_LENGTH_323); + for (pos=scrambled ; *pos && to < buff+sizeof(buff) ; pos++) *to++=(char) (floor(my_rnd(&rand_st)*31)+64); if (pos-scrambled != SCRAMBLE_LENGTH_323) return 1; diff --git a/sql/protocol.cc b/sql/protocol.cc index 4c916d78378..d537f9cf829 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -537,10 +537,18 @@ bool Protocol::send_fields(List<Item> *list, uint flags) pos= (char*) local_packet->ptr()+local_packet->length(); *pos++= 12; // Length of packed fields if (item->collation.collation == &my_charset_bin || thd_charset == NULL) + { + /* No conversion */ int2store(pos, field.charsetnr); + int4store(pos+2, field.length); + } else - int2store(pos, thd_charset->number); - int4store(pos+2, field.length); + { + /* With conversion */ + int2store(pos, thd_charset->number); + uint char_len= field.length / item->collation.collation->mbmaxlen; + int4store(pos+2, char_len * thd_charset->mbmaxlen); + } pos[6]= field.type; int2store(pos+7,field.flags); pos[9]= (char) field.decimals; diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc index b7575f3a44e..7852993b95b 100644 --- a/sql/repl_failsafe.cc +++ b/sql/repl_failsafe.cc @@ -20,7 +20,6 @@ #include "repl_failsafe.h" #include "sql_repl.h" #include "slave.h" -#include "sql_acl.h" #include "log_event.h" #include <mysql.h> diff --git a/sql/set_var.cc b/sql/set_var.cc index da6341597f1..a5370fde671 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -55,7 +55,6 @@ #include "mysql_priv.h" #include <mysql.h> #include "slave.h" -#include "sql_acl.h" #include <my_getopt.h> #include <thr_alarm.h> #include <myisam.h> @@ -374,22 +373,18 @@ sys_var_thd_bool sys_innodb_table_locks("innodb_table_locks", sys_var_long_ptr sys_innodb_autoextend_increment("innodb_autoextend_increment", &srv_auto_extend_increment); #endif + #ifdef HAVE_NDBCLUSTER_DB -// ndb thread specific variable settings +/* ndb thread specific variable settings */ sys_var_thd_ulong sys_ndb_autoincrement_prefetch_sz("ndb_autoincrement_prefetch_sz", &SV::ndb_autoincrement_prefetch_sz); sys_var_thd_bool -sys_ndb_force_send("ndb_force_send", - &SV::ndb_force_send); +sys_ndb_force_send("ndb_force_send", &SV::ndb_force_send); sys_var_thd_bool -sys_ndb_use_exact_count("ndb_use_exact_count", - &SV::ndb_use_exact_count); +sys_ndb_use_exact_count("ndb_use_exact_count", &SV::ndb_use_exact_count); sys_var_thd_bool -sys_ndb_use_transactions("ndb_use_transactions", - &SV::ndb_use_transactions); -// ndb server global variable settings -// none +sys_ndb_use_transactions("ndb_use_transactions", &SV::ndb_use_transactions); #endif /* Time/date/datetime formats */ @@ -2856,7 +2851,8 @@ int set_var::check(THD *thd) return 0; } - if (value->fix_fields(thd, 0, &value) || value->check_cols(1)) + if ((!value->fixed && + value->fix_fields(thd, 0, &value)) || value->check_cols(1)) return -1; if (var->check_update_type(value->result_type())) { @@ -2890,7 +2886,8 @@ int set_var::light_check(THD *thd) if (type == OPT_GLOBAL && check_global_access(thd, SUPER_ACL)) return 1; - if (value && (value->fix_fields(thd, 0, &value) || value->check_cols(1))) + if (value && ((!value->fixed && value->fix_fields(thd, 0, &value)) || + value->check_cols(1))) return -1; return 0; } diff --git a/sql/slave.cc b/sql/slave.cc index 9ddbe7d05de..6b8559859fc 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -658,7 +658,7 @@ int terminate_slave_threads(MASTER_INFO* mi,int thread_mask,bool skip_lock) int terminate_slave_thread(THD* thd, pthread_mutex_t* term_lock, pthread_mutex_t *cond_lock, pthread_cond_t* term_cond, - volatile bool* slave_running) + volatile uint *slave_running) { if (term_lock) { @@ -696,7 +696,7 @@ int terminate_slave_thread(THD* thd, pthread_mutex_t* term_lock, int start_slave_thread(pthread_handler h_func, pthread_mutex_t *start_lock, pthread_mutex_t *cond_lock, pthread_cond_t *start_cond, - volatile bool *slave_running, + volatile uint *slave_running, volatile ulong *slave_run_id, MASTER_INFO* mi, bool high_priority) @@ -1081,7 +1081,7 @@ void end_slave() static bool io_slave_killed(THD* thd, MASTER_INFO* mi) { DBUG_ASSERT(mi->io_thd == thd); - DBUG_ASSERT(mi->slave_running == 1); // tracking buffer overrun + DBUG_ASSERT(mi->slave_running); // tracking buffer overrun return mi->abort_slave || abort_loop || thd->killed; } @@ -1949,19 +1949,13 @@ void init_master_info_with_options(MASTER_INFO* mi) strmake(mi->ssl_key, master_ssl_key, sizeof(mi->ssl_key)-1); } -static void clear_slave_error(RELAY_LOG_INFO* rli) +void clear_slave_error(RELAY_LOG_INFO* rli) { /* Clear the errors displayed by SHOW SLAVE STATUS */ rli->last_slave_error[0]= 0; rli->last_slave_errno= 0; } -void clear_slave_error_timestamp(RELAY_LOG_INFO* rli) -{ - rli->last_master_timestamp= 0; - clear_slave_error(rli); -} - /* Reset UNTIL condition for RELAY_LOG_INFO SYNOPSYS @@ -2349,6 +2343,11 @@ bool show_master_info(THD* thd, MASTER_INFO* mi) String *packet= &thd->packet; protocol->prepare_for_resend(); + /* + TODO: we read slave_running without run_lock, whereas these variables + are updated under run_lock and not data_lock. In 5.0 we should lock + run_lock on top of data_lock (with good order). + */ pthread_mutex_lock(&mi->data_lock); pthread_mutex_lock(&mi->rli.data_lock); @@ -2409,7 +2408,12 @@ bool show_master_info(THD* thd, MASTER_INFO* mi) protocol->store(mi->ssl_cipher, &my_charset_bin); protocol->store(mi->ssl_key, &my_charset_bin); - if (mi->rli.last_master_timestamp) + /* + Seconds_Behind_Master: if SQL thread is running and I/O thread is + connected, we can compute it otherwise show NULL (i.e. unknown). + */ + if ((mi->slave_running == MYSQL_SLAVE_RUN_CONNECT) && + mi->rli.slave_running) { long tmp= (long)((time_t)time((time_t*) 0) - mi->rli.last_master_timestamp) @@ -2429,9 +2433,13 @@ bool show_master_info(THD* thd, MASTER_INFO* mi) slave is 2. At SHOW SLAVE STATUS time, assume that the difference between timestamp of slave and rli->last_master_timestamp is 0 (i.e. they are in the same second), then we get 0-(2-1)=-1 as a result. - This confuses users, so we don't go below 0. + This confuses users, so we don't go below 0: hence the max(). + + last_master_timestamp == 0 (an "impossible" timestamp 1970) is a + special marker to say "consider we have caught up". */ - protocol->store((longlong)(max(0, tmp))); + protocol->store((longlong)(mi->rli.last_master_timestamp ? max(0, tmp) + : 0)); } else protocol->store_null(); @@ -3280,6 +3288,8 @@ slave_begin: connected: + // TODO: the assignment below should be under mutex (5.0) + mi->slave_running= MYSQL_SLAVE_RUN_CONNECT; thd->slave_net = &mysql->net; thd->proc_info = "Checking master version"; if (get_master_version_and_clock(mysql, mi)) @@ -3312,6 +3322,7 @@ dump"); goto err; } + mi->slave_running= MYSQL_SLAVE_RUN_NOT_CONNECT; thd->proc_info= "Waiting to reconnect after a failed binlog dump request"; #ifdef SIGNAL_WITH_VIO_CLOSE thd->clear_active_vio(); @@ -3388,6 +3399,7 @@ max_allowed_packet", mysql_error(mysql)); goto err; } + mi->slave_running= MYSQL_SLAVE_RUN_NOT_CONNECT; thd->proc_info = "Waiting to reconnect after a failed master event read"; #ifdef SIGNAL_WITH_VIO_CLOSE thd->clear_active_vio(); @@ -3566,6 +3578,14 @@ slave_begin: pthread_mutex_lock(&LOCK_thread_count); threads.append(thd); pthread_mutex_unlock(&LOCK_thread_count); + /* + We are going to set slave_running to 1. Assuming slave I/O thread is + alive and connected, this is going to make Seconds_Behind_Master be 0 + i.e. "caught up". Even if we're just at start of thread. Well it's ok, at + the moment we start we can think we are caught up, and the next second we + start receiving data so we realize we are not caught up and + Seconds_Behind_Master grows. No big deal. + */ rli->slave_running = 1; rli->abort_slave = 0; pthread_mutex_unlock(&rli->run_lock); @@ -4604,6 +4624,21 @@ Log_event* next_event(RELAY_LOG_INFO* rli) */ if (hot_log) { + /* + We say in Seconds_Behind_Master that we have "caught up". Note that + for example if network link is broken but I/O slave thread hasn't + noticed it (slave_net_timeout not elapsed), then we'll say "caught + up" whereas we're not really caught up. Fixing that would require + internally cutting timeout in smaller pieces in network read, no + thanks. Another example: SQL has caught up on I/O, now I/O has read + a new event and is queuing it; the false "0" will exist until SQL + finishes executing the new event; it will be look abnormal only if + the events have old timestamps (then you get "many", 0, "many"). + Transient phases like this can't really be fixed. + */ + time_t save_timestamp= rli->last_master_timestamp; + rli->last_master_timestamp= 0; + DBUG_ASSERT(rli->relay_log.get_open_count() == rli->cur_log_old_open_count); /* We can, and should release data_lock while we are waiting for @@ -4650,6 +4685,7 @@ Log_event* next_event(RELAY_LOG_INFO* rli) rli->relay_log.wait_for_update(rli->sql_thd, 1); // re-acquire data lock since we released it earlier pthread_mutex_lock(&rli->data_lock); + rli->last_master_timestamp= save_timestamp; continue; } /* diff --git a/sql/slave.h b/sql/slave.h index 69d3dc38e78..e0816fd45a7 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -93,6 +93,21 @@ extern my_bool opt_log_slave_updates; extern ulonglong relay_log_space_limit; struct st_master_info; +/* + 3 possible values for MASTER_INFO::slave_running and + RELAY_LOG_INFO::slave_running. + The values 0,1,2 are very important: to keep the diff small, I didn't + substitute places where we use 0/1 with the newly defined symbols. So don't change + these values. + The same way, code is assuming that in RELAY_LOG_INFO we use only values + 0/1. + I started with using an enum, but + enum_variable=1; is not legal so would have required many line changes. +*/ +#define MYSQL_SLAVE_NOT_RUN 0 +#define MYSQL_SLAVE_RUN_NOT_CONNECT 1 +#define MYSQL_SLAVE_RUN_CONNECT 2 + /**************************************************************************** Replication SQL Thread @@ -248,7 +263,8 @@ typedef struct st_relay_log_info /* if not set, the value of other members of the structure are undefined */ bool inited; - volatile bool abort_slave, slave_running; + volatile bool abort_slave; + volatile uint slave_running; /* Condition and its parameters from START SLAVE UNTIL clause. @@ -383,7 +399,8 @@ typedef struct st_master_info int events_till_abort; #endif bool inited; - volatile bool abort_slave, slave_running; + volatile bool abort_slave; + volatile uint slave_running; volatile ulong slave_run_id; /* The difference in seconds between the clock of the master and the clock of @@ -462,7 +479,7 @@ int terminate_slave_threads(MASTER_INFO* mi, int thread_mask, int terminate_slave_thread(THD* thd, pthread_mutex_t* term_mutex, pthread_mutex_t* cond_lock, pthread_cond_t* term_cond, - volatile bool* slave_running); + volatile uint* slave_running); int start_slave_threads(bool need_slave_mutex, bool wait_for_start, MASTER_INFO* mi, const char* master_info_fname, const char* slave_info_fname, int thread_mask); @@ -475,7 +492,7 @@ int start_slave_threads(bool need_slave_mutex, bool wait_for_start, int start_slave_thread(pthread_handler h_func, pthread_mutex_t* start_lock, pthread_mutex_t *cond_lock, pthread_cond_t* start_cond, - volatile bool *slave_running, + volatile uint *slave_running, volatile ulong *slave_run_id, MASTER_INFO* mi, bool high_priority); @@ -517,7 +534,7 @@ void slave_print_error(RELAY_LOG_INFO* rli, int err_code, const char* msg, ...); void end_slave(); /* clean up */ void init_master_info_with_options(MASTER_INFO* mi); void clear_until_condition(RELAY_LOG_INFO* rli); -void clear_slave_error_timestamp(RELAY_LOG_INFO* rli); +void clear_slave_error(RELAY_LOG_INFO* rli); int init_master_info(MASTER_INFO* mi, const char* master_info_fname, const char* slave_info_fname, bool abort_if_no_master_info_file, diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index d33faffb2c2..81419d64e15 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -26,7 +26,6 @@ */ #include "mysql_priv.h" -#include "sql_acl.h" #include "hash_filo.h" #ifdef HAVE_REPLICATION #include "sql_repl.h" //for tables_ok() diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 2500769ee30..250da2981ff 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -18,7 +18,6 @@ /* Basic functions needed by many modules */ #include "mysql_priv.h" -#include "sql_acl.h" #include "sql_select.h" #include "sp_head.h" #include "sql_trigger.h" diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 39ee8f61f1a..26b1eff49e7 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -300,7 +300,6 @@ TODO list: #include <m_ctype.h> #include <my_dir.h> #include <hash.h> -#include "sql_acl.h" #include "ha_myisammrg.h" #ifndef MASTER #include "../srclib/myisammrg/myrg_def.h" @@ -375,7 +374,7 @@ inline Query_cache_block * Query_cache_block_table::block() return (Query_cache_block *)(((byte*)this) - ALIGN_SIZE(sizeof(Query_cache_block_table)*n) - ALIGN_SIZE(sizeof(Query_cache_block))); -}; +} /***************************************************************************** Query_cache_block method(s) diff --git a/sql/sql_class.cc b/sql/sql_class.cc index cc178a3121b..d369e85d775 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -27,7 +27,6 @@ #endif #include "mysql_priv.h" -#include "sql_acl.h" #include <m_ctype.h> #include <sys/stat.h> #include <thr_alarm.h> @@ -1025,7 +1024,7 @@ static File create_file(THD *thd, char *path, sql_exchange *exchange, return -1; } /* Create the file world readable */ - if ((file= my_create(path, 0666, O_WRONLY, MYF(MY_WME))) < 0) + if ((file= my_create(path, 0666, O_WRONLY|O_EXCL, MYF(MY_WME))) < 0) return file; #ifdef HAVE_FCHMOD (void) fchmod(file, 0666); // Because of umask() @@ -1307,9 +1306,10 @@ bool select_singlerow_subselect::send_data(List<Item> &items) bool select_max_min_finder_subselect::send_data(List<Item> &items) { DBUG_ENTER("select_max_min_finder_subselect::send_data"); - Item_singlerow_subselect *it= (Item_singlerow_subselect *)item; + Item_maxmin_subselect *it= (Item_maxmin_subselect *)item; List_iterator_fast<Item> li(items); Item *val_item= li++; + it->register_value(); if (it->assigned()) { cache->store(val_item); diff --git a/sql/sql_class.h b/sql/sql_class.h index 6cec2c2c787..149d12225a3 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1371,9 +1371,11 @@ class select_insert :public select_result_interceptor { COPY_INFO info; bool insert_into_view; - select_insert(TABLE_LIST *table_list_par, TABLE *table_par, - List<Item> *fields_par, enum_duplicates duplic, - bool ignore_check_option_errors); + select_insert(TABLE_LIST *table_list_par, + TABLE *table_par, List<Item> *fields_par, + List<Item> *update_fields, List<Item> *update_values, + enum_duplicates duplic, + bool ignore_check_option_errors); ~select_insert(); int prepare(List<Item> &list, SELECT_LEX_UNIT *u); bool send_data(List<Item> &items); diff --git a/sql/sql_db.cc b/sql/sql_db.cc index ea81013a401..d3b30de0bcd 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -19,7 +19,6 @@ #include "mysql_priv.h" #include <mysys_err.h> -#include "sql_acl.h" #include "sp.h" #include <my_dir.h> #include <m_ctype.h> @@ -399,7 +398,7 @@ bool mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create_info, VOID(pthread_mutex_lock(&LOCK_mysql_create_db)); - // do not create database if another thread is holding read lock + /* do not create database if another thread is holding read lock */ if (wait_if_global_read_lock(thd, 0, 1)) { error= -1; @@ -522,7 +521,7 @@ bool mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create_info) VOID(pthread_mutex_lock(&LOCK_mysql_create_db)); - // do not alter database if another thread is holding read lock + /* do not alter database if another thread is holding read lock */ if ((error=wait_if_global_read_lock(thd,0,1))) goto exit2; @@ -549,9 +548,11 @@ bool mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create_info) Query_log_event qinfo(thd, thd->query, thd->query_length, 0, /* suppress_use */ TRUE); - // Write should use the database being created as the "current - // database" and not the threads current database, which is the - // default. + /* + Write should use the database being created as the "current + database" and not the threads current database, which is the + default. + */ qinfo.db = db; qinfo.db_len = strlen(db); @@ -595,7 +596,7 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) VOID(pthread_mutex_lock(&LOCK_mysql_create_db)); - // do not drop database if another thread is holding read lock + /* do not drop database if another thread is holding read lock */ if (wait_if_global_read_lock(thd, 0, 1)) { error= -1; @@ -662,10 +663,11 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) { Query_log_event qinfo(thd, query, query_length, 0, /* suppress_use */ TRUE); - - // Write should use the database being created as the "current - // database" and not the threads current database, which is the - // default. + /* + Write should use the database being created as the "current + database" and not the threads current database, which is the + default. + */ qinfo.db = db; qinfo.db_len = strlen(db); @@ -799,7 +801,7 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db, found_other_files++; continue; } - // just for safety we use files_charset_info + /* just for safety we use files_charset_info */ if (db && !my_strcasecmp(files_charset_info, extension, reg_ext)) { diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index 1d4b911bb65..520393f8544 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -23,7 +23,6 @@ #include "mysql_priv.h" #include "sql_select.h" -#include "sql_acl.h" diff --git a/sql/sql_do.cc b/sql/sql_do.cc index 3f34835c2c9..e37f3e86dda 100644 --- a/sql/sql_do.cc +++ b/sql/sql_do.cc @@ -18,7 +18,6 @@ /* Execute DO statement */ #include "mysql_priv.h" -#include "sql_acl.h" bool mysql_do(THD *thd, List<Item> &values) { diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 853b3dd37c6..2ee7734e4f3 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -409,7 +409,8 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables, } tables->table=table; - if (cond && (cond->fix_fields(thd, tables, &cond) || cond->check_cols(1))) + if (cond && ((!cond->fixed && + cond->fix_fields(thd, tables, &cond)) || cond->check_cols(1))) goto err0; table->file->init_table_handle_for_HANDLER(); // Only InnoDB requires it @@ -494,7 +495,8 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables, for (key_len=0 ; (item=it_ke++) ; key_part++) { // 'item' can be changed by fix_fields() call - if (item->fix_fields(thd, tables, it_ke.ref()) || + if ((!item->fixed && + item->fix_fields(thd, tables, it_ke.ref())) || (item= *it_ke.ref())->check_cols(1)) goto err; if (item->used_tables() & ~RAND_TABLE_BIT) diff --git a/sql/sql_help.cc b/sql/sql_help.cc index f71e9a7133a..99273b42f2a 100644 --- a/sql/sql_help.cc +++ b/sql/sql_help.cc @@ -556,7 +556,8 @@ int send_variant_2_list(MEM_ROOT *mem_root, Protocol *protocol, SQL_SELECT *prepare_simple_select(THD *thd, Item *cond, TABLE_LIST *tables, TABLE *table, int *error) { - cond->fix_fields(thd, tables, &cond); // can never fail + if (!cond->fixed) + cond->fix_fields(thd, tables, &cond); // can never fail SQL_SELECT *res= make_select(table,0,0,cond,error); if (*error || (res && res->check_quick(thd, 0, HA_POS_ERROR))) { diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 0814c7a747e..e2b7ee93905 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -18,7 +18,6 @@ /* Insert of records */ #include "mysql_priv.h" -#include "sql_acl.h" #include "sp_head.h" #include "sql_trigger.h" #include "sql_select.h" @@ -244,13 +243,6 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, thd->used_tables=0; values= its++; - if (duplic == DUP_UPDATE) - { - /* it should be allocated before Item::fix_fields() */ - if (table_list->set_insert_values(thd->mem_root)) - goto abort; - } - if (mysql_prepare_insert(thd, table_list, table, fields, values, update_fields, update_values, duplic)) goto abort; @@ -666,26 +658,33 @@ static bool mysql_prepare_insert_check_table(THD *thd, TABLE_LIST *table_list, */ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, TABLE *table, - List<Item> &fields, List_item *values, - List<Item> &update_fields, List<Item> &update_values, - enum_duplicates duplic) + List<Item> &fields, List_item *values, + List<Item> &update_fields, List<Item> &update_values, + enum_duplicates duplic) { bool insert_into_view= (table_list->view != 0); /* TODO: use this condition for 'WITH CHECK OPTION' */ Item *unused_conds= 0; bool res; DBUG_ENTER("mysql_prepare_insert"); - DBUG_PRINT("enter", ("table_list 0x%lx, table 0x%lx, view %d", (ulong)table_list, (ulong)table, (int)insert_into_view)); + + if (duplic == DUP_UPDATE) + { + /* it should be allocated before Item::fix_fields() */ + if (table_list->set_insert_values(thd->mem_root)) + goto abort; + } + if (mysql_prepare_insert_check_table(thd, table_list, fields, &unused_conds, FALSE)) DBUG_RETURN(TRUE); - if (check_insert_fields(thd, table_list, fields, *values, 1, - !insert_into_view) || - setup_fields(thd, 0, table_list, *values, 0, 0, 0) || + if ((values && check_insert_fields(thd, table_list, fields, *values, 1, + !insert_into_view)) || + (values && setup_fields(thd, 0, table_list, *values, 0, 0, 0)) || (duplic == DUP_UPDATE && ((thd->lex->select_lex.no_wrap_view_item= 1, (res= setup_fields(thd, 0, table_list, update_fields, 1, 0, 0)), @@ -697,7 +696,9 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, TABLE *table, if (!table) table= table_list->table; - if (unique_table(table_list, table_list->next_global)) + if ((thd->lex->sql_command == SQLCOM_INSERT || + thd->lex->sql_command == SQLCOM_REPLACE) && + unique_table(table_list, table_list->next_global)) { my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->real_name); DBUG_RETURN(TRUE); @@ -795,8 +796,10 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) that matches, is updated. If update causes a conflict again, an error is returned */ + DBUG_ASSERT(table->insert_values != NULL); store_record(table,insert_values); restore_record(table,record[1]); + DBUG_ASSERT(info->update_fields->elements == info->update_values->elements); if (fill_record(thd, *info->update_fields, *info->update_values, 0)) goto err; @@ -1753,17 +1756,21 @@ bool mysql_insert_select_prepare(THD *thd) select_insert::select_insert(TABLE_LIST *table_list_par, TABLE *table_par, - List<Item> *fields_par, enum_duplicates duplic, + List<Item> *fields_par, + List<Item> *update_fields, List<Item> *update_values, + enum_duplicates duplic, bool ignore_check_option_errors) :table_list(table_list_par), table(table_par), fields(fields_par), last_insert_id(0), insert_into_view(table_list_par && table_list_par->view != 0) { bzero((char*) &info,sizeof(info)); - info.handle_duplicates=duplic; + info.handle_duplicates= duplic; + info.ignore= ignore_check_option_errors; + info.update_fields= update_fields; + info.update_values= update_values; if (table_list_par) info.view= (table_list_par->view ? table_list_par : 0); - info.ignore= ignore_check_option_errors; } diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 2205ec504e9..ece960c5d1c 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -129,6 +129,7 @@ void lex_start(THD *thd, uchar *buf,uint length) lex->thd= lex->unit.thd= thd; lex->select_lex.init_query(); lex->value_list.empty(); + lex->update_list.empty(); lex->param_list.empty(); lex->view_list.empty(); lex->unit.next= lex->unit.master= diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 7a4ef0b98e5..3ef475b89b0 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -682,7 +682,7 @@ typedef struct st_lex List<LEX_COLUMN> columns; List<Key> key_list; List<create_field> create_list; - List<Item> *insert_list,field_list,value_list; + List<Item> *insert_list,field_list,value_list,update_list; List<List_item> many_values; List<set_var_base> var_list; List<Item_param> param_list; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 01e80653372..b5b0d76dcf3 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -15,7 +15,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "mysql_priv.h" -#include "sql_acl.h" #include "sql_repl.h" #include "repl_failsafe.h" #include <m_ctype.h> @@ -1129,13 +1128,26 @@ extern "C" pthread_handler_decl(handle_bootstrap,arg) thd->init_for_queries(); while (fgets(buff, thd->net.max_packet, file)) { - uint length=(uint) strlen(buff); - if (buff[length-1]!='\n' && !feof(file)) - { - net_send_error(thd, ER_NET_PACKET_TOO_LARGE, NullS); - thd->fatal_error(); - break; - } + ulong length= (ulong) strlen(buff); + while (buff[length-1] != '\n' && !feof(file)) + { + /* + We got only a part of the current string. Will try to increase + net buffer then read the rest of the current string. + */ + if (net_realloc(&(thd->net), 2 * thd->net.max_packet)) + { + net_send_error(thd, ER_NET_PACKET_TOO_LARGE, NullS); + thd->fatal_error(); + break; + } + buff= (char*) thd->net.buff; + fgets(buff + length, thd->net.max_packet - length, file); + length+= (ulong) strlen(buff + length); + } + if (thd->is_fatal_error) + break; + while (length && (my_isspace(thd->charset(), buff[length-1]) || buff[length-1] == ';')) length--; @@ -2817,7 +2829,9 @@ create_error: check_access(thd, SELECT_ACL | EXTRA_ACL, first_table->db, &first_table->grant.privilege, 0, 0)) goto error; - res = mysqld_show_create(thd, first_table); + if (grant_option && check_grant(thd, SELECT_ACL, all_tables, 2, UINT_MAX, 0)) + goto error; + res= mysqld_show_create(thd, first_table); break; } #endif @@ -2945,7 +2959,7 @@ create_error: if ((res= insert_precheck(thd, all_tables))) break; res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values, - select_lex->item_list, lex->value_list, + lex->update_list, lex->value_list, (lex->value_list.elements ? DUP_UPDATE : lex->duplicates)); if (first_table->view && !first_table->contain_auto_increment) @@ -2956,7 +2970,7 @@ create_error: case SQLCOM_INSERT_SELECT: { DBUG_ASSERT(first_table == all_tables && first_table != 0); - if ((res= insert_select_precheck(thd, all_tables))) + if ((res= insert_precheck(thd, all_tables))) break; /* Fix lock for first table */ @@ -2969,19 +2983,23 @@ create_error: select_result *result; unit->set_limit(select_lex, select_lex); - if (!(res= open_and_lock_tables(thd, all_tables))) + if (!(res= open_and_lock_tables(thd, all_tables)) && + !(res= mysql_prepare_insert(thd, tables, first_local_table, + tables->table, lex->field_list, 0, + lex->update_list, lex->value_list, + lex->duplicates))) { + TABLE *table= tables->table; /* Skip first table, which is the table we are inserting in */ lex->select_lex.table_list.first= (byte*)first_table->next_local; res= mysql_insert_select_prepare(thd); if (!res && (result= new select_insert(first_table, first_table->table, &lex->field_list, + &lex->update_list, &lex->value_list, lex->duplicates, lex->duplicates == DUP_IGNORE))) { - TABLE_LIST *first_select_table; - /* insert/replace from SELECT give its SELECT_LEX for SELECT, and item_list belong to SELECT @@ -2990,6 +3008,7 @@ create_error: res= handle_select(thd, lex, result); lex->select_lex.resolve_mode= SELECT_LEX::INSERT_MODE; delete result; + table->insert_values= 0; } /* revert changes for SP */ lex->select_lex.table_list.first= (byte*) first_table; @@ -3355,7 +3374,13 @@ create_error: } case SQLCOM_ALTER_DB: { - if (!strip_sp(lex->name) || check_db_name(lex->name)) + char *db= lex->name ? lex->name : thd->db; + if (!db) + { + my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0)); + break; + } + if (!strip_sp(db) || check_db_name(db)) { my_error(ER_WRONG_DB_NAME, MYF(0), lex->name); break; @@ -3369,14 +3394,14 @@ create_error: */ #ifdef HAVE_REPLICATION if (thd->slave_thread && - (!db_ok(lex->name, replicate_do_db, replicate_ignore_db) || - !db_ok_with_wild_table(lex->name))) + (!db_ok(db, replicate_do_db, replicate_ignore_db) || + !db_ok_with_wild_table(db))) { my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0)); break; } #endif - if (check_access(thd,ALTER_ACL,lex->name,0,1,0)) + if (check_access(thd, ALTER_ACL, db, 0, 1, 0)) break; if (thd->locked_tables || thd->active_transaction()) { @@ -3384,7 +3409,7 @@ create_error: ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0)); goto error; } - res=mysql_alter_db(thd,lex->name,&lex->create_info); + res= mysql_alter_db(thd, db, &lex->create_info); break; } case SQLCOM_SHOW_CREATE_DB: @@ -3396,12 +3421,6 @@ create_error: } if (check_access(thd,SELECT_ACL,lex->name,0,1,0)) break; - if (thd->locked_tables || thd->active_transaction()) - { - my_message(ER_LOCK_OR_ACTIVE_TRANSACTION, - ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0)); - goto error; - } res=mysqld_show_create_db(thd,lex->name,&lex->create_info); break; } @@ -4093,7 +4112,7 @@ error: /* Check grants for commands which work only with one table and all other - tables belong to subselects. + tables belonging to subselects or implicitly opened tables. SYNOPSIS check_one_table_access() @@ -4116,7 +4135,7 @@ bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *all_tables) if (grant_option && check_grant(thd, privilege, all_tables, 0, 1, 0)) return 1; - /* Check rights on tables of subselect (if exists) */ + /* Check rights on tables of subselects and implictly opened tables */ TABLE_LIST *subselects_tables; if ((subselects_tables= all_tables->next_global)) { @@ -5977,9 +5996,9 @@ Item * all_any_subquery_creator(Item *left_expr, Item_allany_subselect *it= new Item_allany_subselect(left_expr, (*cmp)(all), select_lex, all); if (all) - return it->upper_not= new Item_func_not_all(it); /* ALL */ + return it->upper_item= new Item_func_not_all(it); /* ALL */ - return it; /* ANY/SOME */ + return it->upper_item= new Item_func_nop_all(it); /* ANY/SOME */ } @@ -6083,6 +6102,9 @@ bool multi_update_precheck(THD *thd, TABLE_LIST *tables) DBUG_PRINT("info",("Checking sub query list")); for (table= tables; table; table= table->next_global) { + if (my_tz_check_n_skip_implicit_tables(&table, + lex->time_zone_tables_used)) + continue; if (!table->table_in_first_from_clause && table->derived) { if (check_access(thd, SELECT_ACL, table->db, @@ -6168,32 +6190,6 @@ bool multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count) /* - INSERT ... SELECT query pre-check - - SYNOPSIS - insert_delete_precheck() - thd Thread handler - tables Global table list - - RETURN VALUE - FALSE OK - TRUE Error -*/ - -bool insert_select_precheck(THD *thd, TABLE_LIST *tables) -{ - DBUG_ENTER("insert_select_precheck"); - /* - Check that we have modify privileges for the first table and - select privileges for the rest - */ - ulong privilege= (thd->lex->duplicates == DUP_REPLACE ? - INSERT_ACL | DELETE_ACL : INSERT_ACL); - DBUG_RETURN(check_one_table_access(thd, privilege, tables)); -} - - -/* simple UPDATE query pre-check SYNOPSIS @@ -6261,6 +6257,10 @@ bool insert_precheck(THD *thd, TABLE_LIST *tables) LEX *lex= thd->lex; DBUG_ENTER("insert_precheck"); + /* + Check that we have modify privileges for the first table and + select privileges for the rest + */ ulong privilege= (INSERT_ACL | (lex->duplicates == DUP_REPLACE ? DELETE_ACL : 0) | (lex->value_list.elements ? UPDATE_ACL : 0)); @@ -6268,7 +6268,7 @@ bool insert_precheck(THD *thd, TABLE_LIST *tables) if (check_one_table_access(thd, privilege, tables)) DBUG_RETURN(TRUE); - if (lex->select_lex.item_list.elements != lex->value_list.elements) + if (lex->update_list.elements != lex->value_list.elements) { my_message(ER_WRONG_VALUE_COUNT, ER(ER_WRONG_VALUE_COUNT), MYF(0)); DBUG_RETURN(TRUE); diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 8afefe3cae8..e4e61dc8d31 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -68,7 +68,6 @@ Long data handling: ***********************************************************************/ #include "mysql_priv.h" -#include "sql_acl.h" #include "sql_select.h" // for JOIN #include <m_ctype.h> // for isspace() #include "sp_head.h" @@ -1387,7 +1386,7 @@ static int mysql_test_insert_select(Prepared_statement *stmt, { int res; LEX *lex= stmt->lex; - if ((res= insert_select_precheck(stmt->thd, tables))) + if ((res= insert_precheck(stmt->thd, tables))) return res; TABLE_LIST *first_local_table= (TABLE_LIST *)lex->select_lex.table_list.first; @@ -1584,6 +1583,27 @@ static bool init_param_array(Prepared_statement *stmt) } +/* Init statement before execution */ + +static void cleanup_stmt_for_execute(Prepared_statement *stmt) +{ + THD *thd= stmt->thd; + LEX *lex= stmt->lex; + SELECT_LEX *sl= lex->all_selects_list; + + for (; sl; sl= sl->next_select_in_list()) + { + for (TABLE_LIST *tables= (TABLE_LIST*) sl->table_list.first; + tables; + tables= tables->next) + { + if (tables->table) + tables->table->insert_values= 0; + } + } +} + + /* Given a query string with parameter markers, create a Prepared Statement from it and send PS info back to the client. @@ -1678,6 +1698,7 @@ bool mysql_stmt_prepare(THD *thd, char *packet, uint packet_length, if (!error) error= check_prepared_statement(stmt, test(name)); + cleanup_stmt_for_execute(stmt); /* restore to WAIT_PRIOR: QUERY_PRIOR is set inside alloc_query */ if (!(specialflag & SPECIAL_NO_PRIOR)) @@ -2038,6 +2059,7 @@ static void execute_stmt(THD *thd, Prepared_statement *stmt, reset_stmt_params(stmt); close_thread_tables(thd); // to close derived tables thd->set_statement(&thd->stmt_backup); + cleanup_stmt_for_execute(stmt); thd->cleanup_after_query(); if (stmt->state == Item_arena::PREPARED) diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 0d1a7f3890d..9f083e19146 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -18,7 +18,6 @@ #ifdef HAVE_REPLICATION #include "sql_repl.h" -#include "sql_acl.h" #include "log_event.h" #include <my_dir.h> @@ -977,10 +976,10 @@ int reset_slave(THD *thd, MASTER_INFO* mi) */ init_master_info_with_options(mi); /* - Reset errors, and master timestamp (the idea is that we forget about the + Reset errors (the idea is that we forget about the old master). */ - clear_slave_error_timestamp(&mi->rli); + clear_slave_error(&mi->rli); clear_until_condition(&mi->rli); // close master_info_file, relay_log_info_file, set mi->inited=rli->inited=0 @@ -1240,8 +1239,8 @@ bool change_master(THD* thd, MASTER_INFO* mi) pthread_mutex_lock(&mi->rli.data_lock); mi->rli.abort_pos_wait++; /* for MASTER_POS_WAIT() to abort */ - /* Clear the errors, for a clean start, and master timestamp */ - clear_slave_error_timestamp(&mi->rli); + /* Clear the errors, for a clean start */ + clear_slave_error(&mi->rli); clear_until_condition(&mi->rli); /* If we don't write new coordinates to disk now, then old will remain in diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 570774c8054..44eb411ed44 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1543,7 +1543,7 @@ JOIN::exec() WHERE clause for any tables after the sorted one. */ JOIN_TAB *curr_table= &curr_join->join_tab[curr_join->const_tables+1]; - JOIN_TAB *end_table= &curr_join->join_tab[tables]; + JOIN_TAB *end_table= &curr_join->join_tab[curr_join->tables]; for (; curr_table < end_table ; curr_table++) { /* @@ -9213,7 +9213,7 @@ join_read_system(JOIN_TAB *tab) { if (error != HA_ERR_END_OF_FILE) return report_error(table, error); - table->null_row=1; // This is ok. + mark_as_null_row(tab->table); empty_record(table); // Make empty record return -1; } @@ -9258,7 +9258,7 @@ join_read_const(JOIN_TAB *tab) if (error) { table->status= STATUS_NOT_FOUND; - table->null_row=1; + mark_as_null_row(tab->table); empty_record(table); if (error != HA_ERR_KEY_NOT_FOUND) return report_error(table, error); @@ -12266,7 +12266,8 @@ static bool add_ref_to_table_cond(THD *thd, JOIN_TAB *join_tab) if (thd->is_fatal_error) DBUG_RETURN(TRUE); - cond->fix_fields(thd,(TABLE_LIST *) 0, (Item**)&cond); + if (!cond->fixed) + cond->fix_fields(thd,(TABLE_LIST *) 0, (Item**)&cond); if (join_tab->select) { error=(int) cond->add(join_tab->select->cond); diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 826bd2038f9..986f8027df2 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -19,7 +19,6 @@ #include "mysql_priv.h" #include "sql_select.h" // For select_describe -#include "sql_acl.h" #include "repl_failsafe.h" #include "sp_head.h" #include <my_dir.h> diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 6629122a1fa..3cc421b1312 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -491,44 +491,45 @@ int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, DBUG_RETURN(-1); } - if ((sql_field->sql_type == FIELD_TYPE_SET || - sql_field->sql_type == FIELD_TYPE_ENUM) && !sql_field->interval) + if (sql_field->sql_type == FIELD_TYPE_SET || + sql_field->sql_type == FIELD_TYPE_ENUM) { uint32 dummy; CHARSET_INFO *cs= sql_field->charset; - TYPELIB *interval; + TYPELIB *interval= sql_field->interval; /* Create typelib from interval_list, and if necessary convert strings from client character set to the column character set. */ - - interval= sql_field->interval= typelib(sql_field->interval_list); - List_iterator<String> it(sql_field->interval_list); - String conv, *tmp; - for (uint i= 0; (tmp= it++); i++) + if (!interval) { - if (String::needs_conversion(tmp->length(), tmp->charset(), cs, - &dummy)) + interval= sql_field->interval= typelib(sql_field->interval_list); + List_iterator<String> it(sql_field->interval_list); + String conv, *tmp; + for (uint i= 0; (tmp= it++); i++) { - uint cnv_errs; - conv.copy(tmp->ptr(), tmp->length(), tmp->charset(), cs, &cnv_errs); - char *buf= (char*) sql_alloc(conv.length()+1); - memcpy(buf, conv.ptr(), conv.length()); - buf[conv.length()]= '\0'; - interval->type_names[i]= buf; - interval->type_lengths[i]= conv.length(); - } + if (String::needs_conversion(tmp->length(), tmp->charset(), + cs, &dummy)) + { + uint cnv_errs; + conv.copy(tmp->ptr(), tmp->length(), tmp->charset(), cs, &cnv_errs); + char *buf= (char*) sql_alloc(conv.length()+1); + memcpy(buf, conv.ptr(), conv.length()); + buf[conv.length()]= '\0'; + interval->type_names[i]= buf; + interval->type_lengths[i]= conv.length(); + } - // Strip trailing spaces. - uint lengthsp= cs->cset->lengthsp(cs, interval->type_names[i], - interval->type_lengths[i]); - interval->type_lengths[i]= lengthsp; - ((uchar *)interval->type_names[i])[lengthsp]= '\0'; + // Strip trailing spaces. + uint lengthsp= cs->cset->lengthsp(cs, interval->type_names[i], + interval->type_lengths[i]); + interval->type_lengths[i]= lengthsp; + ((uchar *)interval->type_names[i])[lengthsp]= '\0'; + } + sql_field->interval_list.empty(); // Don't need interval_list anymore } - sql_field->interval_list.empty(); // Don't need interval_list anymore - /* Convert the default value from client character diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 815e04c0111..d0a33163a38 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -398,6 +398,8 @@ bool st_select_lex_unit::exec() if (uncacheable || !item || !item->assigned() || describe) { + if (item) + item->reset_value_registration(); if (optimized && item) { if (item->assigned()) diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 9613e39d403..fa4acce31f7 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -21,7 +21,6 @@ */ #include "mysql_priv.h" -#include "sql_acl.h" #include "sql_select.h" #include "sp_head.h" #include "sql_trigger.h" @@ -225,7 +224,7 @@ int mysql_update(THD *thd, table_list->grant.want_privilege= table->grant.want_privilege= (SELECT_ACL & ~table->grant.privilege); #endif - if (setup_fields(thd, 0, table_list, values, 0, 0, 0)) + if (setup_fields(thd, 0, table_list, values, 1, 0, 0)) { free_underlaid_joins(thd, select_lex); DBUG_RETURN(1); /* purecov: inspected */ @@ -740,6 +739,19 @@ bool mysql_multi_update_prepare(THD *thd) } if (!using_lock_tables) tl->table->reginfo.lock_type= tl->lock_type; + + /* Check access privileges for table */ + { + TABLE_LIST *save= tl->next; + bool res; + tl->next= 0; + res= (check_access(thd, tl->updating ? UPDATE_ACL : SELECT_ACL, + tl->db, &tl->grant.privilege, 0, 0) || + (grant_option && check_grant(thd, wants, tl, 0, 0, 0))); + tl->next= save; + if (res) + DBUG_RETURN(TRUE); + } } /* check single table update for view compound from several tables */ diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 99b8caed784..1c2dde5d278 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -32,7 +32,6 @@ #define Select Lex->current_select #include "mysql_priv.h" #include "slave.h" -#include "sql_acl.h" #include "lex_symbol.h" #include "item_create.h" #include "sp_head.h" @@ -675,7 +674,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type <simple_string> remember_name remember_end opt_ident opt_db text_or_password - opt_constraint constraint + opt_constraint constraint ident_or_empty %type <string> text_string opt_gconcat_separator @@ -3236,7 +3235,7 @@ alter: } alter_list {} - | ALTER DATABASE ident + | ALTER DATABASE ident_or_empty { Lex->create_info.default_table_charset= NULL; Lex->create_info.used_fields= 0; @@ -3245,7 +3244,7 @@ alter: { LEX *lex=Lex; lex->sql_command=SQLCOM_ALTER_DB; - lex->name=$3.str; + lex->name= $3; } | ALTER PROCEDURE sp_name { @@ -3289,6 +3288,10 @@ alter: {} ; +ident_or_empty: + /* empty */ { $$= 0; } + | ident { $$= $1.str; }; + alter_list: | DISCARD TABLESPACE { Lex->alter_info.tablespace_op= DISCARD_TABLESPACE; } | IMPORT TABLESPACE { Lex->alter_info.tablespace_op= IMPORT_TABLESPACE; } @@ -5714,19 +5717,7 @@ expr_or_default: opt_insert_update: /* empty */ | ON DUPLICATE_SYM - { - LEX *lex= Lex; - /* - For simplicity, let's forget about INSERT ... SELECT ... UPDATE - for a moment. - */ - if (lex->sql_command != SQLCOM_INSERT) - { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; - } - } - KEY_SYM UPDATE_SYM update_list + KEY_SYM UPDATE_SYM insert_update_list ; /* Update rows in a table */ @@ -5762,16 +5753,26 @@ update: ; update_list: - update_list ',' simple_ident_nospvar equal expr_or_default + update_list ',' update_elem + | update_elem; + +update_elem: + simple_ident_nospvar equal expr_or_default + { + }; + +insert_update_list: + insert_update_list ',' insert_update_elem + | insert_update_elem; + +insert_update_elem: + simple_ident_nospvar equal expr_or_default { - if (add_item_to_list(YYTHD, $3) || add_value_to_list(YYTHD, $5)) + LEX *lex= Lex; + if (lex->update_list.push_back($1) || + lex->value_list.push_back($3)) YYABORT; - } - | simple_ident_nospvar equal expr_or_default - { - if (add_item_to_list(YYTHD, $1) || add_value_to_list(YYTHD, $3)) - YYABORT; - }; + }; opt_low_priority: /* empty */ { $$= YYTHD->update_lock_default; } diff --git a/sql/strfunc.cc b/sql/strfunc.cc index 2253f48e558..777b3851294 100644 --- a/sql/strfunc.cc +++ b/sql/strfunc.cc @@ -53,8 +53,22 @@ ulonglong find_set(TYPELIB *lib, const char *str, uint length, CHARSET_INFO *cs, { const char *pos= start; uint var_len; + int mblen= 1; - for (; pos != end && *pos != field_separator; pos++) ; + if (cs && cs->mbminlen > 1) + { + for ( ; pos < end; pos+= mblen) + { + my_wc_t wc; + if ((mblen= cs->cset->mb_wc(cs, &wc, (const uchar *) pos, + (const uchar *) end)) < 1) + mblen= 1; // Not to hang on a wrong multibyte sequence + if (wc == (my_wc_t) field_separator) + break; + } + } + else + for (; pos != end && *pos != field_separator; pos++) ; var_len= (uint) (pos - start); uint find= cs ? find_type2(lib, start, var_len, cs) : find_type(lib, start, var_len, (bool) 0); @@ -66,9 +80,9 @@ ulonglong find_set(TYPELIB *lib, const char *str, uint length, CHARSET_INFO *cs, } else found|= ((longlong) 1 << (find - 1)); - if (pos == end) + if (pos >= end) break; - start= pos + 1; + start= pos + mblen; } } return found; diff --git a/sql/table.cc b/sql/table.cc index c18a2557337..a37186287b4 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -509,6 +509,32 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, charset= outparam->table_charset; bzero((char*) &comment, sizeof(comment)); } + + if (interval_nr && charset->mbminlen > 1) + { + /* Unescape UCS2 intervals from HEX notation */ + TYPELIB *interval= outparam->intervals + interval_nr - 1; + for (uint pos= 0; pos < interval->count; pos++) + { + char *from, *to; + for (from= to= (char*) interval->type_names[pos]; *from; ) + { + /* + Note, hexchar_to_int(*from++) doesn't work + one some compilers, e.g. IRIX. Looks like a compiler + bug in inline functions in combination with arguments + that have a side effect. So, let's use from[0] and from[1] + and increment 'from' by two later. + */ + + *to++= (char) (hexchar_to_int(from[0]) << 4) + + hexchar_to_int(from[1]); + from+= 2; + } + interval->type_lengths[pos] /= 2; + } + } + *field_ptr=reg_field= make_field(record+recpos, (uint32) field_length, diff --git a/sql/table.h b/sql/table.h index a804376ee3c..871b96165b2 100644 --- a/sql/table.h +++ b/sql/table.h @@ -145,8 +145,14 @@ struct st_table { int current_lock; /* Type of lock on table */ enum tmp_table_type tmp_table; my_bool copy_blobs; /* copy_blobs when storing */ - my_bool null_row; /* All columns are null */ - my_bool maybe_null,outer_join; /* Used with OUTER JOIN */ + /* + Used in outer joins: if true, all columns are considered to have NULL + values, including columns declared as "not null". + */ + my_bool null_row; + /* 0 or JOIN_TYPE_{LEFT|RIGHT}, same as TABLE_LIST::outer_join */ + my_bool outer_join; + my_bool maybe_null; /* true if (outer_join != 0) */ my_bool force_index; my_bool distinct,const_table,no_rows; my_bool key_read; diff --git a/sql/tztime.h b/sql/tztime.h index 6d2388bb160..8b26c8fa378 100644 --- a/sql/tztime.h +++ b/sql/tztime.h @@ -66,8 +66,8 @@ extern void my_tz_free(); /* - Check if we have pointer to the beggining of list of implictly used - time zone tables and fast-forward to its end. + Check if we have pointer to the begining of list of implicitly used time + zone tables, set SELECT_ACL for them and fast-forward to its end. SYNOPSIS my_tz_check_n_skip_implicit_tables() @@ -87,6 +87,8 @@ inline bool my_tz_check_n_skip_implicit_tables(TABLE_LIST **table, { if (*table == tz_tables) { + for (int i= 0; i < 4; i++) + (*table)[i].grant.privilege= SELECT_ACL; (*table)+= 3; return TRUE; } diff --git a/sql/udf_example.cc b/sql/udf_example.cc index f0f33ed6fd7..a186b4fbf6c 100644 --- a/sql/udf_example.cc +++ b/sql/udf_example.cc @@ -619,10 +619,12 @@ my_bool sequence_init(UDF_INIT *initid, UDF_ARGS *args, char *message) return 1; } bzero(initid->ptr,sizeof(longlong)); - // Fool MySQL to think that this function is a constant - // This will ensure that MySQL only evalutes the function - // when the rows are sent to the client and not before any ORDER BY - // clauses + /* + Fool MySQL to think that this function is a constant + This will ensure that MySQL only evalutes the function + when the rows are sent to the client and not before any ORDER BY + clauses + */ initid->const_item=1; return 0; } @@ -639,9 +641,10 @@ longlong sequence(UDF_INIT *initid, UDF_ARGS *args, char *is_null, ulonglong val=0; if (args->arg_count) val= *((longlong*) args->args[0]); - return ++ *((longlong*) initid->ptr) + val; + return ++*((longlong*) initid->ptr) + val; } + /**************************************************************************** ** Some functions that handles IP and hostname conversions ** The orignal function was from Zeev Suraski. diff --git a/sql/unireg.cc b/sql/unireg.cc index dbd3da58a33..a16439530fc 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -423,6 +423,28 @@ static bool pack_header(uchar *forminfo, enum db_type table_type, if (field->interval) { uint old_int_count=int_count; + + if (field->charset->mbminlen > 1) + { + /* Escape UCS2 intervals using HEX notation */ + for (uint pos= 0; pos < field->interval->count; pos++) + { + char *dst; + uint length= field->interval->type_lengths[pos], hex_length; + const char *src= field->interval->type_names[pos]; + const char *srcend= src + length; + hex_length= length * 2; + field->interval->type_lengths[pos]= hex_length; + field->interval->type_names[pos]= dst= sql_alloc(hex_length + 1); + for ( ; src < srcend; src++) + { + *dst++= _dig_vec_upper[((uchar) *src) >> 4]; + *dst++= _dig_vec_upper[((uchar) *src) & 15]; + } + *dst= '\0'; + } + } + field->interval_id=get_interval_id(&int_count,create_fields,field); if (old_int_count != int_count) { |