diff options
author | unknown <pem@mysql.com> | 2004-05-26 17:04:45 +0200 |
---|---|---|
committer | unknown <pem@mysql.com> | 2004-05-26 17:04:45 +0200 |
commit | f8866c162b57fa102298dbfc9e49d4f72f16c1d7 (patch) | |
tree | e4e5bb1f8b9718ec195b357c050b3dc3254b447d /sql | |
parent | 556b51e12aa58c5a7894a6bd010c2cd59d6e0cc3 (diff) | |
parent | 3a675ff5f8ed659344d7540f811ca052d999407d (diff) | |
download | mariadb-git-f8866c162b57fa102298dbfc9e49d4f72f16c1d7.tar.gz |
Merging 4.1 to 5.0.
BitKeeper/etc/ignore:
auto-union
BitKeeper/etc/logging_ok:
auto-union
client/mysqltest.c:
Auto merged
configure.in:
Auto merged
include/my_sys.h:
Auto merged
include/mysql_com.h:
Auto merged
libmysql/libmysql.c:
Auto merged
libmysqld/Makefile.am:
Auto merged
libmysqld/lib_sql.cc:
Auto merged
myisam/mi_check.c:
Auto merged
myisam/myisamchk.c:
Auto merged
myisam/myisamdef.h:
Auto merged
mysql-test/r/func_group.result:
Auto merged
mysql-test/r/func_time.result:
Auto merged
mysql-test/r/null.result:
Auto merged
mysql-test/r/query_cache.result:
Auto merged
mysql-test/r/rpl_server_id2.result:
Auto merged
mysql-test/r/select.result:
Auto merged
mysql-test/r/subselect.result:
Auto merged
mysql-test/r/variables.result:
Auto merged
mysql-test/t/rpl000015.test:
Auto merged
mysql-test/t/rpl_error_ignored_table.test:
Auto merged
mysql-test/t/rpl_log.test:
Auto merged
mysql-test/t/rpl_log_pos.test:
Auto merged
mysql-test/t/rpl_max_relay_size.test:
Auto merged
mysql-test/t/rpl_relayrotate.test:
Auto merged
mysql-test/t/rpl_rotate_logs.test:
Auto merged
mysql-test/t/rpl_server_id2.test:
Auto merged
mysql-test/t/subselect.test:
Auto merged
mysql-test/t/variables.test:
Auto merged
mysys/my_pthread.c:
Auto merged
netware/BUILD/compile-netware-all:
Auto merged
netware/BUILD/compile-netware-standard:
Auto merged
netware/BUILD/mwenv:
Auto merged
netware/Makefile.am:
Auto merged
netware/my_manage.c:
Auto merged
netware/my_manage.h:
Auto merged
netware/mysql_test_run.c:
Auto merged
scripts/mysql_install_db.sh:
Auto merged
sql/Makefile.am:
Auto merged
sql/ha_berkeley.cc:
Auto merged
sql/ha_berkeley.h:
Auto merged
sql/ha_innodb.cc:
Auto merged
sql/ha_innodb.h:
Auto merged
sql/ha_myisam.cc:
Auto merged
sql/handler.cc:
Auto merged
sql/handler.h:
Auto merged
sql/item.cc:
Auto merged
sql/item.h:
Auto merged
sql/item_cmpfunc.cc:
Auto merged
sql/item_func.cc:
Auto merged
sql/item_subselect.cc:
Auto merged
sql/item_subselect.h:
Auto merged
sql/item_sum.cc:
Auto merged
sql/item_sum.h:
Auto merged
sql/item_timefunc.cc:
Auto merged
sql/lock.cc:
Auto merged
sql/mysql_priv.h:
Auto merged
sql/opt_range.cc:
Auto merged
sql/protocol.cc:
Auto merged
sql/protocol.h:
Auto merged
sql/repl_failsafe.cc:
Auto merged
sql/set_var.cc:
Auto merged
sql/sql_acl.cc:
Auto merged
sql/sql_base.cc:
Auto merged
sql/sql_cache.cc:
Auto merged
sql/sql_class.cc:
Auto merged
sql/sql_class.h:
Auto merged
sql/sql_db.cc:
Auto merged
sql/sql_insert.cc:
Auto merged
sql/sql_parse.cc:
Auto merged
sql/sql_prepare.cc:
Auto merged
sql/sql_select.h:
Auto merged
sql/sql_show.cc:
Auto merged
sql/sql_string.cc:
Auto merged
sql/sql_string.h:
Auto merged
sql/sql_table.cc:
Auto merged
sql/sql_union.cc:
Auto merged
sql/sql_yacc.yy:
Auto merged
tests/client_test.c:
Auto merged
Diffstat (limited to 'sql')
67 files changed, 2926 insertions, 1160 deletions
diff --git a/sql/Makefile.am b/sql/Makefile.am index 1d6d424a274..828456c4510 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -49,7 +49,7 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \ mysql_priv.h item_geofunc.h sql_bitmap.h \ procedure.h sql_class.h sql_lex.h sql_list.h \ sql_manager.h sql_map.h sql_string.h unireg.h \ - field.h handler.h \ + field.h handler.h mysqld_suffix.h \ ha_isammrg.h ha_isam.h ha_myisammrg.h\ ha_heap.h ha_myisam.h ha_berkeley.h ha_innodb.h \ ha_ndbcluster.h opt_range.h protocol.h \ @@ -88,7 +88,7 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc \ sql_udf.cc sql_analyse.cc sql_analyse.h sql_cache.cc \ slave.cc sql_repl.cc sql_union.cc sql_derived.cc \ client.c sql_client.cc mini_client_errors.c pack.c\ - stacktrace.c repl_failsafe.h repl_failsafe.cc sql_olap.cc\ + stacktrace.c repl_failsafe.h repl_failsafe.cc \ gstream.cc spatial.cc sql_help.cc protocol_cursor.cc \ examples/ha_example.cc \ sp_head.cc sp_pcontext.cc sp_rcontext.cc sp.cc \ diff --git a/sql/discover.cc b/sql/discover.cc index e260f44a8db..696be193148 100644 --- a/sql/discover.cc +++ b/sql/discover.cc @@ -159,7 +159,6 @@ int create_table_from_handler(const char *db, error = writefrm(path, frmblob, frmlen); } - err: if (frmblob) my_free((char*) frmblob,MYF(0)); DBUG_RETURN(error); diff --git a/sql/examples/ha_example.cc b/sql/examples/ha_example.cc index e2463761e67..2d17caf1a83 100644 --- a/sql/examples/ha_example.cc +++ b/sql/examples/ha_example.cc @@ -14,6 +14,55 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* + ha_example is a stubbed storage engine. It does nothing at this point. It + will let you create/open/delete tables but that is all. You can enable it + in your buld by doing the following during your build process: + ./configure --with-example-storage-engine + + Once this is done mysql will let you create tables with: + CREATE TABLE A (...) ENGINE=EXAMPLE; + + The example is setup to use table locks. It implements an example "SHARE" + that is inserted into a hash by table name. You can use this to store + information of state that any example handler object will be able to see + if it is using the same table. + + Please read the object definition in ha_example.h before reading the rest + if this file. + + To get an idea of what occurs here is an example select that would do a + scan of an entire table: + ha_example::store_lock + ha_example::external_lock + ha_example::info + ha_example::rnd_init + ha_example::extra + ENUM HA_EXTRA_CACHE Cash record in HA_rrnd() + ha_example::rnd_next + ha_example::rnd_next + ha_example::rnd_next + ha_example::rnd_next + ha_example::rnd_next + ha_example::rnd_next + ha_example::rnd_next + ha_example::rnd_next + ha_example::rnd_next + ha_example::extra + ENUM HA_EXTRA_NO_CACHE End cacheing of records (def) + ha_example::external_lock + ha_example::extra + ENUM HA_EXTRA_RESET Reset database to after open + + In the above example has 9 row called before rnd_next signalled that it was + at the end of its data. In the above example the table was already opened + (or you would have seen a call to ha_example::open(). Calls to + ha_example::extra() are hints as to what will be occuring to the request. + + Happy coding! + -Brian +*/ + #ifdef __GNUC__ #pragma implementation // gcc: Class implementation #endif @@ -24,10 +73,14 @@ #include "ha_example.h" /* Variables for example share methods */ -pthread_mutex_t example_mutex; -static HASH example_open_tables; -static int example_init= 0; +static HASH example_open_tables; // Hash used to track open tables +pthread_mutex_t example_mutex; // This is the mutex we use to init the hash +static int example_init= 0; // Variable for checking the init state of hash + +/* + Function we use in the creation of our hash to get key. +*/ static byte* example_get_key(EXAMPLE_SHARE *share,uint *length, my_bool not_used __attribute__((unused))) { @@ -37,7 +90,9 @@ static byte* example_get_key(EXAMPLE_SHARE *share,uint *length, /* - Example of simple lock controls. + Example of simple lock controls. The "share" it creates is structure we will + pass to each example handler. Do you have to have one of these? Well, you have + pieces that are used for locking, and they are needed to function. */ static EXAMPLE_SHARE *get_share(const char *table_name, TABLE *table) { @@ -45,6 +100,12 @@ static EXAMPLE_SHARE *get_share(const char *table_name, TABLE *table) uint length; char *tmp_name; + /* + So why does this exist? There is no way currently to init a storage engine. + Innodb and BDB both have modifications to the server to allow them to + do this. Since you will not want to do this, this is probably the next + best method. + */ if (!example_init) { /* Hijack a mutex for init'ing the storage engine */ @@ -101,7 +162,8 @@ error: /* - Free lock controls. + Free lock controls. We call this whenever we close a table. If the table had + the last reference to the share then we free memory associated with it. */ static int free_share(EXAMPLE_SHARE *share) { @@ -119,10 +181,24 @@ static int free_share(EXAMPLE_SHARE *share) } +/* + If frm_error() is called then we will use this to to find out what file extentions + exist for the storage engine. This is also used by the default rename_table and + delete_table method in handler.cc. +*/ const char **ha_example::bas_ext() const { static const char *ext[]= { NullS }; return ext; } +/* + Used for opening tables. The name will be the name of the file. + A table is opened when it needs to be opened. For instance + when a request comes in for a select on the table (tables are not + open and closed for each request, they are cached). + + Called from handler.cc by handler::ha_open(). The server opens all tables by + calling ha_open() which then calls the handler specific open(). +*/ int ha_example::open(const char *name, int mode, uint test_if_locked) { DBUG_ENTER("ha_example::open"); @@ -134,18 +210,66 @@ int ha_example::open(const char *name, int mode, uint test_if_locked) DBUG_RETURN(0); } + +/* + Closes a table. We call the free_share() function to free any resources + that we have allocated in the "shared" structure. + + Called from sql_base.cc, sql_select.cc, and table.cc. + In sql_select.cc it is only used to close up temporary tables or during + the process where a temporary table is converted over to being a + myisam table. + For sql_base.cc look at close_data_tables(). +*/ int ha_example::close(void) { DBUG_ENTER("ha_example::close"); DBUG_RETURN(free_share(share)); } + +/* + write_row() inserts a row. No extra() hint is given currently if a bulk load + is happeneding. buf() is a byte array of data. You can use the field + information to extract the data from the native byte array type. + Example of this would be: + for (Field **field=table->field ; *field ; field++) + { + ... + } + + See ha_tina.cc for an example of extracting all of the data as strings. + ha_berekly.cc has an example of how to store it intact by "packing" it + for ha_berkeley's own native storage type. + + See the note for update_row() on auto_increments and timestamps. This + case also applied to write_row(). + + Called from item_sum.cc, item_sum.cc, sql_acl.cc, sql_insert.cc, + sql_insert.cc, sql_select.cc, sql_table.cc, sql_udf.cc, and sql_update.cc. +*/ int ha_example::write_row(byte * buf) { DBUG_ENTER("ha_example::write_row"); DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED); } + +/* + Yes, update_row() does what you expect, it updates a row. old_data will have + the previous row record in it, while new_data will have the newest data in + it. + Keep in mind that the server can do updates based on ordering if an ORDER BY + clause was used. Consecutive ordering is not guarenteed. + Currently new_data will not have an updated auto_increament record, or + and updated timestamp field. You can do these for example by doing these: + if (table->timestamp_on_update_now) + update_timestamp(new_row+table->timestamp_on_update_now-1); + if (table->next_number_field && record == table->record[0]) + update_auto_increment(); + + Called from sql_select.cc, sql_acl.cc, sql_update.cc, and sql_insert.cc. +*/ int ha_example::update_row(const byte * old_data, byte * new_data) { @@ -153,12 +277,32 @@ int ha_example::update_row(const byte * old_data, byte * new_data) DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED); } + +/* + This will delete a row. buf will contain a copy of the row to be deleted. + The server will call this right after the current row has been called (from + either a previous rnd_nexT() or index call). + If you keep a pointer to the last row or can access a primary key it will + make doing the deletion quite a bit easier. + Keep in mind that the server does no guarentee consecutive deletions. ORDER BY + clauses can be used. + + Called in sql_acl.cc and sql_udf.cc to manage internal table information. + Called in sql_delete.cc, sql_insert.cc, and sql_select.cc. In sql_select it is + used for removing duplicates while in insert it is used for REPLACE calls. +*/ int ha_example::delete_row(const byte * buf) { DBUG_ENTER("ha_example::delete_row"); DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED); } + +/* + Positions an index cursor to the index specified in the handle. Fetches the + row if available. If the key value is null, begin at the first key of the + index. +*/ int ha_example::index_read(byte * buf, const byte * key, uint key_len __attribute__((unused)), enum ha_rkey_function find_flag @@ -168,6 +312,11 @@ int ha_example::index_read(byte * buf, const byte * key, DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED); } + +/* + Positions an index cursor to the index specified in key. Fetches the + row if any. This is only used to read whole keys. +*/ int ha_example::index_read_idx(byte * buf, uint index, const byte * key, uint key_len __attribute__((unused)), enum ha_rkey_function find_flag @@ -178,66 +327,187 @@ int ha_example::index_read_idx(byte * buf, uint index, const byte * key, } +/* + Used to read forward through the index. +*/ int ha_example::index_next(byte * buf) { DBUG_ENTER("ha_example::index_next"); DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED); } + +/* + Used to read backwards through the index. +*/ int ha_example::index_prev(byte * buf) { DBUG_ENTER("ha_example::index_prev"); DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED); } + +/* + index_first() asks for the first key in the index. + + Called from opt_range.cc, opt_sum.cc, sql_handler.cc, + and sql_select.cc. +*/ int ha_example::index_first(byte * buf) { DBUG_ENTER("ha_example::index_first"); DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED); } + +/* + index_last() asks for the last key in the index. + + Called from opt_range.cc, opt_sum.cc, sql_handler.cc, + and sql_select.cc. +*/ int ha_example::index_last(byte * buf) { DBUG_ENTER("ha_example::index_last"); DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED); } + +/* + rnd_init() is called when the system wants the storage engine to do a table + scan. + See the example in the introduction at the top of this file to see when + rnd_init() is called. + + Called from filesort.cc, records.cc, sql_handler.cc, sql_select.cc, sql_table.cc, + and sql_update.cc. +*/ int ha_example::rnd_init(bool scan) { DBUG_ENTER("ha_example::rnd_init"); DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED); } + +/* + This is called for each row of the table scan. When you run out of records + you should return HA_ERR_END_OF_FILE. Fill buff up with the row information. + The Field structure for the table is the key to getting data into buf + in a manner that will allow the server to understand it. + + Called from filesort.cc, records.cc, sql_handler.cc, sql_select.cc, sql_table.cc, + and sql_update.cc. +*/ int ha_example::rnd_next(byte *buf) { DBUG_ENTER("ha_example::rnd_next"); DBUG_RETURN(HA_ERR_END_OF_FILE); } + +/* + position() is called after each call to rnd_next() if the data needs + to be ordered. You can do something like the following to store + the position: + ha_store_ptr(ref, ref_length, current_position); + + The server uses ref to store data. ref_length in the above case is + the size needed to store current_position. ref is just a byte array + that the server will maintain. If you are using offsets to mark rows, then + current_position should be the offset. If it is a primary key like in + BDB, then it needs to be a primary key. + + Called from filesort.cc, sql_select.cc, sql_delete.cc and sql_update.cc. +*/ void ha_example::position(const byte *record) { DBUG_ENTER("ha_example::position"); DBUG_VOID_RETURN; } + +/* + This is like rnd_next, but you are given a position to use + to determine the row. The position will be of the type that you stored in + ref. You can use ha_get_ptr(pos,ref_length) to retrieve whatever key + or position you saved when position() was called. + Called from filesort.cc records.cc sql_insert.cc sql_select.cc sql_update.cc. +*/ int ha_example::rnd_pos(byte * buf, byte *pos) { DBUG_ENTER("ha_example::rnd_pos"); DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED); } + +/* + ::info() is used to return information to the optimizer. + Currently this table handler doesn't implement most of the fields + really needed. SHOW also makes use of this data + Another note, you will probably want to have the following in your + code: + if (records < 2) + records = 2; + The reason is that the server will optimize for cases of only a single + record. If in a table scan you don't know the number of records + it will probably be better to set records to two so you can return + as many records as you need. + Along with records a few more variables you may wish to set are: + records + deleted + data_file_length + index_file_length + delete_length + check_time + Take a look at the public variables in handler.h for more information. + + Called in: + filesort.cc + ha_heap.cc + item_sum.cc + opt_sum.cc + sql_delete.cc + sql_delete.cc + sql_derived.cc + sql_select.cc + sql_select.cc + sql_select.cc + sql_select.cc + sql_select.cc + sql_show.cc + sql_show.cc + sql_show.cc + sql_show.cc + sql_table.cc + sql_union.cc + sql_update.cc + +*/ void ha_example::info(uint flag) { DBUG_ENTER("ha_example::info"); DBUG_VOID_RETURN; } + +/* + extra() is called whenever the server wishes to send a hint to + the storage engine. The myisam engine implements the most hints. + ha_innodb.cc has the most exhaustive list of these hints. +*/ int ha_example::extra(enum ha_extra_function operation) { DBUG_ENTER("ha_example::extra"); DBUG_RETURN(0); } + +/* + Deprecated and likely to be removed in the future. Storage engines normally + just make a call like: + ha_example::extra(HA_EXTRA_RESET); + to handle it. +*/ int ha_example::reset(void) { DBUG_ENTER("ha_example::reset"); @@ -245,18 +515,71 @@ int ha_example::reset(void) } +/* + Used to delete all rows in a table. Both for cases of truncate and + for cases where the optimizer realizes that all rows will be + removed as a result of a SQL statement. + + Called from item_sum.cc by Item_func_group_concat::clear(), + Item_sum_count_distinct::clear(), and Item_func_group_concat::clear(). + Called from sql_delete.cc by mysql_delete(). + Called from sql_select.cc by JOIN::reinit(). + Called from sql_union.cc by st_select_lex_unit::exec(). +*/ int ha_example::delete_all_rows() { DBUG_ENTER("ha_example::delete_all_rows"); DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED); } + +/* + First you should go read the section "locking functions for mysql" in + lock.cc to understand this. + This create a lock on the table. If you are implementing a storage engine + that can handle transacations look at ha_berkely.cc to see how you will + want to goo about doing this. Otherwise you should consider calling flock() + here. + + Called from lock.cc by lock_external() and unlock_external(). Also called + from sql_table.cc by copy_data_between_tables(). +*/ int ha_example::external_lock(THD *thd, int lock_type) { DBUG_ENTER("ha_example::external_lock"); DBUG_RETURN(0); } + +/* + The idea with handler::store_lock() is the following: + + The statement decided which locks we should need for the table + for updates/deletes/inserts we get WRITE locks, for SELECT... we get + read locks. + + Before adding the lock into the table lock handler (see thr_lock.c) + mysqld calls store lock with the requested locks. Store lock can now + modify a write lock to a read lock (or some other lock), ignore the + lock (if we don't want to use MySQL table locks at all) or add locks + for many tables (like we do when we are using a MERGE handler). + + Berkeley DB for example changes all WRITE locks to TL_WRITE_ALLOW_WRITE + (which signals that we are doing WRITES, but we are still allowing other + reader's and writer's. + + When releasing locks, store_lock() are also called. In this case one + usually doesn't have to do anything. + + In some exceptional cases MySQL may send a request for a TL_IGNORE; + This means that we are requesting the same lock as last time and this + should also be ignored. (This may happen when someone does a flush + table when we have opened a part of the tables, in which case mysqld + closes and reopens the tables and tries to get the same locks at last + time). In the future we will probably try to remove this. + + Called from lock.cc by get_lock_data(). +*/ THR_LOCK_DATA **ha_example::store_lock(THD *thd, THR_LOCK_DATA **to, enum thr_lock_type lock_type) @@ -267,6 +590,16 @@ THR_LOCK_DATA **ha_example::store_lock(THD *thd, return to; } +/* + Used to delete a table. By the time delete_table() has been called all + opened references to this table will have been closed (and your globally + shared references released. The variable name will just be the name of + the table. You will need to remove any files you have created at this point. + + Called from handler.cc by delete_table and ha_create_table(). Only used + during create if the table_flag HA_DROP_BEFORE_CREATE was specified for + the storage engine. +*/ int ha_example::delete_table(const char *name) { DBUG_ENTER("ha_example::delete_table"); @@ -274,24 +607,44 @@ int ha_example::delete_table(const char *name) DBUG_RETURN(0); } +/* + Renames a table from one name to another from alter table call. + + Called from sql_table.cc by mysql_rename_table(). +*/ int ha_example::rename_table(const char * from, const char * to) { DBUG_ENTER("ha_example::rename_table "); DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED); } -ha_rows ha_example::records_in_range(int inx, - const byte *start_key,uint start_key_len, - enum ha_rkey_function start_search_flag, - const byte *end_key,uint end_key_len, - enum ha_rkey_function end_search_flag) +/* + Given a starting key, and an ending key estimate the number of rows that + will exist between the two. end_key may be empty which in case determine + if start_key matches any rows. + + Called from opt_range.cc by check_quick_keys(). +*/ +ha_rows ha_example::records_in_range(uint inx, key_range *min_key, + key_range *max_key) { - DBUG_ENTER("ha_example::records_in_range "); - DBUG_RETURN(records); // HA_ERR_NOT_IMPLEMENTED + DBUG_ENTER("ha_example::records_in_range"); + DBUG_RETURN(10); // low number to force index usage } -int ha_example::create(const char *name, TABLE *table_arg, HA_CREATE_INFO *create_info) +/* + create() is called to create a database. The variable name will have the name + of the table. When create() is called you do not need to worry about opening + the table. Also, the FRM file will have already been created so adjusting + create_info will not do you any good. You can overwrite the frm file at this + point if you wish to change the table definition, but there are no methods + currently provided for doing that. + + Called from handle.cc by ha_create_table(). +*/ +int ha_example::create(const char *name, TABLE *table_arg, + HA_CREATE_INFO *create_info) { DBUG_ENTER("ha_example::create"); /* This is not implemented but we want someone to be able that it works. */ diff --git a/sql/examples/ha_example.h b/sql/examples/ha_example.h index 466632a1795..2228f04284a 100644 --- a/sql/examples/ha_example.h +++ b/sql/examples/ha_example.h @@ -14,6 +14,17 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* + Please read ha_exmple.cc before reading this file. + Please keep in mind that the example storage engine implements all methods + that are required to be implemented. handler.h has a full list of methods + that you can implement. +*/ + +/* + EXAMPLE_SHARE is a structure that will be shared amoung all open handlers + The example implements the minimum of what you will probably need. +*/ typedef struct st_example_share { char *table_name; uint table_name_length,use_count; @@ -21,6 +32,9 @@ typedef struct st_example_share { THR_LOCK lock; } EXAMPLE_SHARE; +/* + Class definition for the storage engine +*/ class ha_example: public handler { THR_LOCK_DATA lock; /* MySQL lock */ @@ -33,17 +47,34 @@ public: ~ha_example() { } - const char *table_type() const { return "EXAMPLE"; } + /* The name that will be used for display purposes */ + const char *table_type() const { return "EXAMPLE"; } + /* The name of the index type that will be used for display */ const char *index_type(uint inx) { return "NONE"; } const char **bas_ext() const; + /* + This is a list of flags that says what the storage engine + implements. The current table flags are documented in + table_flags. + */ ulong table_flags() const { return 0; } + /* + This is a list of flags that says how the storage engine + implements indexes. The current index flags are documented in + handler.h. If you do not implement indexes, just return zero + here. + */ ulong index_flags(uint inx) const { return 0; } + /* + unireg.cc will call the following to make sure that the storage engine can + handle the data it is about to send. + */ uint max_record_length() const { return HA_MAX_REC_LENGTH; } uint max_keys() const { return 0; } uint max_key_parts() const { return 0; } @@ -52,10 +83,15 @@ public: Called in test_quick_select to determine if indexes should be used. */ virtual double scan_time() { return (double) (records+deleted) / 20.0+10; } - /* The next method will never be called */ + /* + The next method will never be called if you do not implement indexes. + */ virtual double read_time(ha_rows rows) { return (double) rows / 20.0+1; } virtual bool fast_key_read() { return 1;} + /* + Everything below are methods that we implment in ha_example.cc. + */ int open(const char *name, int mode, uint test_if_locked); int close(void); int write_row(byte * buf); @@ -78,10 +114,8 @@ public: int reset(void); int external_lock(THD *thd, int lock_type); int delete_all_rows(void); - ha_rows records_in_range(int inx, const byte *start_key,uint start_key_len, - enum ha_rkey_function start_search_flag, - const byte *end_key,uint end_key_len, - enum ha_rkey_function end_search_flag); + ha_rows records_in_range(uint inx, key_range *min_key, + key_range *max_key); int delete_table(const char *from); int rename_table(const char * from, const char * to); int create(const char *name, TABLE *form, HA_CREATE_INFO *create_info); diff --git a/sql/field.cc b/sql/field.cc index fdf314972c8..df9b4f84ae7 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -423,30 +423,11 @@ bool Field::get_time(TIME *ltime) void Field::store_time(TIME *ltime,timestamp_type type) { - char buff[25]; - switch (type) { - case TIMESTAMP_NONE: - case TIMESTAMP_DATETIME_ERROR: - store("",0,&my_charset_bin); // Probably an error - break; - case TIMESTAMP_DATE: - sprintf(buff,"%04d-%02d-%02d", ltime->year,ltime->month,ltime->day); - store(buff,10,&my_charset_bin); - break; - case TIMESTAMP_DATETIME: - sprintf(buff,"%04d-%02d-%02d %02d:%02d:%02d", - ltime->year,ltime->month,ltime->day, - ltime->hour,ltime->minute,ltime->second); - store(buff,19,&my_charset_bin); - break; - case TIMESTAMP_TIME: - { - ulong length= my_sprintf(buff, (buff, "%02d:%02d:%02d", - ltime->hour,ltime->minute,ltime->second)); - store(buff,(uint) length, &my_charset_bin); - break; - } - } + char buff[MAX_DATE_REP_LENGTH]; + String tmp; + tmp.set(buff, sizeof(buff), &my_charset_bin); + TIME_to_string(ltime, &tmp); + store(buff, tmp.length(), &my_charset_bin); } @@ -2307,7 +2288,8 @@ int Field_float::store(double nr) } else { - max_value= (log_10[field_length]-1)/log_10[dec]; + uint tmp=min(field_length,array_elements(log_10)-1); + max_value= (log_10[tmp]-1)/log_10[dec]; /* The following comparison is needed to not get an overflow if nr is close to FLT_MAX @@ -2607,7 +2589,8 @@ int Field_double::store(double nr) } else { - max_value= (log_10[field_length]-1)/log_10[dec]; + uint tmp=min(field_length,array_elements(log_10)-1); + max_value= (log_10[tmp]-1)/log_10[dec]; if (fabs(nr) < DBL_MAX/10.0e+32) nr= floor(nr*log_10[dec]+0.5)/log_10[dec]; } @@ -3928,6 +3911,11 @@ void Field_newdate::sql_type(String &res) const int Field_datetime::store(const char *from,uint len,CHARSET_INFO *cs) { longlong tmp=str_to_datetime(from,len,1); + if (tmp < 0) + { + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + tmp= 0; + } #ifdef WORDS_BIGENDIAN if (table->db_low_byte_first) { @@ -4250,12 +4238,13 @@ int Field_string::cmp(const char *a_ptr, const char *b_ptr) like in latin_de 'ae' and 0xe4 */ return field_charset->coll->strnncollsp(field_charset, - (const uchar*) a_ptr, field_length, - (const uchar*) b_ptr, field_length); + (const uchar*) a_ptr, field_length, + (const uchar*) b_ptr, + field_length); } return field_charset->coll->strnncoll(field_charset, - (const uchar*) a_ptr, field_length, - (const uchar*) b_ptr, field_length); + (const uchar*) a_ptr, field_length, + (const uchar*) b_ptr, field_length); } void Field_string::sort_string(char *to,uint length) diff --git a/sql/ha_berkeley.cc b/sql/ha_berkeley.cc index df5a45480c6..93ca5b318fa 100644 --- a/sql/ha_berkeley.cc +++ b/sql/ha_berkeley.cc @@ -1542,7 +1542,7 @@ int ha_berkeley::index_next_same(byte * buf, const byte *key, uint keylen) { error=read_row(cursor->c_get(cursor, &last_key, &row, DB_NEXT), (char*) buf, active_index, &row, &last_key, 1); - if (!error && ::key_cmp(table, key, active_index, keylen)) + if (!error && ::key_cmp_if_same(table, key, active_index, keylen)) error=HA_ERR_END_OF_FILE; } DBUG_RETURN(error); @@ -1987,11 +1987,8 @@ double ha_berkeley::scan_time() return rows2double(records/3); } -ha_rows ha_berkeley::records_in_range(int keynr, - const byte *start_key,uint start_key_len, - enum ha_rkey_function start_search_flag, - const byte *end_key,uint end_key_len, - enum ha_rkey_function end_search_flag) +ha_rows ha_berkeley::records_in_range(uint keynr, key_range *start_key, + key_range *end_key) { DBT key; DB_KEY_RANGE start_range, end_range; @@ -2000,25 +1997,27 @@ ha_rows ha_berkeley::records_in_range(int keynr, DBUG_ENTER("records_in_range"); if ((start_key && kfile->key_range(kfile,transaction, - pack_key(&key, keynr, key_buff, start_key, - start_key_len), - &start_range,0)) || + pack_key(&key, keynr, key_buff, + start_key->key, + start_key->length), + &start_range,0)) || (end_key && kfile->key_range(kfile,transaction, - pack_key(&key, keynr, key_buff, end_key, - end_key_len), + pack_key(&key, keynr, key_buff, + end_key->key, + end_key->length), &end_range,0))) DBUG_RETURN(HA_BERKELEY_RANGE_COUNT); // Better than returning an error /* purecov: inspected */ if (!start_key) - start_pos=0.0; - else if (start_search_flag == HA_READ_KEY_EXACT) + start_pos= 0.0; + else if (start_key->flag == HA_READ_KEY_EXACT) start_pos=start_range.less; else start_pos=start_range.less+start_range.equal; if (!end_key) - end_pos=1.0; - else if (end_search_flag == HA_READ_BEFORE_KEY) + end_pos= 1.0; + else if (end_key->flag == HA_READ_BEFORE_KEY) end_pos=end_range.less; else end_pos=end_range.less+end_range.equal; diff --git a/sql/ha_berkeley.h b/sql/ha_berkeley.h index 525c82febd0..249f5ef2370 100644 --- a/sql/ha_berkeley.h +++ b/sql/ha_berkeley.h @@ -143,12 +143,7 @@ class ha_berkeley: public handler int optimize(THD* thd, HA_CHECK_OPT* check_opt); int check(THD* thd, HA_CHECK_OPT* check_opt); - ha_rows records_in_range(int inx, - const byte *start_key,uint start_key_len, - enum ha_rkey_function start_search_flag, - const byte *end_key,uint end_key_len, - enum ha_rkey_function end_search_flag); - + ha_rows records_in_range(uint inx, key_range *min_key, key_range *max_key); int create(const char *name, register TABLE *form, HA_CREATE_INFO *create_info); int delete_table(const char *name); diff --git a/sql/ha_heap.cc b/sql/ha_heap.cc index 47978d647ec..c375614ac95 100644 --- a/sql/ha_heap.cc +++ b/sql/ha_heap.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000,2004 MySQL AB & MySQL Finland AB & TCX DataKonsult AB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -47,12 +47,7 @@ int ha_heap::open(const char *name, int mode, uint test_if_locked) if (file) { /* Initialize variables for the opened table */ - btree_keys.clear_all(); - for (uint i= 0 ; i < table->keys ; i++) - { - if (table->key_info[i].algorithm == HA_KEY_ALG_BTREE) - btree_keys.set_bit(i); - } + set_keys_for_scanning(); } return (file ? 0 : 1); } @@ -62,6 +57,33 @@ int ha_heap::close(void) return heap_close(file); } + +/* + Compute which keys to use for scanning + + SYNOPSIS + set_keys_for_scanning() + no parameter + + DESCRIPTION + Set the bitmap btree_keys, which is used when the upper layers ask + which keys to use for scanning. For each btree index the + corresponding bit is set. + + RETURN + void +*/ + +void ha_heap::set_keys_for_scanning(void) +{ + btree_keys.clear_all(); + for (uint i= 0 ; i < table->keys ; i++) + { + if (table->key_info[i].algorithm == HA_KEY_ALG_BTREE) + btree_keys.set_bit(i); + } +} + int ha_heap::write_row(byte * buf) { statistic_increment(ha_write_count,&LOCK_status); @@ -207,6 +229,114 @@ int ha_heap::external_lock(THD *thd, int lock_type) return 0; // No external locking } + +/* + Disable indexes. + + SYNOPSIS + disable_indexes() + mode mode of operation: + HA_KEY_SWITCH_NONUNIQ disable all non-unique keys + HA_KEY_SWITCH_ALL disable all keys + HA_KEY_SWITCH_NONUNIQ_SAVE dis. non-uni. and make persistent + HA_KEY_SWITCH_ALL_SAVE dis. all keys and make persistent + + DESCRIPTION + Disable indexes and clear keys to use for scanning. + + IMPLEMENTATION + HA_KEY_SWITCH_NONUNIQ is not implemented. + HA_KEY_SWITCH_NONUNIQ_SAVE is not implemented with HEAP. + HA_KEY_SWITCH_ALL_SAVE is not implemented with HEAP. + + RETURN + 0 ok + HA_ERR_WRONG_COMMAND mode not implemented. +*/ + +int ha_heap::disable_indexes(uint mode) +{ + int error; + + if (mode == HA_KEY_SWITCH_ALL) + { + if (!(error= heap_disable_indexes(file))) + set_keys_for_scanning(); + } + else + { + /* mode not implemented */ + error= HA_ERR_WRONG_COMMAND; + } + return error; +} + + +/* + Enable indexes. + + SYNOPSIS + enable_indexes() + mode mode of operation: + HA_KEY_SWITCH_NONUNIQ enable all non-unique keys + HA_KEY_SWITCH_ALL enable all keys + HA_KEY_SWITCH_NONUNIQ_SAVE en. non-uni. and make persistent + HA_KEY_SWITCH_ALL_SAVE en. all keys and make persistent + + DESCRIPTION + Enable indexes and set keys to use for scanning. + The indexes might have been disabled by disable_index() before. + The function works only if both data and indexes are empty, + since the heap storage engine cannot repair the indexes. + To be sure, call handler::delete_all_rows() before. + + IMPLEMENTATION + HA_KEY_SWITCH_NONUNIQ is not implemented. + HA_KEY_SWITCH_NONUNIQ_SAVE is not implemented with HEAP. + HA_KEY_SWITCH_ALL_SAVE is not implemented with HEAP. + + RETURN + 0 ok + HA_ERR_CRASHED data or index is non-empty. Delete all rows and retry. + HA_ERR_WRONG_COMMAND mode not implemented. +*/ + +int ha_heap::enable_indexes(uint mode) +{ + int error; + + if (mode == HA_KEY_SWITCH_ALL) + { + if (!(error= heap_enable_indexes(file))) + set_keys_for_scanning(); + } + else + { + /* mode not implemented */ + error= HA_ERR_WRONG_COMMAND; + } + return error; +} + + +/* + Test if indexes are disabled. + + SYNOPSIS + indexes_are_disabled() + no parameters + + RETURN + 0 indexes are not disabled + 1 all indexes are disabled + [2 non-unique indexes are disabled - NOT YET IMPLEMENTED] +*/ + +int ha_heap::indexes_are_disabled(void) +{ + return heap_indexes_are_disabled(file); +} + THR_LOCK_DATA **ha_heap::store_lock(THD *thd, THR_LOCK_DATA **to, enum thr_lock_type lock_type) @@ -233,28 +363,20 @@ int ha_heap::rename_table(const char * from, const char * to) return heap_rename(from,to); } -ha_rows ha_heap::records_in_range(int inx, - const byte *start_key,uint start_key_len, - enum ha_rkey_function start_search_flag, - const byte *end_key,uint end_key_len, - enum ha_rkey_function end_search_flag) + +ha_rows ha_heap::records_in_range(uint inx, key_range *min_key, + key_range *max_key) { - KEY *pos=table->key_info+inx; - if (pos->algorithm == HA_KEY_ALG_BTREE) - { - return hp_rb_records_in_range(file, inx, start_key, start_key_len, - start_search_flag, end_key, end_key_len, - end_search_flag); - } - else - { - if (start_key_len != end_key_len || - start_key_len != pos->key_length || - start_search_flag != HA_READ_KEY_EXACT || - end_search_flag != HA_READ_AFTER_KEY) - return HA_POS_ERROR; // Can't only use exact keys - return 10; // Good guess - } + KEY *key=table->key_info+inx; + if (key->algorithm == HA_KEY_ALG_BTREE) + return hp_rb_records_in_range(file, inx, min_key, max_key); + + if (min_key->length != max_key->length || + min_key->length != key->key_length || + min_key->flag != HA_READ_KEY_EXACT || + max_key->flag != HA_READ_AFTER_KEY) + return HA_POS_ERROR; // Can only use exact keys + return 10; // Good guess } diff --git a/sql/ha_heap.h b/sql/ha_heap.h index 68406202c76..f55eda91149 100644 --- a/sql/ha_heap.h +++ b/sql/ha_heap.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000,2004 MySQL AB & MySQL Finland AB & TCX DataKonsult AB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -61,6 +61,7 @@ class ha_heap: public handler int open(const char *name, int mode, uint test_if_locked); int close(void); + void set_keys_for_scanning(void); int write_row(byte * buf); int update_row(const byte * old_data, byte * new_data); int delete_row(const byte * buf); @@ -82,10 +83,10 @@ class ha_heap: public handler int extra(enum ha_extra_function operation); int external_lock(THD *thd, int lock_type); int delete_all_rows(void); - ha_rows records_in_range(int inx, const byte *start_key,uint start_key_len, - enum ha_rkey_function start_search_flag, - const byte *end_key,uint end_key_len, - enum ha_rkey_function end_search_flag); + int disable_indexes(uint mode); + int enable_indexes(uint mode); + int indexes_are_disabled(void); + ha_rows records_in_range(uint inx, key_range *min_key, key_range *max_key); int delete_table(const char *from); int rename_table(const char * from, const char * to); int create(const char *name, TABLE *form, HA_CREATE_INFO *create_info); diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index 4c4c32691e5..601536ab27e 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -325,6 +325,35 @@ convert_error_code_to_mysql( } /***************************************************************** +If you want to print a thd that is not associated with the current thread, +you must call this function before reserving the InnoDB kernel_mutex, to +protect MySQL from setting thd->query NULL. If you print a thd of the current +thread, we know that MySQL cannot modify thd->query, and it is not necessary +to call this. Call innobase_mysql_end_print_arbitrary_thd() after you release +the kernel_mutex. +NOTE that /mysql/innobase/lock/lock0lock.c must contain the prototype for this +function! */ +extern "C" +void +innobase_mysql_prepare_print_arbitrary_thd(void) +/*============================================*/ +{ + VOID(pthread_mutex_lock(&LOCK_thread_count)); +} + +/***************************************************************** +Relases the mutex reserved by innobase_mysql_prepare_print_arbitrary_thd(). +NOTE that /mysql/innobase/lock/lock0lock.c must contain the prototype for this +function! */ +extern "C" +void +innobase_mysql_end_print_arbitrary_thd(void) +/*========================================*/ +{ + VOID(pthread_mutex_unlock(&LOCK_thread_count)); +} + +/***************************************************************** Prints info of a THD object (== user session thread) to the standard output. NOTE that /mysql/innobase/trx/trx0trx.c must contain the prototype for this function! */ @@ -335,9 +364,11 @@ innobase_mysql_print_thd( FILE* f, /* in: output stream */ void* input_thd)/* in: pointer to a MySQL THD object */ { - THD* thd; + const THD* thd; + const char* s; + char buf[301]; - thd = (THD*) input_thd; + thd = (const THD*) input_thd; fprintf(f, "MySQL thread id %lu, query id %lu", thd->thread_id, thd->query_id); @@ -356,14 +387,31 @@ innobase_mysql_print_thd( fputs(thd->user, f); } - if (thd->proc_info) { + if ((s = thd->proc_info)) { putc(' ', f); - fputs(thd->proc_info, f); + fputs(s, f); } - if (thd->query) { - putc(' ', f); - fputs(thd->query, f); + if ((s = thd->query)) { + /* determine the length of the query string */ + uint32 i, len; + + len = thd->query_length; + + if (len > 300) { + len = 300; /* ADDITIONAL SAFETY: print at most + 300 chars to reduce the probability of + a seg fault if there is a race in + thd->query_length in MySQL; after + May 14, 2004 probably no race any more, + but better be safe */ + } + + /* Use strmake to reduce the timeframe + for a race, compared to fwrite() */ + i= (uint) (strmake(buf, s, len) - buf); + putc('\n', f); + fwrite(buf, 1, i, f); } putc('\n', f); @@ -3928,18 +3976,11 @@ ha_innobase::records_in_range( /*==========================*/ /* out: estimated number of rows */ - int keynr, /* in: index number */ - const mysql_byte* start_key, /* in: start key value of the - range, may also be empty */ - uint start_key_len, /* in: start key val len, may - also be 0 */ - enum ha_rkey_function start_search_flag,/* in: start search condition - e.g., 'greater than' */ - const mysql_byte* end_key, /* in: range end key val, may - also be empty */ - uint end_key_len, /* in: range end key val len, - may also be 0 */ - enum ha_rkey_function end_search_flag)/* in: range end search cond */ + uint keynr, /* in: index number */ + key_range *min_key, /* in: start key value of the + range, may also be 0 */ + key_range *max_key) /* in: range end key val, may + also be 0 */ { row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; KEY* key; @@ -3960,12 +4001,6 @@ ha_innobase::records_in_range( DBUG_ENTER("records_in_range"); - /* We do not know if MySQL can call this function before calling - external_lock(). To be safe, update the thd of the current table - handle. */ - - update_thd(current_thd); - prebuilt->trx->op_info = (char*)"estimating records in index range"; /* In case MySQL calls this in the middle of a SELECT query, release @@ -3989,17 +4024,21 @@ ha_innobase::records_in_range( range_start, (byte*) key_val_buff, (ulint)upd_and_key_val_buff_len, index, - (byte*) start_key, - (ulint) start_key_len); + (byte*) (min_key ? min_key->key : + (const mysql_byte*) 0), + (ulint) (min_key ? min_key->length : 0)); row_sel_convert_mysql_key_to_innobase( range_end, (byte*) key_val_buff2, buff2_len, index, - (byte*) end_key, - (ulint) end_key_len); - - mode1 = convert_search_mode_to_innobase(start_search_flag); - mode2 = convert_search_mode_to_innobase(end_search_flag); + (byte*) (max_key ? max_key->key : + (const mysql_byte*) 0), + (ulint) (max_key ? max_key->length : 0)); + + mode1 = convert_search_mode_to_innobase(min_key ? min_key->flag : + HA_READ_KEY_EXACT); + mode2 = convert_search_mode_to_innobase(max_key ? max_key->flag : + HA_READ_KEY_EXACT); n_rows = btr_estimate_n_rows_in_range(index, range_start, mode1, range_end, mode2); diff --git a/sql/ha_innodb.h b/sql/ha_innodb.h index 62f5e85b5d4..a33f8974049 100644 --- a/sql/ha_innodb.h +++ b/sql/ha_innodb.h @@ -166,11 +166,7 @@ class ha_innobase: public handler int start_stmt(THD *thd); void position(byte *record); - ha_rows records_in_range(int inx, - const byte *start_key,uint start_key_len, - enum ha_rkey_function start_search_flag, - const byte *end_key,uint end_key_len, - enum ha_rkey_function end_search_flag); + ha_rows records_in_range(uint inx, key_range *min_key, key_range *max_key); ha_rows estimate_number_of_rows(); int create(const char *name, register TABLE *form, diff --git a/sql/ha_isam.cc b/sql/ha_isam.cc index 09c2aeceafc..85ab25a31d9 100644 --- a/sql/ha_isam.cc +++ b/sql/ha_isam.cc @@ -382,18 +382,21 @@ int ha_isam::create(const char *name, register TABLE *form, } +static key_range no_range= { (byte*) 0, 0, HA_READ_KEY_EXACT }; -ha_rows ha_isam::records_in_range(int inx, - const byte *start_key,uint start_key_len, - enum ha_rkey_function start_search_flag, - const byte *end_key,uint end_key_len, - enum ha_rkey_function end_search_flag) +ha_rows ha_isam::records_in_range(uint inx, key_range *min_key, + key_range *max_key) { + /* ISAM checks if 'key' pointer <> 0 to know if there is no range */ + if (!min_key) + min_key= &no_range; + if (!max_key) + max_key= &no_range; return (ha_rows) nisam_records_in_range(file, - inx, - start_key,start_key_len, - start_search_flag, - end_key,end_key_len, - end_search_flag); + (int) inx, + min_key->key, min_key->length, + min_key->flag, + max_key->key, max_key->length, + max_key->flag); } #endif /* HAVE_ISAM */ diff --git a/sql/ha_isam.h b/sql/ha_isam.h index 2c8ec274145..8a887ababde 100644 --- a/sql/ha_isam.h +++ b/sql/ha_isam.h @@ -70,11 +70,7 @@ class ha_isam: public handler void info(uint); int extra(enum ha_extra_function operation); int external_lock(THD *thd, int lock_type); - ha_rows records_in_range(int inx, - const byte *start_key,uint start_key_len, - enum ha_rkey_function start_search_flag, - const byte *end_key,uint end_key_len, - enum ha_rkey_function end_search_flag); + ha_rows records_in_range(uint inx, key_range *min_key, key_range *max_key); int create(const char *name, TABLE *form, HA_CREATE_INFO *create_info); THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to, diff --git a/sql/ha_myisam.cc b/sql/ha_myisam.cc index dfb41efa88a..0c43b1f263e 100644 --- a/sql/ha_myisam.cc +++ b/sql/ha_myisam.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000,2004 MySQL AB & MySQL Finland AB & TCX DataKonsult AB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -810,47 +810,146 @@ int ha_myisam::preload_keys(THD* thd, HA_CHECK_OPT *check_opt) } } + /* - disable indexes, making it persistent if requested + Disable indexes, making it persistent if requested. + SYNOPSIS - disable_indexes(all, save) - all disable all indexes - if not set only non-unique indexes will be disabled - [all=1 is NOT IMPLEMENTED YET] - save save the disabled state, so that it will persist - between queries/threads/reboots - [save=0 is NOT IMPLEMENTED YET] + disable_indexes() + mode mode of operation: + HA_KEY_SWITCH_NONUNIQ disable all non-unique keys + HA_KEY_SWITCH_ALL disable all keys + HA_KEY_SWITCH_NONUNIQ_SAVE dis. non-uni. and make persistent + HA_KEY_SWITCH_ALL_SAVE dis. all keys and make persistent + + IMPLEMENTATION + HA_KEY_SWITCH_NONUNIQ is not implemented. + HA_KEY_SWITCH_ALL_SAVE is not implemented. + + RETURN + 0 ok + HA_ERR_WRONG_COMMAND mode not implemented. */ -int ha_myisam::disable_indexes(bool all, bool save) + +int ha_myisam::disable_indexes(uint mode) { - mi_extra(file, HA_EXTRA_NO_KEYS, 0); - info(HA_STATUS_CONST); // Read new key info - return 0; + int error; + + if (mode == HA_KEY_SWITCH_ALL) + { + /* call a storage engine function to switch the key map */ + error= mi_disable_indexes(file); + } + else if (mode == HA_KEY_SWITCH_NONUNIQ_SAVE) + { + mi_extra(file, HA_EXTRA_NO_KEYS, 0); + info(HA_STATUS_CONST); // Read new key info + error= 0; + } + else + { + /* mode not implemented */ + error= HA_ERR_WRONG_COMMAND; + } + return error; } -int ha_myisam::enable_indexes() + +/* + Enable indexes, making it persistent if requested. + + SYNOPSIS + enable_indexes() + mode mode of operation: + HA_KEY_SWITCH_NONUNIQ enable all non-unique keys + HA_KEY_SWITCH_ALL enable all keys + HA_KEY_SWITCH_NONUNIQ_SAVE en. non-uni. and make persistent + HA_KEY_SWITCH_ALL_SAVE en. all keys and make persistent + + DESCRIPTION + Enable indexes, which might have been disabled by disable_index() before. + The modes without _SAVE work only if both data and indexes are empty, + since the MyISAM repair would enable them persistently. + To be sure in these cases, call handler::delete_all_rows() before. + + IMPLEMENTATION + HA_KEY_SWITCH_NONUNIQ is not implemented. + HA_KEY_SWITCH_ALL_SAVE is not implemented. + + RETURN + 0 ok + !=0 Error, among others: + HA_ERR_CRASHED data or index is non-empty. Delete all rows and retry. + HA_ERR_WRONG_COMMAND mode not implemented. +*/ + +int ha_myisam::enable_indexes(uint mode) { + int error; + if (file->s->state.key_map == set_bits(ulonglong, file->s->base.keys)) + { + /* All indexes are enabled already. */ return 0; + } - int error=0; - THD *thd=current_thd; - MI_CHECK param; - const char *save_proc_info=thd->proc_info; - thd->proc_info="Creating index"; - myisamchk_init(¶m); - param.op_name = (char*) "recreating_index"; - param.testflag = (T_SILENT | T_REP_BY_SORT | T_QUICK | - T_CREATE_MISSING_KEYS); - param.myf_rw&= ~MY_WAIT_IF_FULL; - param.sort_buffer_length= thd->variables.myisam_sort_buff_size; - param.tmpdir=&mysql_tmpdir_list; - error=repair(thd,param,0) != HA_ADMIN_OK; - info(HA_STATUS_CONST); - thd->proc_info=save_proc_info; + if (mode == HA_KEY_SWITCH_ALL) + { + error= mi_enable_indexes(file); + /* + Do not try to repair on error, + as this could make the enabled state persistent, + but mode==HA_KEY_SWITCH_ALL forbids it. + */ + } + else if (mode == HA_KEY_SWITCH_NONUNIQ_SAVE) + { + THD *thd=current_thd; + MI_CHECK param; + const char *save_proc_info=thd->proc_info; + thd->proc_info="Creating index"; + myisamchk_init(¶m); + param.op_name = (char*) "recreating_index"; + param.testflag = (T_SILENT | T_REP_BY_SORT | T_QUICK | + T_CREATE_MISSING_KEYS); + param.myf_rw&= ~MY_WAIT_IF_FULL; + param.sort_buffer_length= thd->variables.myisam_sort_buff_size; + param.tmpdir=&mysql_tmpdir_list; + error=repair(thd,param,0) != HA_ADMIN_OK; + info(HA_STATUS_CONST); + thd->proc_info=save_proc_info; + } + else + { + /* mode not implemented */ + error= HA_ERR_WRONG_COMMAND; + } return error; } + +/* + Test if indexes are disabled. + + + SYNOPSIS + indexes_are_disabled() + no parameters + + + RETURN + 0 indexes are not disabled + 1 all indexes are disabled + [2 non-unique indexes are disabled - NOT YET IMPLEMENTED] +*/ + +int ha_myisam::indexes_are_disabled(void) +{ + + return mi_indexes_are_disabled(file); +} + + /* prepare for a many-rows insert operation e.g. - disable indexes (if they can be recreated fast) or @@ -898,7 +997,8 @@ int ha_myisam::end_bulk_insert() { mi_end_bulk_insert(file); int err=mi_extra(file, HA_EXTRA_NO_CACHE, 0); - return err ? err : can_enable_indexes ? enable_indexes() : 0; + return err ? err : can_enable_indexes ? + enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE) : 0; } @@ -1415,19 +1515,15 @@ longlong ha_myisam::get_auto_increment() SYNOPSIS records_in_range() inx Index to use - start_key Start of range. Null pointer if from first key - start_key_len Length of start key - start_search_flag Flag if start key should be included or not - end_key End of range. Null pointer if to last key - end_key_len Length of end key - end_search_flag Flag if start key should be included or not + min_key Start of range. Null pointer if from first key + max_key End of range. Null pointer if to last key NOTES - start_search_flag can have one of the following values: + min_key.flag can have one of the following values: HA_READ_KEY_EXACT Include the key in the range HA_READ_AFTER_KEY Don't include key in range - end_search_flag can have one of the following values: + max_key.flag can have one of the following values: HA_READ_BEFORE_KEY Don't include key in range HA_READ_AFTER_KEY Include all 'end_key' values in the range @@ -1438,18 +1534,10 @@ longlong ha_myisam::get_auto_increment() the range. */ -ha_rows ha_myisam::records_in_range(int inx, - const byte *start_key,uint start_key_len, - enum ha_rkey_function start_search_flag, - const byte *end_key,uint end_key_len, - enum ha_rkey_function end_search_flag) +ha_rows ha_myisam::records_in_range(uint inx, key_range *min_key, + key_range *max_key) { - return (ha_rows) mi_records_in_range(file, - inx, - start_key,start_key_len, - start_search_flag, - end_key,end_key_len, - end_search_flag); + return (ha_rows) mi_records_in_range(file, (int) inx, min_key, max_key); } diff --git a/sql/ha_myisam.h b/sql/ha_myisam.h index ca318b02778..77887220903 100644 --- a/sql/ha_myisam.h +++ b/sql/ha_myisam.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000,2004 MySQL AB & MySQL Finland AB & TCX DataKonsult AB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -105,15 +105,12 @@ class ha_myisam: public handler int extra_opt(enum ha_extra_function operation, ulong cache_size); int external_lock(THD *thd, int lock_type); int delete_all_rows(void); - int disable_indexes(bool all, bool save); - int enable_indexes(); + int disable_indexes(uint mode); + int enable_indexes(uint mode); + int indexes_are_disabled(void); void start_bulk_insert(ha_rows rows); int end_bulk_insert(); - ha_rows records_in_range(int inx, - const byte *start_key,uint start_key_len, - enum ha_rkey_function start_search_flag, - const byte *end_key,uint end_key_len, - enum ha_rkey_function end_search_flag); + ha_rows records_in_range(uint inx, key_range *min_key, key_range *max_key); void update_create_info(HA_CREATE_INFO *create_info); int create(const char *name, TABLE *form, HA_CREATE_INFO *create_info); THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to, diff --git a/sql/ha_myisammrg.cc b/sql/ha_myisammrg.cc index 041d9eaead3..9aa6d039efb 100644 --- a/sql/ha_myisammrg.cc +++ b/sql/ha_myisammrg.cc @@ -199,20 +199,14 @@ void ha_myisammrg::position(const byte *record) ha_store_ptr(ref, ref_length, (my_off_t) position); } -ha_rows ha_myisammrg::records_in_range(int inx, - const byte *start_key,uint start_key_len, - enum ha_rkey_function start_search_flag, - const byte *end_key,uint end_key_len, - enum ha_rkey_function end_search_flag) + +ha_rows ha_myisammrg::records_in_range(uint inx, key_range *min_key, + key_range *max_key) { - return (ha_rows) myrg_records_in_range(file, - inx, - start_key,start_key_len, - start_search_flag, - end_key,end_key_len, - end_search_flag); + return (ha_rows) myrg_records_in_range(file, (int) inx, min_key, max_key); } + void ha_myisammrg::info(uint flag) { MYMERGE_INFO info; diff --git a/sql/ha_myisammrg.h b/sql/ha_myisammrg.h index c0f81a77a1e..fd36c78202d 100644 --- a/sql/ha_myisammrg.h +++ b/sql/ha_myisammrg.h @@ -70,11 +70,7 @@ class ha_myisammrg: public handler int rnd_next(byte *buf); int rnd_pos(byte * buf, byte *pos); void position(const byte *record); - ha_rows records_in_range(int inx, - const byte *start_key,uint start_key_len, - enum ha_rkey_function start_search_flag, - const byte *end_key,uint end_key_len, - enum ha_rkey_function end_search_flag); + ha_rows records_in_range(uint inx, key_range *min_key, key_range *max_key); my_off_t row_position() { return myrg_position(file); } void info(uint); int extra(enum ha_extra_function operation); diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index 3bc322878d1..21056ef4a8f 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -2748,22 +2748,19 @@ void ha_ndbcluster::set_dbname(const char *path_name) ha_rows -ha_ndbcluster::records_in_range(int inx, - const byte *start_key,uint start_key_len, - enum ha_rkey_function start_search_flag, - const byte *end_key,uint end_key_len, - enum ha_rkey_function end_search_flag) -{ - ha_rows records= 10; - KEY* key_info= table->key_info + inx; +ha_ndbcluster::records_in_range(uint inx, key_range *min_key, + key_range *max_key) +{ + ha_rows records= 10; /* Good guess when you don't know anything */ + KEY *key_info= table->key_info + inx; uint key_length= key_info->key_length; DBUG_ENTER("records_in_range"); - DBUG_PRINT("enter", ("inx: %d", inx)); - DBUG_PRINT("enter", ("start_key: %x, start_key_len: %d", start_key, start_key_len)); - DBUG_PRINT("enter", ("start_search_flag: %d", start_search_flag)); - DBUG_PRINT("enter", ("end_key: %x, end_key_len: %d", end_key, end_key_len)); - DBUG_PRINT("enter", ("end_search_flag: %d", end_search_flag)); + DBUG_PRINT("enter", ("inx: %u", inx)); + DBUG_DUMP("start_key", min_key->key, min_key->length); + DBUG_DUMP("end_key", max_key->key, max_key->length); + DBUG_PRINT("enter", ("start_search_flag: %u end_search_flag: %u", + min_key->flag, max_key->flag)); /* Check that start_key_len is equal to @@ -2772,7 +2769,7 @@ ha_ndbcluster::records_in_range(int inx, */ NDB_INDEX_TYPE idx_type= get_index_type(inx); if ((idx_type == UNIQUE_INDEX || idx_type == PRIMARY_KEY_INDEX) && - start_key_len < key_length) + min_key->length < key_length) { DBUG_PRINT("warning", ("Tried to use index which required" "full key length: %d, HA_POS_ERROR", diff --git a/sql/ha_ndbcluster.h b/sql/ha_ndbcluster.h index bd601f39fc4..029ebc11e46 100644 --- a/sql/ha_ndbcluster.h +++ b/sql/ha_ndbcluster.h @@ -121,12 +121,7 @@ class ha_ndbcluster: public handler } double scan_time(); - ha_rows records_in_range(int inx, - const byte *start_key,uint start_key_len, - enum ha_rkey_function start_search_flag, - const byte *end_key,uint end_key_len, - enum ha_rkey_function end_search_flag); - + ha_rows records_in_range(uint inx, key_range *min_key, key_range *max_key); static Ndb* seize_ndb(); static void release_ndb(Ndb* ndb); diff --git a/sql/handler.cc b/sql/handler.cc index 944d3421d75..079ff5ba687 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -103,15 +103,16 @@ TYPELIB tx_isolation_typelib= {array_elements(tx_isolation_names)-1,"", enum db_type ha_resolve_by_name(const char *name, uint namelen) { - if (!my_strcasecmp(&my_charset_latin1, name, "DEFAULT")) { - return(enum db_type) current_thd->variables.table_type; + THD *thd=current_thd; + if (thd && !my_strcasecmp(&my_charset_latin1, name, "DEFAULT")) { + return (enum db_type) thd->variables.table_type; } show_table_type_st *types; for (types= sys_table_types; types->type; types++) { if (!my_strcasecmp(&my_charset_latin1, name, types->type)) - return(enum db_type)types->db_type; + return (enum db_type) types->db_type; } return DB_TYPE_UNKNOWN; } @@ -742,7 +743,7 @@ int ha_delete_table(enum db_type table_type, const char *path) { /* Ensure that table handler get path in lower case */ strmov(tmp_path, path); - my_casedn_str(system_charset_info, tmp_path); + my_casedn_str(files_charset_info, tmp_path); path= tmp_path; } int error=file->delete_table(path); @@ -1184,7 +1185,7 @@ int handler::index_next_same(byte *buf, const byte *key, uint keylen) int error; if (!(error=index_next(buf))) { - if (key_cmp(table, key, active_index, keylen)) + if (key_cmp_if_same(table, key, active_index, keylen)) { table->status=STATUS_NOT_FOUND; error=HA_ERR_END_OF_FILE; @@ -1248,7 +1249,7 @@ int ha_create_table(const char *name, HA_CREATE_INFO *create_info, { /* Ensure that handler gets name in lower case */ strmov(name_buff, name); - my_casedn_str(system_charset_info, name_buff); + my_casedn_str(files_charset_info, name_buff); name= name_buff; } @@ -1390,6 +1391,7 @@ int ha_discover(const char* dbname, const char* name, read_range_first() start_key Start key. Is 0 if no min range end_key End key. Is 0 if no max range + eq_range_arg Set to 1 if start_key == end_key sorted Set to 1 if result should be sorted per key NOTES @@ -1403,11 +1405,12 @@ int ha_discover(const char* dbname, const char* name, int handler::read_range_first(const key_range *start_key, const key_range *end_key, - bool sorted) + bool eq_range_arg, bool sorted) { int result; DBUG_ENTER("handler::read_range_first"); + eq_range= eq_range_arg; end_range= 0; if (end_key) { @@ -1418,7 +1421,6 @@ int handler::read_range_first(const key_range *start_key, } range_key_part= table->key_info[active_index].key_part; - if (!start_key) // Read first record result= index_first(table->record[0]); else @@ -1440,7 +1442,6 @@ int handler::read_range_first(const key_range *start_key, SYNOPSIS read_range_next() - eq_range Set to 1 if start_key == end_key NOTES Record is read into table->record[0] @@ -1451,17 +1452,19 @@ int handler::read_range_first(const key_range *start_key, # Error code */ -int handler::read_range_next(bool eq_range) +int handler::read_range_next() { int result; DBUG_ENTER("handler::read_range_next"); if (eq_range) - result= index_next_same(table->record[0], - end_range->key, - end_range->length); - else - result= index_next(table->record[0]); + { + /* We trust that index_next_same always gives a row in range */ + DBUG_RETURN(index_next_same(table->record[0], + end_range->key, + end_range->length)); + } + result= index_next(table->record[0]); if (result) DBUG_RETURN(result); DBUG_RETURN(compare_key(end_range) <= 0 ? 0 : HA_ERR_END_OF_FILE); @@ -1469,16 +1472,18 @@ int handler::read_range_next(bool eq_range) /* - Compare if found key is over max-value + Compare if found key (in row) is over max-value SYNOPSIS compare_key - range key to compare to row + range range to compare to row. May be 0 for no range NOTES - For this to work, the row must be stored in table->record[0] + See key.cc::key_cmp() for details RETURN + The return value is SIGN(key_in_row - range_key): + 0 Key is equal to range or 'range' == 0 (no range) -1 Key is less than range 1 Key is larger than range @@ -1486,37 +1491,11 @@ int handler::read_range_next(bool eq_range) int handler::compare_key(key_range *range) { - KEY_PART_INFO *key_part= range_key_part; - uint store_length; - + int cmp; if (!range) return 0; // No max range - - for (const char *key= (const char*) range->key, *end=key+range->length; - key < end; - key+= store_length, key_part++) - { - int cmp; - store_length= key_part->store_length; - if (key_part->null_bit) - { - if (*key) - { - if (!key_part->field->is_null()) - return 1; - continue; - } - else if (key_part->field->is_null()) - return 0; - key++; // Skip null byte - store_length--; - } - if ((cmp=key_part->field->key_cmp((byte*) key, key_part->length)) < 0) - return -1; - if (cmp > 0) - return 1; - } - return key_compare_result_on_equal; + cmp= key_cmp(range_key_part, range->key, range->length); + if (!cmp) + cmp= key_compare_result_on_equal; + return cmp; } - - diff --git a/sql/handler.h b/sql/handler.h index 15fc6a201d8..1cafeedc7ef 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000,2004 MySQL AB & MySQL Finland AB & TCX DataKonsult AB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -91,6 +91,13 @@ #define HA_KEY_READ_ONLY 64 /* Support HA_EXTRA_KEYREAD */ +/* operations for disable/enable indexes */ +#define HA_KEY_SWITCH_NONUNIQ 0 +#define HA_KEY_SWITCH_ALL 1 +#define HA_KEY_SWITCH_NONUNIQ_SAVE 2 +#define HA_KEY_SWITCH_ALL_SAVE 3 + + /* Bits in index_ddl_flags(KEY *wanted_index) for what ddl you can do with index @@ -227,14 +234,6 @@ typedef struct st_ha_check_opt } HA_CHECK_OPT; -typedef struct st_key_range -{ - const byte *key; - uint length; - enum ha_rkey_function flag; -} key_range; - - class handler :public Sql_alloc { protected: @@ -261,6 +260,7 @@ public: key_range save_end_range, *end_range; KEY_PART_INFO *range_key_part; int key_compare_result_on_equal; + bool eq_range; uint errkey; /* Last dup key */ uint sortkey, key_used_on_scan; @@ -323,13 +323,14 @@ public: return (my_errno=HA_ERR_WRONG_COMMAND); } virtual int read_range_first(const key_range *start_key, - const key_range *end_key, - bool sorted); - virtual int read_range_next(bool eq_range); + const key_range *end_key, + bool eq_range, bool sorted); + virtual int read_range_next(); int compare_key(key_range *range); virtual int ft_init() { return -1; } - virtual FT_INFO *ft_init_ext(uint flags,uint inx,const byte *key, uint keylen) + virtual FT_INFO *ft_init_ext(uint flags,uint inx,const byte *key, + uint keylen) { return NULL; } virtual int ft_read(byte *buf) { return -1; } virtual int rnd_init(bool scan=1)=0; @@ -338,11 +339,8 @@ public: virtual int rnd_pos(byte * buf, byte *pos)=0; virtual int read_first_row(byte *buf, uint primary_key); virtual int restart_rnd_next(byte *buf, byte *pos); - virtual ha_rows records_in_range(int inx, - const byte *start_key,uint start_key_len, - enum ha_rkey_function start_search_flag, - const byte *end_key,uint end_key_len, - enum ha_rkey_function end_search_flag) + virtual ha_rows records_in_range(uint inx, key_range *min_key, + key_range *max_key) { return (ha_rows) 10; } virtual void position(const byte *record)=0; virtual my_off_t row_position() { return HA_OFFSET_ERROR; } @@ -373,8 +371,9 @@ public: */ virtual int restore(THD* thd, HA_CHECK_OPT* check_opt); virtual int dump(THD* thd, int fd = -1) { return ER_DUMP_NOT_IMPLEMENTED; } - virtual int disable_indexes(bool all, bool save) { return HA_ERR_WRONG_COMMAND; } - virtual int enable_indexes() { return HA_ERR_WRONG_COMMAND; } + virtual int disable_indexes(uint mode) { return HA_ERR_WRONG_COMMAND; } + virtual int enable_indexes(uint mode) { return HA_ERR_WRONG_COMMAND; } + virtual int indexes_are_disabled(void) {return 0;} virtual void start_bulk_insert(ha_rows rows) {} virtual int end_bulk_insert() {return 0; } virtual int discard_or_import_tablespace(my_bool discard) {return -1;} diff --git a/sql/item.cc b/sql/item.cc index 8c20d45714f..8dfe40abea8 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -28,6 +28,8 @@ static void mark_as_dependent(THD *thd, SELECT_LEX *last, SELECT_LEX *current, Item_ident *item); +const String my_null_string("NULL", 4, default_charset_info); + /***************************************************************************** ** Item functions *****************************************************************************/ @@ -399,9 +401,14 @@ const char *Item_ident::full_name() const } else { - tmp=(char*) sql_alloc((uint) strlen(table_name)+ - (uint) strlen(field_name)+2); - strxmov(tmp,table_name,".",field_name,NullS); + if (table_name[0]) + { + tmp= (char*) sql_alloc((uint) strlen(table_name) + + (uint) strlen(field_name) + 2); + strxmov(tmp, table_name, ".", field_name, NullS); + } + else + tmp= (char*) field_name; } return tmp; } @@ -648,13 +655,11 @@ default_set_param_func(Item_param *param, param->set_null(); } -Item_param::Item_param(unsigned position) : - value_is_set(FALSE), +Item_param::Item_param(unsigned pos_in_query_arg) : + state(NO_VALUE), item_result_type(STRING_RESULT), item_type(STRING_ITEM), - item_is_time(FALSE), - long_data_supplied(FALSE), - pos_in_query(position), + pos_in_query(pos_in_query_arg), set_param_func(default_set_param_func) { name= (char*) "?"; @@ -670,74 +675,93 @@ void Item_param::set_null() { DBUG_ENTER("Item_param::set_null"); /* These are cleared after each execution by reset() method */ - null_value= value_is_set= 1; + max_length= 0; + null_value= 1; + /* + Because of NULL and string values we need to set max_length for each new + placeholder value: user can submit NULL for any placeholder type, and + string length can be different in each execution. + */ + max_length= 0; + decimals= 0; + state= NULL_VALUE; DBUG_VOID_RETURN; } -void Item_param::set_int(longlong i) +void Item_param::set_int(longlong i, uint32 max_length_arg) { DBUG_ENTER("Item_param::set_int"); - int_value= (longlong)i; - item_type= INT_ITEM; - value_is_set= 1; + value.integer= (longlong) i; + state= INT_VALUE; + max_length= max_length_arg; + decimals= 0; maybe_null= 0; - DBUG_PRINT("info", ("integer: %lld", int_value)); DBUG_VOID_RETURN; } -void Item_param::set_double(double value) +void Item_param::set_double(double d) { DBUG_ENTER("Item_param::set_double"); - real_value=value; - item_type= REAL_ITEM; - value_is_set= 1; + value.real= d; + state= REAL_VALUE; + max_length= DBL_DIG + 8; + decimals= NOT_FIXED_DEC; maybe_null= 0; - DBUG_PRINT("info", ("double: %lg", real_value)); DBUG_VOID_RETURN; } -void Item_param::set_value(const char *str, uint length) -{ - DBUG_ENTER("Item_param::set_value"); - str_value.copy(str,length,default_charset()); - item_type= STRING_ITEM; - value_is_set= 1; +void Item_param::set_time(TIME *tm, timestamp_type type, uint32 max_length_arg) +{ + DBUG_ENTER("Item_param::set_time"); + + value.time= *tm; + value.time.time_type= type; + + state= TIME_VALUE; maybe_null= 0; - DBUG_PRINT("info", ("string: %s", str_value.ptr())); + max_length= max_length_arg; + decimals= 0; DBUG_VOID_RETURN; } -void Item_param::set_time(TIME *tm, timestamp_type type) -{ - ltime.year= tm->year; - ltime.month= tm->month; - ltime.day= tm->day; - - ltime.hour= tm->hour; - ltime.minute= tm->minute; - ltime.second= tm->second; - - ltime.second_part= tm->second_part; - - ltime.neg= tm->neg; - - ltime.time_type= type; - - item_is_time= TRUE; - item_type= STRING_ITEM; - value_is_set= 1; +bool Item_param::set_str(const char *str, ulong length) +{ + DBUG_ENTER("Item_param::set_str"); + /* + Assign string with no conversion: data is converted only after it's + been written to the binary log. + */ + if (str_value.copy(str, length, &my_charset_bin, &my_charset_bin)) + DBUG_RETURN(TRUE); + state= STRING_VALUE; maybe_null= 0; + /* max_length and decimals are set after charset conversion */ + /* sic: str may be not null-terminated, don't add DBUG_PRINT here */ + DBUG_RETURN(FALSE); } -void Item_param::set_longdata(const char *str, ulong length) -{ - str_value.append(str,length); - long_data_supplied= 1; - value_is_set= 1; +bool Item_param::set_longdata(const char *str, ulong length) +{ + DBUG_ENTER("Item_param::set_longdata"); + + /* + If client character set is multibyte, end of long data packet + may hit at the middle of a multibyte character. Additionally, + if binary log is open we must write long data value to the + binary log in character set of client. This is why we can't + convert long data to connection character set as it comes + (here), and first have to concatenate all pieces together, + write query to the binary log and only then perform conversion. + */ + if (str_value.append(str, length, &my_charset_bin)) + DBUG_RETURN(TRUE); + state= LONG_DATA_VALUE; maybe_null= 0; + + DBUG_RETURN(FALSE); } @@ -753,9 +777,18 @@ void Item_param::set_longdata(const char *str, ulong length) */ void Item_param::reset() -{ - str_value.set("", 0, &my_charset_bin); - value_is_set= long_data_supplied= 0; +{ + /* Shrink string buffer if it's bigger than max possible CHAR column */ + if (str_value.alloced_length() > MAX_CHAR_WIDTH) + str_value.free(); + else + str_value.length(0); + /* + We must prevent all charset conversions unless data of str_value + has been written to the binary log. + */ + str_value.set_charset(&my_charset_bin); + state= NO_VALUE; maybe_null= 1; null_value= 0; } @@ -764,155 +797,223 @@ void Item_param::reset() int Item_param::save_in_field(Field *field, bool no_conversions) { DBUG_ASSERT(current_thd->command == COM_EXECUTE); - - if (null_value) - return (int) set_field_to_null(field); - + field->set_notnull(); - if (item_result_type == INT_RESULT) - { - longlong nr=val_int(); - return field->store(nr); - } - if (item_result_type == REAL_RESULT) - { - double nr=val(); - return field->store(nr); - } - if (item_is_time) - { - field->store_time(<ime, ltime.time_type); + + switch (state) { + case INT_VALUE: + return field->store(value.integer); + case REAL_VALUE: + return field->store(value.real); + case TIME_VALUE: + field->store_time(&value.time, value.time.time_type); return 0; + case STRING_VALUE: + case LONG_DATA_VALUE: + return field->store(str_value.ptr(), str_value.length(), + str_value.charset()); + case NULL_VALUE: + return set_field_to_null(field); + case NO_VALUE: + default: + DBUG_ASSERT(0); } - String *result=val_str(&str_value); - return field->store(result->ptr(),result->length(),field->charset()); + return 1; } + bool Item_param::get_time(TIME *res) { - *res=ltime; - return 0; + if (state == TIME_VALUE) + { + *res= value.time; + return 0; + } + /* + If parameter value isn't supplied assertion will fire in val_str() + which is called from Item::get_time(). + */ + return Item::get_time(res); } + double Item_param::val() { - DBUG_ASSERT(value_is_set == 1); - int err; - if (null_value) + switch (state) { + case REAL_VALUE: + return value.real; + case INT_VALUE: + return (double) value.integer; + case STRING_VALUE: + case LONG_DATA_VALUE: + { + int dummy_err; + return my_strntod(str_value.charset(), (char*) str_value.ptr(), + str_value.length(), (char**) 0, &dummy_err); + } + case TIME_VALUE: + /* + This works for example when user says SELECT ?+0.0 and supplies + time value for the placeholder. + */ + return (double) TIME_to_ulonglong(&value.time); + case NULL_VALUE: return 0.0; - switch (item_result_type) { - case STRING_RESULT: - return (double) my_strntod(str_value.charset(), (char*) str_value.ptr(), - str_value.length(), (char**) 0, &err); - case INT_RESULT: - return (double)int_value; default: - return real_value; + DBUG_ASSERT(0); } + return 0.0; } longlong Item_param::val_int() { - DBUG_ASSERT(value_is_set == 1); - int err; - if (null_value) - return 0; - switch (item_result_type) { - case STRING_RESULT: - return my_strntoll(str_value.charset(), - str_value.ptr(),str_value.length(),10, - (char**) 0,&err); - case REAL_RESULT: - return (longlong) (real_value+(real_value > 0 ? 0.5 : -0.5)); + switch (state) { + case REAL_VALUE: + return (longlong) (value.real + (value.real > 0 ? 0.5 : -0.5)); + case INT_VALUE: + return value.integer; + case STRING_VALUE: + case LONG_DATA_VALUE: + { + int dummy_err; + return my_strntoll(str_value.charset(), str_value.ptr(), + str_value.length(), 10, (char**) 0, &dummy_err); + } + case TIME_VALUE: + return (longlong) TIME_to_ulonglong(&value.time); + case NULL_VALUE: + return 0; default: - return int_value; + DBUG_ASSERT(0); } + return 0; } String *Item_param::val_str(String* str) { - DBUG_ASSERT(value_is_set == 1); - if (null_value) - return NULL; - switch (item_result_type) { - case INT_RESULT: - str->set(int_value, &my_charset_bin); + switch (state) { + case STRING_VALUE: + case LONG_DATA_VALUE: + return &str_value; + case REAL_VALUE: + str->set(value.real, NOT_FIXED_DEC, &my_charset_bin); return str; - case REAL_RESULT: - str->set(real_value, 2, &my_charset_bin); + case INT_VALUE: + str->set(value.integer, &my_charset_bin); + return str; + case TIME_VALUE: + { + if (str->reserve(MAX_DATE_REP_LENGTH)) + break; + TIME_to_string(&value.time, str); return str; + } + case NULL_VALUE: + return NULL; default: - return (String*) &str_value; + DBUG_ASSERT(0); } + return str; } /* Return Param item values in string format, for generating the dynamic query used in update/binary logs + TODO: change interface and implementation to fill log data in place + and avoid one more memcpy/alloc between str and log string. */ -String *Item_param::query_val_str(String* str) +const String *Item_param::query_val_str(String* str) const { - DBUG_ASSERT(value_is_set == 1); - switch (item_result_type) { - case INT_RESULT: - case REAL_RESULT: - return val_str(str); - default: - str->set("'", 1, default_charset()); - - if (!item_is_time) + switch (state) { + case INT_VALUE: + str->set(value.integer, &my_charset_bin); + break; + case REAL_VALUE: + str->set(value.real, NOT_FIXED_DEC, &my_charset_bin); + break; + case TIME_VALUE: { - str->append(str_value); - const char *from= str->ptr(); - uint32 length= 1; - - // Escape misc cases - char *to= (char *)from, *end= (char *)to+str->length(); - for (to++; to != end ; length++, to++) - { - switch(*to) { - case '\'': - case '"': - case '\r': - case '\n': - case '\\': // TODO: Add remaining .. - str->replace(length,0,"\\",1); - to++; end++; length++; - break; - default: - break; - } - } + char *buf, *ptr; + String tmp; + str->length(0); + /* + TODO: in case of error we need to notify replication + that binary log contains wrong statement + */ + if (str->reserve(MAX_DATE_REP_LENGTH+3)) + break; + + /* Create date string inplace */ + buf= str->c_ptr_quick(); + ptr= buf; + *ptr++= '\''; + tmp.set(ptr, MAX_DATE_REP_LENGTH, &my_charset_bin); + tmp.length(0); + TIME_to_string(&value.time, &tmp); + + ptr+= tmp.length(); + *ptr++= '\''; + str->length((uint32) (ptr - buf)); + break; } - else + case STRING_VALUE: + case LONG_DATA_VALUE: { - char buff[40]; - String tmp(buff,sizeof(buff), &my_charset_bin); - - switch (ltime.time_type) { - case TIMESTAMP_NONE: - case TIMESTAMP_DATETIME_ERROR: - tmp.length(0); // Should never happen - break; - case TIMESTAMP_DATE: - make_date((DATE_TIME_FORMAT*) 0, <ime, &tmp); - break; - case TIMESTAMP_DATETIME: - make_datetime((DATE_TIME_FORMAT*) 0, <ime, &tmp); - break; - case TIMESTAMP_TIME: - make_time((DATE_TIME_FORMAT*) 0, <ime, &tmp); - break; - } - str->append(tmp); + char *buf, *ptr; + str->length(0); + if (str->reserve(str_value.length()*2+3)) + break; + + buf= str->c_ptr_quick(); + ptr= buf; + *ptr++= '\''; + ptr+= escape_string_for_mysql(str_value.charset(), ptr, + str_value.ptr(), str_value.length()); + *ptr++= '\''; + str->length(ptr - buf); + break; } - str->append('\''); + case NULL_VALUE: + return &my_null_string; + default: + DBUG_ASSERT(0); } return str; } + + +/* + Convert string from client character set to the character set of + connection. +*/ + +bool Item_param::convert_str_value(THD *thd) +{ + bool rc= FALSE; + if (state == STRING_VALUE || state == LONG_DATA_VALUE) + { + /* + Check is so simple because all charsets were set up properly + in setup_one_conversion_function, where typecode of + placeholder was also taken into account: the variables are different + here only if conversion is really necessary. + */ + if (value.cs_info.final_character_set_of_str_value != + value.cs_info.character_set_client) + { + rc= thd->convert_string(&str_value, + value.cs_info.character_set_client, + value.cs_info.final_character_set_of_str_value); + } + max_length= str_value.length(); + decimals= 0; + } + return rc; +} + /* End of Item_param related */ diff --git a/sql/item.h b/sql/item.h index 8c356d84aa0..8847cb35493 100644 --- a/sql/item.h +++ b/sql/item.h @@ -495,33 +495,63 @@ public: class Item_param :public Item { public: - bool value_is_set; - longlong int_value; - double real_value; - TIME ltime; + enum enum_item_param_state + { + NO_VALUE, NULL_VALUE, INT_VALUE, REAL_VALUE, + STRING_VALUE, TIME_VALUE, LONG_DATA_VALUE + } state; + + union + { + longlong integer; + double real; + /* + Character sets conversion info for string values. + Character sets of client and connection defined at bind time are used + for all conversions, even if one of them is later changed (i.e. + between subsequent calls to mysql_stmt_execute). + */ + struct CONVERSION_INFO + { + CHARSET_INFO *character_set_client; + /* + This points at character set of connection if conversion + to it is required (i. e. if placeholder typecode is not BLOB). + Otherwise it's equal to character_set_client (to simplify + check in convert_str_value()). + */ + CHARSET_INFO *final_character_set_of_str_value; + } cs_info; + TIME time; + } value; + + /* Cached values for virtual methods to save us one switch. */ enum Item_result item_result_type; enum Type item_type; - enum enum_field_types buffer_type; - bool item_is_time; - bool long_data_supplied; + /* + Offset of placeholder inside statement text. Used to create + no-placeholders version of this statement for the binary log. + */ uint pos_in_query; - Item_param(uint position); + Item_param(uint pos_in_query_arg); + + enum Item_result result_type () const { return item_result_type; } enum Type type() const { return item_type; } + enum_field_types field_type() const { return MYSQL_TYPE_STRING; } + double val(); longlong val_int(); String *val_str(String*); + bool get_time(TIME *tm); int save_in_field(Field *field, bool no_conversions); + void set_null(); - void set_int(longlong i); + void set_int(longlong i, uint32 max_length_arg); void set_double(double i); - void set_value(const char *str, uint length); - void set_long_str(const char *str, ulong length); - void set_long_binary(const char *str, ulong length); - void set_longdata(const char *str, ulong length); - void set_long_end(); - void set_time(TIME *tm, timestamp_type type); - bool get_time(TIME *tm); + bool set_str(const char *str, ulong length); + bool set_longdata(const char *str, ulong length); + void set_time(TIME *tm, timestamp_type type, uint32 max_length_arg); void reset(); /* Assign placeholder value from bind data. @@ -531,10 +561,10 @@ public: */ void (*set_param_func)(Item_param *param, uchar **pos, ulong len); - enum Item_result result_type () const - { return item_result_type; } - String *query_val_str(String *str); - enum_field_types field_type() const { return MYSQL_TYPE_STRING; } + const String *query_val_str(String *str) const; + + bool convert_str_value(THD *thd); + Item *new_item() { return new Item_param(pos_in_query); } /* If value for parameter was not set we treat it as non-const @@ -542,7 +572,7 @@ public: parameter is constant during execution. */ virtual table_map used_tables() const - { return value_is_set ? (table_map)0 : PARAM_TABLE_BIT; } + { return state != NO_VALUE ? (table_map)0 : PARAM_TABLE_BIT; } void print(String *str) { str->append('?'); } }; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index afbf0b7163e..446d72ac143 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -221,6 +221,7 @@ void Item_bool_func2::fix_length_and_dec() { conv= new Item_func_conv_charset(args[weak],args[strong]->collation.collation); conv->collation.set(args[weak]->collation.derivation); + conv->fix_fields(current_thd, 0, &conv); } args[weak]= conv ? conv : args[weak]; } diff --git a/sql/item_func.cc b/sql/item_func.cc index 47ca94238ab..c4cb8a7a2f0 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -615,6 +615,7 @@ longlong Item_func_div::val_int() void Item_func_div::fix_length_and_dec() { decimals=max(args[0]->decimals,args[1]->decimals)+2; + set_if_smaller(decimals, NOT_FIXED_DEC); max_length=args[0]->max_length - args[0]->decimals + decimals; uint tmp=float_length(decimals); set_if_smaller(max_length,tmp); @@ -2386,7 +2387,10 @@ longlong user_var_entry::val_int(my_bool *null_value) case INT_RESULT: return *(longlong*) value; case STRING_RESULT: - return strtoull(value,NULL,10); // String is null terminated + { + int error; + return my_strtoll10(value, (char**) 0, &error);// String is null terminated + } case ROW_RESULT: DBUG_ASSERT(1); // Impossible break; diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index c05091cae1f..676d8c1386a 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -2587,6 +2587,7 @@ String *Item_func_quote::val_str(String *str) *to= '\''; str->length(new_length); str->set_charset(collation.collation); + null_value= 0; return str; null: diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index dc773de158b..48d38c0d602 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -56,10 +56,24 @@ void Item_subselect::init(st_select_lex *select_lex, DBUG_PRINT("subs", ("select_lex 0x%xl", (ulong) select_lex)); unit= select_lex->master_unit(); - if (select_lex->next_select()) - engine= new subselect_union_engine(unit, result, this); + if (unit->item) + { + /* + Item can be changed in JOIN::prepare while engine in JOIN::optimize + => we do not copy old_engine here + */ + engine= unit->item->engine; + unit->item->engine= 0; + unit->item= this; + engine->change_item(this, result); + } else - engine= new subselect_single_select_engine(select_lex, result, this); + { + if (select_lex->next_select()) + engine= new subselect_union_engine(unit, result, this); + else + engine= new subselect_single_select_engine(select_lex, result, this); + } DBUG_VOID_RETURN; } @@ -69,11 +83,13 @@ void Item_subselect::cleanup() Item_result_field::cleanup(); if (old_engine) { - engine->cleanup(); + if (engine) + engine->cleanup(); engine= old_engine; old_engine= 0; } - engine->cleanup(); + if (engine) + engine->cleanup(); reset(); value_assigned= 0; DBUG_VOID_RETURN; @@ -127,7 +143,6 @@ bool Item_subselect::fix_fields(THD *thd_param, TABLE_LIST *tables, Item **ref) if (have_to_be_excluded) engine->exclude(); substitution= 0; - fixed= 1; thd->where= "checking transformed subquery"; if (!(*ref)->fixed) ret= (*ref)->fix_fields(thd, tables, ref); @@ -660,10 +675,20 @@ Item_in_subselect::single_value_transformer(JOIN *join, item= new Item_sum_min(*select_lex->ref_pointer_array); } *select_lex->ref_pointer_array= item; - select_lex->item_list.empty(); - select_lex->item_list.push_back(item); + { + List_iterator<Item> it(select_lex->item_list); + it++; + it.replace(item); + } - // fix_fields call for 'item' will be made during new subquery fix_fields + /* + Item_sum_(max|min) can't substitute other item => we can use 0 as + reference + */ + if (item->fix_fields(thd, join->tables_list, 0)) + goto err; + /* we added aggregate function => we have to change statistic */ + count_field_types(&join->tmp_table_param, join->all_fields, 0); subs= new Item_singlerow_subselect(select_lex); } @@ -1422,3 +1447,67 @@ void subselect_indexsubquery_engine::print(String *str) } str->append(')'); } + +/* + change select_result object of engine + + SINOPSYS + subselect_single_select_engine::change_result() + si new subselect Item + res new select_result object + + RETURN + 0 OK + -1 error +*/ + +int subselect_single_select_engine::change_item(Item_subselect *si, + select_subselect *res) +{ + item= si; + result= res; + return select_lex->join->change_result(result); +} + + +/* + change select_result object of engine + + SINOPSYS + subselect_single_select_engine::change_result() + si new subselect Item + res new select_result object + + RETURN + 0 OK + -1 error +*/ + +int subselect_union_engine::change_item(Item_subselect *si, + select_subselect *res) +{ + item= si; + int rc= unit->change_result(res, result); + result= res; + return rc; +} + + +/* + change select_result emulation, never should be called + + SINOPSYS + subselect_single_select_engine::change_result() + si new subselect Item + res new select_result object + + RETURN + -1 error +*/ + +int subselect_uniquesubquery_engine::change_item(Item_subselect *si, + select_subselect *res) +{ + DBUG_ASSERT(0); + return -1; +} diff --git a/sql/item_subselect.h b/sql/item_subselect.h index f9c570d5e25..019d43e4819 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -290,6 +290,7 @@ public: virtual table_map upper_select_const_tables()= 0; static table_map calc_const_tables(TABLE_LIST *); virtual void print(String *str)= 0; + virtual int change_item(Item_subselect *si, select_subselect *result)= 0; }; @@ -313,6 +314,7 @@ public: void exclude(); table_map upper_select_const_tables(); void print (String *str); + int change_item(Item_subselect *si, select_subselect *result); }; @@ -332,6 +334,7 @@ public: void exclude(); table_map upper_select_const_tables(); void print (String *str); + int change_item(Item_subselect *si, select_subselect *result); }; @@ -360,6 +363,7 @@ public: void exclude(); table_map upper_select_const_tables() { return 0; } void print (String *str); + int change_item(Item_subselect *si, select_subselect *result); }; diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 94f791a06cc..3275440e522 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -618,6 +618,14 @@ void Item_sum_variance::update_field() /* min & max */ +void Item_sum_hybrid::clear() +{ + sum= 0.0; + sum_int= 0; + value.length(0); + null_value= 1; +} + double Item_sum_hybrid::val() { DBUG_ASSERT(fixed == 1); diff --git a/sql/item_sum.h b/sql/item_sum.h index 4da8db11cb0..ce46e92e6cf 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -450,13 +450,7 @@ class Item_sum_hybrid :public Item_sum table_map used_tables() const { return used_table_cache; } bool const_item() const { return !used_table_cache; } - void clear() - { - sum=0.0; - sum_int=0; - value.length(0); - null_value=1; - } + void clear(); double val(); longlong val_int(); void reset_field(); diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 2cda5dde3aa..39f67ba24f8 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -1140,9 +1140,6 @@ void Item_func_curdate::fix_length_and_dec() store_now_in_tm(current_thd->query_start(),&start); - value=(longlong) ((ulong) ((uint) start.tm_year+1900)*10000L+ - ((uint) start.tm_mon+1)*100+ - (uint) start.tm_mday); /* For getdate */ ltime.year= start.tm_year+1900; ltime.month= start.tm_mon+1; @@ -1153,6 +1150,7 @@ void Item_func_curdate::fix_length_and_dec() ltime.second_part=0; ltime.neg=0; ltime.time_type=TIMESTAMP_DATE; + value= (longlong) TIME_to_ulonglong_date(<ime); } String *Item_func_curdate::val_str(String *str) @@ -1211,15 +1209,12 @@ void Item_func_curtime::fix_length_and_dec() decimals=0; store_now_in_tm(current_thd->query_start(),&start); - value=(longlong) ((ulong) ((uint) start.tm_hour)*10000L+ - (ulong) (((uint) start.tm_min)*100L+ - (uint) start.tm_sec)); - ltime.day= 0; ltime.hour= start.tm_hour; ltime.minute= start.tm_min; ltime.second= start.tm_sec; ltime.second_part= 0; ltime.neg= 0; + value= TIME_to_ulonglong_time(<ime); make_time((DATE_TIME_FORMAT *) 0, <ime, &tmp); max_length= buff_length= tmp.length(); } @@ -1262,23 +1257,12 @@ void Item_func_now::fix_length_and_dec() collation.set(&my_charset_bin); store_now_in_tm(current_thd->query_start(),&start); - value=((longlong) ((ulong) ((uint) start.tm_year+1900)*10000L+ - (((uint) start.tm_mon+1)*100+ - (uint) start.tm_mday))*(longlong) 1000000L+ - (longlong) ((ulong) ((uint) start.tm_hour)*10000L+ - (ulong) (((uint) start.tm_min)*100L+ - (uint) start.tm_sec))); /* For getdate */ - ltime.year= start.tm_year+1900; - ltime.month= start.tm_mon+1; - ltime.day= start.tm_mday; - ltime.hour= start.tm_hour; - ltime.minute= start.tm_min; - ltime.second= start.tm_sec; - ltime.second_part= 0; - ltime.neg= 0; + localtime_to_TIME(<ime, &start); ltime.time_type= TIMESTAMP_DATETIME; + + value= (longlong) TIME_to_ulonglong_datetime(<ime); make_datetime((DATE_TIME_FORMAT *) 0, <ime, &tmp); max_length= buff_length= tmp.length(); @@ -1463,10 +1447,10 @@ uint Item_func_date_format::format_length(const String *format) String *Item_func_date_format::val_str(String *str) { - DBUG_ASSERT(fixed == 1); String *format; TIME l_time; uint size; + DBUG_ASSERT(fixed == 1); if (!is_time_format) { @@ -1513,25 +1497,18 @@ null_date: String *Item_func_from_unixtime::val_str(String *str) { - DBUG_ASSERT(fixed == 1); - struct tm tm_tmp,*start; - time_t tmp=(time_t) args[0]->val_int(); + struct tm tm_tmp; + time_t tmp; TIME ltime; + DBUG_ASSERT(fixed == 1); + tmp= (time_t) args[0]->val_int(); if ((null_value=args[0]->null_value)) goto null_date; localtime_r(&tmp,&tm_tmp); - start=&tm_tmp; - - ltime.year= start->tm_year+1900; - ltime.month= start->tm_mon+1; - ltime.day= start->tm_mday; - ltime.hour= start->tm_hour; - ltime.minute= start->tm_min; - ltime.second= start->tm_sec; - ltime.second_part= 0; - ltime.neg=0; + + localtime_to_TIME(<ime, &tm_tmp); if (str->alloc(20*MY_CHARSET_BIN_MB_MAXLEN)) goto null_date; @@ -1546,19 +1523,17 @@ null_date: longlong Item_func_from_unixtime::val_int() { + TIME ltime; + struct tm tm_tmp; + time_t tmp; DBUG_ASSERT(fixed == 1); - time_t tmp=(time_t) (ulong) args[0]->val_int(); + + tmp= (time_t) (ulong) args[0]->val_int(); if ((null_value=args[0]->null_value)) return 0; - struct tm tm_tmp,*start; localtime_r(&tmp,&tm_tmp); - start= &tm_tmp; - return ((longlong) ((ulong) ((uint) start->tm_year+1900)*10000L+ - (((uint) start->tm_mon+1)*100+ - (uint) start->tm_mday))*LL(1000000)+ - (longlong) ((ulong) ((uint) start->tm_hour)*10000L+ - (ulong) (((uint) start->tm_min)*100L+ - (uint) start->tm_sec))); + localtime_to_TIME(<ime, &tm_tmp); + return (longlong) TIME_to_ulonglong_datetime(<ime); } bool Item_func_from_unixtime::get_date(TIME *ltime, @@ -1567,17 +1542,9 @@ bool Item_func_from_unixtime::get_date(TIME *ltime, time_t tmp=(time_t) (ulong) args[0]->val_int(); if ((null_value=args[0]->null_value)) return 1; - struct tm tm_tmp,*start; + struct tm tm_tmp; localtime_r(&tmp,&tm_tmp); - start= &tm_tmp; - ltime->year= start->tm_year+1900; - ltime->month= start->tm_mon+1; - ltime->day= start->tm_mday; - ltime->hour= start->tm_hour; - ltime->minute=start->tm_min; - ltime->second=start->tm_sec; - ltime->second_part=0; - ltime->neg=0; + localtime_to_TIME(ltime, &tm_tmp); return 0; } @@ -2054,7 +2021,7 @@ String *Item_date_typecast::val_str(String *str) if (!get_arg0_date(<ime,1) && !str->alloc(11)) { - make_date((DATE_TIME_FORMAT *) 0,<ime, str); + make_date((DATE_TIME_FORMAT *) 0, <ime, str); return str; } diff --git a/sql/key.cc b/sql/key.cc index a2c3b2c8989..9425a368669 100644 --- a/sql/key.cc +++ b/sql/key.cc @@ -156,9 +156,28 @@ void key_restore(TABLE *table,byte *key,uint idx,uint key_length) } /* key_restore */ - /* Compare if a key has changed */ +/* + Compare if a key has changed + + SYNOPSIS + key_cmp_if_same() + table TABLE + key key to compare to row + idx Index used + key_length Length of key + + NOTES + In theory we could just call field->cmp() for all field types, + but as we are only interested if a key has changed (not if the key is + larger or smaller than the previous value) we can do things a bit + faster by using memcmp() instead. + + RETURN + 0 If key is equal + 1 Key has changed +*/ -int key_cmp(TABLE *table,const byte *key,uint idx,uint key_length) +bool key_cmp_if_same(TABLE *table,const byte *key,uint idx,uint key_length) { uint length; KEY_PART_INFO *key_part; @@ -281,3 +300,56 @@ bool check_if_key_used(TABLE *table, uint idx, List<Item> &fields) return check_if_key_used(table, table->primary_key, fields); return 0; } + + +/* + Compare key in row to a given key + + SYNOPSIS + key_cmp() + key_part Key part handler + key Key to compare to value in table->record[0] + key_length length of 'key' + + RETURN + The return value is SIGN(key_in_row - range_key): + + 0 Key is equal to range or 'range' == 0 (no range) + -1 Key is less than range + 1 Key is larger than range +*/ + +int key_cmp(KEY_PART_INFO *key_part, const byte *key, uint key_length) +{ + uint store_length; + + for (const byte *end=key + key_length; + key < end; + key+= store_length, key_part++) + { + int cmp; + store_length= key_part->store_length; + if (key_part->null_bit) + { + /* This key part allows null values; NULL is lower than everything */ + register bool field_is_null= key_part->field->is_null(); + if (*key) // If range key is null + { + /* the range is expecting a null value */ + if (!field_is_null) + return 1; // Found key is > range + /* null -- exact match, go to next key part */ + continue; + } + else if (field_is_null) + return -1; // NULL is less than any value + key++; // Skip null byte + store_length--; + } + if ((cmp=key_part->field->key_cmp((byte*) key, key_part->length)) < 0) + return -1; + if (cmp > 0) + return 1; + } + return 0; // Keys are equal +} diff --git a/sql/lock.cc b/sql/lock.cc index 6d9deb8e4c6..723469b255e 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -240,7 +240,7 @@ void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock) { if (sql_lock->locks[i]->type >= TL_WRITE_ALLOW_READ) { - swap(THR_LOCK_DATA *,*lock,sql_lock->locks[i]); + swap_variables(THR_LOCK_DATA *, *lock, sql_lock->locks[i]); lock++; found++; } @@ -259,7 +259,7 @@ void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock) { if ((uint) sql_lock->table[i]->reginfo.lock_type >= TL_WRITE_ALLOW_READ) { - swap(TABLE *,*table,sql_lock->table[i]); + swap_variables(TABLE *, *table, sql_lock->table[i]); table++; found++; } diff --git a/sql/log_event.cc b/sql/log_event.cc index f0274405ba8..b8d80883451 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -1507,7 +1507,9 @@ end: TEMPORARY TABLE don't suffer from these assignments to 0 as DROP TEMPORARY TABLE uses the db.table syntax). */ - thd->db= thd->query= thd->catalog =0; + thd->db= 0; // prevent db from being freed + thd->query= 0; // just to be sure + thd->query_length= 0; VOID(pthread_mutex_unlock(&LOCK_thread_count)); // assume no convert for next query unless set explictly #ifdef TO_BE_REMOVED @@ -2399,6 +2401,7 @@ int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli, thd->db= (char*) rewrite_db(db); DBUG_ASSERT(thd->query == 0); thd->query= 0; // Should not be needed + thd->query_length= 0; // Should not be needed thd->query_error= 0; clear_all_errors(thd, rli); if (!use_rli_only_for_errors) diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 9cd4c65118d..f3a55e0cad7 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -323,6 +323,14 @@ void debug_sync_point(const char* lock_name, uint lock_timeout); #define WEEK_MONDAY_FIRST 1 #define WEEK_YEAR 2 #define WEEK_FIRST_WEEKDAY 4 +/* + Required buffer length for make_date, make_time, make_datetime + and TIME_to_string functions. Note, that the caller is still + responsible to check that given TIME structure has values + in valid ranges, otherwise size of the buffer could be not + enough. +*/ +#define MAX_DATE_REP_LENGTH 30 struct st_table; class THD; @@ -559,13 +567,10 @@ int mysql_alter_table(THD *thd, char *new_db, char *new_name, HA_CREATE_INFO *create_info, TABLE_LIST *table_list, List<create_field> &fields, - List<Key> &keys,List<Alter_drop> &drop_list, - List<Alter_column> &alter_list, - uint order_num, ORDER *order, uint alter_flags, + List<Key> &keys, + uint order_num, ORDER *order, enum enum_duplicates handle_duplicates, - enum enum_enable_or_disable keys_onoff=LEAVE_AS_IS, - enum tablespace_op_type tablespace_op=NO_TABLESPACE_OP, - bool simple_alter=0); + ALTER_INFO *alter_info); int mysql_create_like_table(THD *thd, TABLE_LIST *table, HA_CREATE_INFO *create_info, Table_ident *src_table); @@ -576,7 +581,7 @@ bool mysql_rename_table(enum db_type base, const char * new_name); int mysql_create_index(THD *thd, TABLE_LIST *table_list, List<Key> &keys); int mysql_drop_index(THD *thd, TABLE_LIST *table_list, - List<Alter_drop> &drop_list); + ALTER_INFO *alter_info); int mysql_prepare_update(THD *thd, TABLE_LIST *table_list, TABLE_LIST *update_table_list, Item **conds, uint order_num, ORDER *order); @@ -797,11 +802,12 @@ void mysql_print_status(THD *thd); int find_ref_key(TABLE *form,Field *field, uint *offset); void key_copy(byte *key,TABLE *form,uint index,uint key_length); void key_restore(TABLE *form,byte *key,uint index,uint key_length); -int key_cmp(TABLE *form,const byte *key,uint index,uint key_length); +bool key_cmp_if_same(TABLE *form,const byte *key,uint index,uint key_length); void key_unpack(String *to,TABLE *form,uint index); bool check_if_key_used(TABLE *table, uint idx, List<Item> &fields); -bool init_errmessage(void); +int key_cmp(KEY_PART_INFO *key_part, const byte *key, uint key_length); +bool init_errmessage(void); void sql_perror(const char *message); void sql_print_error(const char *format,...) __attribute__ ((format (printf, 1, 2))); @@ -900,7 +906,7 @@ extern uint protocol_version, mysqld_port, dropping_tables; extern uint delay_key_write_options, lower_case_table_names; extern bool opt_endinfo, using_udf_functions, locked_in_memory; extern bool opt_using_transactions, mysql_embedded; -extern bool using_update_log, opt_large_files; +extern bool using_update_log, opt_large_files, server_id_supplied; extern bool opt_log, opt_update_log, opt_bin_log, opt_slow_log, opt_error_log; extern bool opt_disable_networking, opt_skip_show_db; extern bool volatile abort_loop, shutdown_in_progress, grant_option; @@ -1041,9 +1047,17 @@ const char *get_date_time_format_str(KNOWN_DATE_TIME_FORMAT *format, timestamp_type type); extern bool make_date_time(DATE_TIME_FORMAT *format, TIME *l_time, timestamp_type type, String *str); -extern void make_time(DATE_TIME_FORMAT *format, TIME *l_time, String *str); -void make_date(DATE_TIME_FORMAT *format, TIME *l_time, String *str); -void make_datetime(DATE_TIME_FORMAT *format, TIME *l_time, String *str); +void make_datetime(const DATE_TIME_FORMAT *format, const TIME *l_time, + String *str); +void make_date(const DATE_TIME_FORMAT *format, const TIME *l_time, + String *str); +void make_time(const DATE_TIME_FORMAT *format, const TIME *l_time, + String *str); +void TIME_to_string(const TIME *time, String *str); +ulonglong TIME_to_ulonglong_datetime(const TIME *time); +ulonglong TIME_to_ulonglong_date(const TIME *time); +ulonglong TIME_to_ulonglong_time(const TIME *time); +ulonglong TIME_to_ulonglong(const TIME *time); int test_if_number(char *str,int *res,bool allow_wildcards); void change_byte(byte *,uint,char,char); diff --git a/sql/mysqld.cc b/sql/mysqld.cc index a6c25f7f1c0..26de814ab33 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -22,6 +22,7 @@ #include "sql_repl.h" #include "repl_failsafe.h" #include "stacktrace.h" +#include "mysqld_suffix.h" #include "mysys_err.h" #ifdef HAVE_BERKELEY_DB #include "ha_berkeley.h" @@ -140,10 +141,27 @@ int deny_severity = LOG_WARNING; #include <nks/vm.h> #include <library.h> #include <monitor.h> - +#include <zOmni.h> //For NEB +#include <neb.h> //For NEB +#include <nebpub.h> //For NEB +#include <zEvent.h> //For NSS event structures +#include <zPublics.h> + +static void *neb_consumer_id= NULL; //For storing NEB consumer id +static char datavolname[256]= {0}; +static VolumeID_t datavolid; static event_handle_t eh; static Report_t ref; +static void *refneb= NULL; +static int volumeid= -1; + + /* NEB event callback */ +unsigned long neb_event_callback(struct EventBlock *eblock); +static void registerwithneb(); +static void getvolumename(); +static void getvolumeID(BYTE *volumeName); #endif /* __NETWARE__ */ + #ifdef _AIX41 int initgroups(const char *,unsigned int); @@ -194,22 +212,6 @@ inline void reset_floating_point_exceptions() extern "C" int gethostname(char *name, int namelen); #endif -/* Set prefix for windows binary */ -#ifdef __WIN__ -#undef MYSQL_SERVER_SUFFIX -#ifdef __NT__ -#if defined(HAVE_BERKELEY_DB) -#define MYSQL_SERVER_SUFFIX "-max-nt" -#else -#define MYSQL_SERVER_SUFFIX "-nt" -#endif /* ...DB */ -#elif defined(HAVE_BERKELEY_DB) -#define MYSQL_SERVER_SUFFIX "-max" -#else -#define MYSQL_SERVER_SUFFIX "" -#endif /* __NT__ */ -#endif /* __WIN__ */ - /* Constants */ @@ -339,7 +341,7 @@ const char *opt_date_time_formats[3]; char *language_ptr, *default_collation_name, *default_character_set_name; char mysql_data_home_buff[2], *mysql_data_home=mysql_real_data_home; -char server_version[SERVER_VERSION_LENGTH]=MYSQL_SERVER_VERSION; +char server_version[SERVER_VERSION_LENGTH]; char *mysqld_unix_port, *opt_mysql_tmpdir; char *my_bind_addr_str; const char **errmesg; /* Error messages */ @@ -493,6 +495,7 @@ static void start_signal_handler(void); extern "C" pthread_handler_decl(signal_hand, arg); static void mysql_init_variables(void); static void get_options(int argc,char **argv); +static void set_server_version(void); static int init_thread_environment(); static char *get_relative_path(const char *path); static void fix_paths(void); @@ -1458,6 +1461,7 @@ static void start_signal_handler(void) static void check_data_home(const char *path) {} + #elif defined(__NETWARE__) // down server event callback @@ -1466,27 +1470,196 @@ void mysql_down_server_cb(void *, void *) kill_server(0); } + // destroy callback resources void mysql_cb_destroy(void *) { UnRegisterEventNotification(eh); // cleanup down event notification NX_UNWRAP_INTERFACE(ref); + + /* Deregister NSS volume deactivation event */ + NX_UNWRAP_INTERFACE(refneb); + if (neb_consumer_id) + UnRegisterConsumer(neb_consumer_id, NULL); } + // initialize callbacks void mysql_cb_init() { // register for down server event void *handle = getnlmhandle(); - rtag_t rt = AllocateResourceTag(handle, "MySQL Down Server Callback", - EventSignature); + rtag_t rt= AllocateResourceTag(handle, "MySQL Down Server Callback", + EventSignature); NX_WRAP_INTERFACE((void *)mysql_down_server_cb, 2, (void **)&ref); - eh = RegisterForEventNotification(rt, EVENT_DOWN_SERVER, - EVENT_PRIORITY_APPLICATION, - NULL, ref, NULL); + eh= RegisterForEventNotification(rt, EVENT_PRE_DOWN_SERVER, + EVENT_PRIORITY_APPLICATION, + NULL, ref, NULL); + + /* + Register for volume deactivation event + Wrap the callback function, as it is called by non-LibC thread + */ + (void)NX_WRAP_INTERFACE(neb_event_callback, 1, &refneb); + registerwithneb(); + NXVmRegisterExitHandler(mysql_cb_destroy, NULL); // clean-up } + +/ *To get the name of the NetWare volume having MySQL data folder */ + +static void getvolumename() +{ + char *p; + /* + We assume that data path is already set. + If not it won't come here. Terminate after volume name + */ + if ((p= strchr(mysql_real_data_home, ':'))) + strmake(datavolname, mysql_real_data_home, + (uint) (p - mysql_real_data_home)); +} + + +/* + Registering with NEB for NSS Volume Deactivation event +*/ + +static void registerwithneb() +{ + + ConsumerRegistrationInfo reg_info; + + /* Clear NEB registration structure */ + bzero((char*) ®_info, sizeof(struct ConsumerRegistrationInfo)); + + /* Fill the NEB consumer information structure */ + reg_info.CRIVersion= 1; // NEB version + /* NEB Consumer name */ + reg_info.CRIConsumerName= (BYTE *) "MySQL Database Server"; + /* Event of interest */ + reg_info.CRIEventName= (BYTE *) "NSS.ChangeVolState.Enter"; + reg_info.CRIUserParameter= NULL; // Consumer Info + reg_info.CRIEventFlags= 0; // Event flags + /* Consumer NLM handle */ + reg_info.CRIOwnerID= (LoadDefinitionStructure *)getnlmhandle(); + reg_info.CRIConsumerESR= NULL; // No consumer ESR required + reg_info.CRISecurityToken= 0; // No security token for the event + reg_info.CRIConsumerFlags= 0; // SMP_ENABLED_BIT; + reg_info.CRIFilterName= 0; // No event filtering + reg_info.CRIFilterDataLength= 0; // No filtering data + reg_info.CRIFilterData= 0; // No filtering data + /* Callback function for the event */ + (void *)reg_info.CRIConsumerCallback= (void *) refneb; + reg_info.CRIOrder= 0; // Event callback order + reg_info.CRIConsumerType= CHECK_CONSUMER; // Consumer type + + /* Register for the event with NEB */ + if (RegisterConsumer(®_info)) + { + consoleprintf("Failed to register for NSS Volume Deactivation event \n"); + return; + } + /* This ID is required for deregistration */ + neb_consumer_id= reg_info.CRIConsumerID; + + /* Get MySQL data volume name, stored in global variable datavolname */ + getvolumename(); + + /* + Get the NSS volume ID of the MySQL Data volume. + Volume ID is stored in a global variable + */ + getvolumeID((BYTE*) datavolname); +} + + +/* + Callback for NSS Volume Deactivation event +*/ +ulong neb_event_callback(struct EventBlock *eblock) +{ + EventChangeVolStateEnter_s *voldata; + voldata= (EventChangeVolStateEnter_s *)eblock->EBEventData; + + /* Deactivation of a volume */ + if ((voldata->oldState == 6 && voldata->newState == 2)) + { + /* + Ensure that we bring down MySQL server only for MySQL data + volume deactivation + */ + if (!memcmp(&voldata->volID, &datavolid, sizeof(VolumeID_t))) + { + consoleprintf("MySQL data volume is deactivated, shutting down MySQL Server \n"); + kill_server(0); + } + } + return 0; +} + + +/* + Function to get NSS volume ID of the MySQL data +*/ + +#define ADMIN_VOL_PATH "_ADMIN:/Volumes/" + +staticvoid getvolumeID(BYTE *volumeName) +{ + char path[zMAX_FULL_NAME]; + Key_t rootKey= 0, fileKey= 0; + QUAD getInfoMask; + zInfo_s info; + STATUS status; + + /* Get the root key */ + if ((status= zRootKey(0, &rootKey)) != zOK) + { + consoleprintf("\nGetNSSVolumeProperties - Failed to get root key, status: %d\n.", (int) status); + goto exit; + } + + /* + Get the file key. This is the key to the volume object in the + NSS admin volumes directory. + */ + + strxmov(path, (const char *) ADMIN_VOL_PATH, (const char *) volumeName, + NullS); + if ((status= zOpen(rootKey, zNSS_TASK, zNSPACE_LONG|zMODE_UTF8, + (BYTE *) path, zRR_READ_ACCESS, &fileKey)) != zOK) + { + consoleprintf("\nGetNSSVolumeProperties - Failed to get file, status: %d\n.", (int) status); + goto exit; + } + + getInfoMask= zGET_IDS | zGET_VOLUME_INFO ; + if ((status= zGetInfo(fileKey, getInfoMask, sizeof(info), + zINFO_VERSION_A, &info)) != zOK) + { + consoleprintf("\nGetNSSVolumeProperties - Failed in zGetInfo, status: %d\n.", (int) status); + goto exit; + } + + /* Copy the data to global variable */ + datavolid.timeLow= info.vol.volumeID.timeLow; + datavolid.timeMid= info.vol.volumeID.timeMid; + datavolid.timeHighAndVersion= info.vol.volumeID.timeHighAndVersion; + datavolid.clockSeqHighAndReserved= info.vol.volumeID.clockSeqHighAndReserved; + datavolid.clockSeqLow= info.vol.volumeID.clockSeqLow; + /* This is guranteed to be 6-byte length (but sizeof() would be better) */ + memcpy(datavolid.node, info.vol.volumeID.node, (unsigned int) 6); + +exit: + if (rootKey) + zClose(rootKey); + if (fileKey) + zClose(fileKey); +} + + static void init_signals(void) { int signals[] = {SIGINT,SIGILL,SIGFPE,SIGSEGV,SIGTERM,SIGABRT}; @@ -1496,6 +1669,7 @@ static void init_signals(void) mysql_cb_init(); // initialize callbacks } + static void start_signal_handler(void) { // Save vm id of this process @@ -1505,7 +1679,12 @@ static void start_signal_handler(void) } -/* Warn if the data is on a Traditional volume */ +/* + Warn if the data is on a Traditional volume + + NOTE + Already done by mysqld_safe +*/ static void check_data_home(const char *path) { @@ -1955,6 +2134,7 @@ extern "C" pthread_handler_decl(handle_shutdown,arg) return 0; } + int STDCALL handle_kill(ulong ctrl_type) { if (ctrl_type == CTRL_CLOSE_EVENT || @@ -2115,18 +2295,11 @@ static int init_common_variables(const char *conf_file_name, int argc, strmake(pidfile_name, glob_hostname, sizeof(pidfile_name)-5); strmov(fn_ext(pidfile_name),".pid"); // Add proper extension -#ifndef DBUG_OFF - if (!*(MYSQL_SERVER_SUFFIX)) - strmov(strend(server_version),"-debug"); - else -#endif - strmov(strend(server_version),MYSQL_SERVER_SUFFIX); - load_defaults(conf_file_name, groups, &argc, &argv); defaults_argv=argv; get_options(argc,argv); - if (opt_log || opt_update_log || opt_slow_log || opt_bin_log) - strcat(server_version,"-log"); + set_server_version(); + DBUG_PRINT("info",("%s Ver %s for %s on %s\n",my_progname, server_version, SYSTEM_TYPE,MACHINE_TYPE)); @@ -2657,14 +2830,14 @@ You should consider changing lower_case_table_names to 1 or 2", switch (server_id) { case 1: sql_print_error("\ -Warning: You have enabled the binary log, but you haven't set server-id:\n\ -Updates will be logged to the binary log, but connections to slaves will\n\ -not be accepted."); +Warning: You have enabled the binary log, but you haven't set server-id to \ +a non-zero value: we force server id to 1; updates will be logged to the \ +binary log, but connections from slaves will not be accepted."); break; case 2: sql_print_error("\ -Warning: You should set server-id to a non-0 value if master_host is set.\n\ -The server will not act as a slave."); +Warning: You should set server-id to a non-0 value if master_host is set; \ +we force server id to 2, but this MySQL server will not act as a slave."); break; } #endif @@ -5015,6 +5188,7 @@ struct show_var_st status_vars[]= { static void print_version(void) { + set_server_version(); printf("%s Ver %s for %s on %s (%s)\n",my_progname, server_version,SYSTEM_TYPE,MACHINE_TYPE, MYSQL_COMPILATION_COMMENT); } @@ -5531,6 +5705,10 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), opt_specialflag|=SPECIAL_NO_RESOLVE; break; case (int) OPT_SKIP_NETWORKING: +#if defined(__NETWARE__) + sql_perror("Can't start server: skip-networking option is currently not supported on NetWare"); + exit(1); +#endif opt_disable_networking=1; mysqld_port=0; break; @@ -5896,6 +6074,29 @@ static void get_options(int argc,char **argv) } +/* + Create version name for running mysqld version + We automaticly add suffixes -debug, -embedded and -log to the version + name to make the version more descriptive. + (MYSQL_SERVER_SUFFIX is set by the compilation environment) +*/ + +static void set_server_version(void) +{ + char *end= strxmov(server_version, MYSQL_SERVER_VERSION, + MYSQL_SERVER_SUFFIX_STR, NullS); +#ifdef EMBEDDED_LIBRARY + end= strmov(end, "-embedded"); +#endif +#ifndef DBUG_OFF + if (!strstr(MYSQL_SERVER_SUFFIX_STR, "-debug")) + end= strmov(end, "-debug"); +#endif + if (opt_log || opt_update_log || opt_slow_log || opt_bin_log) + strmov(end, "-log"); // This may slow down system +} + + static char *get_relative_path(const char *path) { if (test_if_hard_path(path) && diff --git a/sql/mysqld_suffix.h b/sql/mysqld_suffix.h new file mode 100644 index 00000000000..405c5d855b7 --- /dev/null +++ b/sql/mysqld_suffix.h @@ -0,0 +1,27 @@ +/* Copyright (C) 2000-2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* + Set MYSQL_SERVER_SUFFIX_STR + The following code is quite ugly as there is no portable way to easily set a + string to the value of a macro +*/ + +#ifdef MYSQL_SERVER_SUFFIX +#define MYSQL_SERVER_SUFFIX_STR STRINGIFY_ARG(MYSQL_SERVER_SUFFIX) +#else +#define MYSQL_SERVER_SUFFIX_STR MYSQL_SERVER_SUFFIX_DEF +#endif diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 28b9cd79fdd..b29fc30b9c5 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -2220,7 +2220,7 @@ key_and(SEL_ARG *key1,SEL_ARG *key2,uint clone_flag) { if (key1->part > key2->part) { - swap(SEL_ARG *,key1,key2); + swap_variables(SEL_ARG *, key1, key2); clone_flag=swap_clone_flag(clone_flag); } // key1->part < key2->part @@ -2236,7 +2236,7 @@ key_and(SEL_ARG *key1,SEL_ARG *key2,uint clone_flag) key2->type != SEL_ARG::MAYBE_KEY) || key1->type == SEL_ARG::MAYBE_KEY) { // Put simple key in key2 - swap(SEL_ARG *,key1,key2); + swap_variables(SEL_ARG *, key1, key2); clone_flag=swap_clone_flag(clone_flag); } @@ -2378,7 +2378,7 @@ key_or(SEL_ARG *key1,SEL_ARG *key2) { if (key2->use_count == 0 || key1->elements > key2->elements) { - swap(SEL_ARG *,key1,key2); + swap_variables(SEL_ARG *,key1,key2); } else if (!(key1=key1->clone_tree())) return 0; // OOM @@ -2619,8 +2619,8 @@ SEL_ARG * SEL_ARG::insert(SEL_ARG *key) { SEL_ARG *element,**par,*last_element; - LINT_INIT(par); LINT_INIT(last_element); + for (element= this; element != &null_element ; ) { last_element=element; @@ -3115,26 +3115,32 @@ check_quick_keys(PARAM *param,uint idx,SEL_ARG *key_tree, { if (tmp_min_flag & GEOM_FLAG) { - tmp= param->table->file-> - records_in_range((int) keynr, (byte*)(param->min_key), - min_key_length, - (ha_rkey_function)(tmp_min_flag ^ GEOM_FLAG), - (byte *)NullS, 0, HA_READ_KEY_EXACT); + key_range min_range; + min_range.key= (byte*) param->min_key; + min_range.length= min_key_length; + /* In this case tmp_min_flag contains the handler-read-function */ + min_range.flag= (ha_rkey_function) (tmp_min_flag ^ GEOM_FLAG); + + tmp= param->table->file->records_in_range(keynr, &min_range, + (key_range*) 0); } else { - tmp=param->table->file-> - records_in_range((int) keynr, - (byte*) (!min_key_length ? NullS : - param->min_key), - min_key_length, - tmp_min_flag & NEAR_MIN ? - HA_READ_AFTER_KEY : HA_READ_KEY_EXACT, - (byte*) (!max_key_length ? NullS : - param->max_key), - max_key_length, - (tmp_max_flag & NEAR_MAX ? - HA_READ_BEFORE_KEY : HA_READ_AFTER_KEY)); + key_range min_range, max_range; + + min_range.key= (byte*) param->min_key; + min_range.length= min_key_length; + min_range.flag= (tmp_min_flag & NEAR_MIN ? HA_READ_AFTER_KEY : + HA_READ_KEY_EXACT); + max_range.key= (byte*) param->max_key; + max_range.length= max_key_length; + max_range.flag= (tmp_max_flag & NEAR_MAX ? + HA_READ_BEFORE_KEY : HA_READ_AFTER_KEY); + tmp=param->table->file->records_in_range(keynr, + (min_key_length ? &min_range : + (key_range*) 0), + (max_key_length ? &max_range : + (key_range*) 0)); } } end: @@ -3563,7 +3569,7 @@ int QUICK_RANGE_SELECT::get_next() if (range) { // Already read through key - result= file->read_range_next(test(range->flag & EQ_RANGE)); + result= file->read_range_next(); if (result != HA_ERR_END_OF_FILE) DBUG_RETURN(result); } @@ -3594,6 +3600,7 @@ int QUICK_RANGE_SELECT::get_next() result= file->read_range_first(range->min_length ? &start_key : 0, range->max_length ? &end_key : 0, + test(range->flag & EQ_RANGE), sorted); if (range->flag == (UNIQUE_RANGE | EQ_RANGE)) range=0; // Stop searching @@ -3871,40 +3878,15 @@ int QUICK_RANGE_SELECT::cmp_next(QUICK_RANGE *range_arg) int QUICK_RANGE_SELECT::cmp_prev(QUICK_RANGE *range_arg) { + int cmp; if (range_arg->flag & NO_MIN_RANGE) return 0; /* key can't be to small */ - KEY_PART *key_part = key_parts; - uint store_length; - - for (char *key = range_arg->min_key, *end = key + range_arg->min_length; - key < end; - key += store_length, key_part++) - { - int cmp; - store_length= key_part->store_length; - if (key_part->null_bit) - { - // this key part allows null values; NULL is lower than everything else - if (*key) - { - // the range is expecting a null value - if (!key_part->field->is_null()) - return 0; // not null -- still inside the range - continue; // null -- exact match, go to next key part - } - else if (key_part->field->is_null()) - return 1; // null -- outside the range - key++; - store_length--; - } - if ((cmp = key_part->field->key_cmp((byte*) key, - key_part->length)) > 0) - return 0; - if (cmp < 0) - return 1; - } - return (range_arg->flag & NEAR_MIN) ? 1 : 0; // Exact match + cmp= key_cmp(key_part_info, (byte*) range_arg->min_key, + range_arg->min_length); + if (cmp > 0 || cmp == 0 && !(range_arg->flag & NEAR_MIN)) + return 0; + return 1; // outside of range } diff --git a/sql/opt_range.h b/sql/opt_range.h index 30e0fcd7be5..4763600963d 100644 --- a/sql/opt_range.h +++ b/sql/opt_range.h @@ -148,6 +148,7 @@ protected: QUICK_RANGE *range; MEM_ROOT alloc; KEY_PART *key_parts; + KEY_PART_INFO *key_part_info; int cmp_next(QUICK_RANGE *range); int cmp_prev(QUICK_RANGE *range); bool row_in_ranges(); diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index 4fdcd093132..8c1cd9ce1cb 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -716,7 +716,7 @@ static bool find_key_for_maxmin(bool max_fl, TABLE_REF *ref, static int reckey_in_range(bool max_fl, TABLE_REF *ref, Field* field, COND *cond, uint range_fl, uint prefix_len) { - if (key_cmp(field->table, ref->key_buff, ref->key, prefix_len)) + if (key_cmp_if_same(field->table, ref->key_buff, ref->key, prefix_len)) return 1; if (!cond || (range_fl & (max_fl ? NO_MIN_RANGE : NO_MAX_RANGE))) return 0; diff --git a/sql/protocol.cc b/sql/protocol.cc index 4d0859aed22..b2334b0f356 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -468,6 +468,7 @@ void Protocol::init(THD *thd_arg) { thd=thd_arg; packet= &thd->packet; + convert= &thd->convert_buffer; #ifndef DEBUG_OFF field_types= 0; #endif @@ -704,6 +705,26 @@ bool Protocol_simple::store_null() #endif +/* + Auxilary function to convert string to the given character set + and store in network buffer. +*/ + +bool Protocol::store_string_aux(const char *from, uint length, + CHARSET_INFO *fromcs, CHARSET_INFO *tocs) +{ + /* 'tocs' is set 0 when client issues SET character_set_results=NULL */ + if (tocs && !my_charset_same(fromcs, tocs) && + fromcs != &my_charset_bin && + tocs != &my_charset_bin) + { + return convert->copy(from, length, fromcs, tocs) || + net_store_data(convert->ptr(), convert->length()); + } + return net_store_data(from, length); +} + + bool Protocol_simple::store(const char *from, uint length, CHARSET_INFO *fromcs, CHARSET_INFO *tocs) { @@ -714,15 +735,7 @@ bool Protocol_simple::store(const char *from, uint length, field_types[field_pos] <= MYSQL_TYPE_GEOMETRY)); field_pos++; #endif - if (tocs && !my_charset_same(fromcs, tocs) && - (fromcs != &my_charset_bin) && - (tocs != &my_charset_bin)) - { - convert.copy(from, length, fromcs, tocs); - return net_store_data(convert.ptr(), convert.length()); - } - else - return net_store_data(from, length); + return store_string_aux(from, length, fromcs, tocs); } @@ -737,15 +750,7 @@ bool Protocol_simple::store(const char *from, uint length, field_types[field_pos] <= MYSQL_TYPE_GEOMETRY)); field_pos++; #endif - if (tocs && !my_charset_same(fromcs, tocs) && - (fromcs != &my_charset_bin) && - (tocs != &my_charset_bin)) - { - convert.copy(from, length, fromcs, tocs); - return net_store_data(convert.ptr(), convert.length()); - } - else - return net_store_data(from, length); + return store_string_aux(from, length, fromcs, tocs); } @@ -839,15 +844,7 @@ bool Protocol_simple::store(Field *field) CHARSET_INFO *tocs= this->thd->variables.character_set_results; field->val_str(&str); - if (tocs && !my_charset_same(field->charset(), tocs) && - (field->charset() != &my_charset_bin) && - (tocs != &my_charset_bin)) - { - convert.copy(str.ptr(), str.length(), str.charset(), tocs); - return net_store_data(convert.ptr(), convert.length()); - } - else - return net_store_data(str.ptr(), str.length()); + return store_string_aux(str.ptr(), str.length(), str.charset(), tocs); } @@ -960,8 +957,9 @@ void Protocol_prep::prepare_for_resend() } -bool Protocol_prep::store(const char *from,uint length, CHARSET_INFO *cs) +bool Protocol_prep::store(const char *from, uint length, CHARSET_INFO *fromcs) { + CHARSET_INFO *tocs= thd->variables.character_set_results; #ifndef DEBUG_OFF DBUG_ASSERT(field_types == 0 || field_types[field_pos] == MYSQL_TYPE_DECIMAL || @@ -969,7 +967,7 @@ bool Protocol_prep::store(const char *from,uint length, CHARSET_INFO *cs) field_types[field_pos] <= MYSQL_TYPE_GEOMETRY)); #endif field_pos++; - return net_store_data(from, length); + return store_string_aux(from, length, fromcs, tocs); } bool Protocol_prep::store(const char *from,uint length, @@ -982,7 +980,7 @@ bool Protocol_prep::store(const char *from,uint length, field_types[field_pos] <= MYSQL_TYPE_GEOMETRY)); #endif field_pos++; - return net_store_data(from, length); + return store_string_aux(from, length, fromcs, tocs); } bool Protocol_prep::store_null() diff --git a/sql/protocol.h b/sql/protocol.h index 52b78a749b3..25efecab712 100644 --- a/sql/protocol.h +++ b/sql/protocol.h @@ -30,7 +30,7 @@ class Protocol protected: THD *thd; String *packet; - String convert; + String *convert; uint field_pos; #ifndef DEBUG_OFF enum enum_field_types *field_types; @@ -42,6 +42,8 @@ protected: MYSQL_FIELD *next_mysql_field; MEM_ROOT *alloc; #endif + bool store_string_aux(const char *from, uint length, + CHARSET_INFO *fromcs, CHARSET_INFO *tocs); public: Protocol() {} Protocol(THD *thd_arg) { init(thd_arg); } diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc index f254ffb3df3..2a5381ae478 100644 --- a/sql/repl_failsafe.cc +++ b/sql/repl_failsafe.cc @@ -915,12 +915,14 @@ int load_master_data(THD* thd) setting active_mi, because init_master_info() sets active_mi with defaults. */ + int error; + if (init_master_info(active_mi, master_info_file, relay_log_info_file, 0)) send_error(thd, ER_MASTER_INFO); strmake(active_mi->master_log_name, row[0], sizeof(active_mi->master_log_name)); - active_mi->master_log_pos = strtoull(row[1], (char**) 0, 10); + active_mi->master_log_pos= my_strtoll10(row[1], (char**) 0, &error); /* at least in recent versions, the condition below should be false */ if (active_mi->master_log_pos < BIN_LOG_HEADER_SIZE) active_mi->master_log_pos = BIN_LOG_HEADER_SIZE; diff --git a/sql/set_var.cc b/sql/set_var.cc index babf62a1b62..811c700d527 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -108,6 +108,7 @@ static void fix_max_connections(THD *thd, enum_var_type type); static int check_max_delayed_threads(THD *thd, set_var *var); static void fix_thd_mem_root(THD *thd, enum_var_type type); static void fix_trans_mem_root(THD *thd, enum_var_type type); +static void fix_server_id(THD *thd, enum_var_type type); static KEY_CACHE *create_key_cache(const char *name, uint length); void fix_sql_mode_var(THD *thd, enum_var_type type); static byte *get_error_count(THD *thd); @@ -312,7 +313,7 @@ sys_query_cache_wlock_invalidate("query_cache_wlock_invalidate", &SV::query_cache_wlock_invalidate); #endif /* HAVE_QUERY_CACHE */ sys_var_bool_ptr sys_secure_auth("secure_auth", &opt_secure_auth); -sys_var_long_ptr sys_server_id("server_id",&server_id); +sys_var_long_ptr sys_server_id("server_id", &server_id, fix_server_id); sys_var_bool_ptr sys_slave_compressed_protocol("slave_compressed_protocol", &opt_slave_compressed_protocol); #ifdef HAVE_REPLICATION @@ -446,9 +447,7 @@ sys_var_thd_ulong sys_group_concat_max_len("group_concat_max_len", sys_var_const_str sys_os("version_compile_os", SYSTEM_TYPE); /* Global read-only variable describing server license */ -sys_var_const_str sys_license("license", LICENSE); - - +sys_var_const_str sys_license("license", STRINGIFY_ARG(LICENSE)); /* @@ -710,7 +709,7 @@ struct show_var_st init_vars[]= { {sys_log_warnings.name, (char*) &sys_log_warnings, SHOW_SYS}, {sys_long_query_time.name, (char*) &sys_long_query_time, SHOW_SYS}, {sys_low_priority_updates.name, (char*) &sys_low_priority_updates, SHOW_SYS}, - {"lower_case_file_system", (char*) &lower_case_file_system, SHOW_BOOL}, + {"lower_case_file_system", (char*) &lower_case_file_system, SHOW_MY_BOOL}, {"lower_case_table_names", (char*) &lower_case_table_names, SHOW_INT}, {sys_max_allowed_packet.name,(char*) &sys_max_allowed_packet, SHOW_SYS}, {sys_max_binlog_cache_size.name,(char*) &sys_max_binlog_cache_size, SHOW_SYS}, @@ -1145,6 +1144,13 @@ static void fix_trans_mem_root(THD *thd, enum_var_type type) thd->variables.trans_prealloc_size); } + +static void fix_server_id(THD *thd, enum_var_type type) +{ + server_id_supplied = 1; +} + + bool sys_var_long_ptr::update(THD *thd, set_var *var) { ulonglong tmp= var->save_result.ulonglong_value; @@ -1304,11 +1310,11 @@ bool sys_var_thd_ulonglong::update(THD *thd, set_var *var) { ulonglong tmp= var->save_result.ulonglong_value; - if ((ulonglong) tmp > max_system_variables.*offset) + if (tmp > max_system_variables.*offset) tmp= max_system_variables.*offset; if (option_limits) - tmp= (ulong) getopt_ull_limit_value(tmp, option_limits); + tmp= getopt_ull_limit_value(tmp, option_limits); if (var->type == OPT_GLOBAL) { /* Lock is needed to make things safe on 32 bit systems */ diff --git a/sql/slave.cc b/sql/slave.cc index f588e034a10..d652ad0e664 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -165,13 +165,11 @@ int init_slave() goto err; } - /* - make sure slave thread gets started if server_id is set, - valid master.info is present, and master_host has not been specified - */ if (server_id && !master_host && active_mi->host[0]) master_host= active_mi->host; + /* If server id is not set, start_slave_thread() will say it */ + if (master_host && !opt_skip_slave_start) { if (start_slave_threads(1 /* need mutex */, @@ -2694,9 +2692,15 @@ static int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type) set_slave_thread_options(thd); /* It's nonsense to constrain the slave threads with max_join_size; if a - query succeeded on master, we HAVE to execute it. + query succeeded on master, we HAVE to execute it. So set + OPTION_BIG_SELECTS. Setting max_join_size to HA_POS_ERROR is not enough + (and it's not needed if we have OPTION_BIG_SELECTS) because an INSERT + SELECT examining more than 4 billion rows would still fail (yes, because + when max_join_size is 4G, OPTION_BIG_SELECTS is automatically set, but + only for client threads. */ - thd->variables.max_join_size= HA_POS_ERROR; + thd->options = ((opt_log_slave_updates) ? OPTION_BIN_LOG:0) | + OPTION_AUTO_IS_NULL | OPTION_BIG_SELECTS; thd->client_capabilities = CLIENT_LOCAL_FILES; thd->real_id=pthread_self(); pthread_mutex_lock(&LOCK_thread_count); @@ -3403,6 +3407,7 @@ err: IO_RPL_LOG_NAME, llstr(mi->master_log_pos,llbuff)); VOID(pthread_mutex_lock(&LOCK_thread_count)); thd->query = thd->db = 0; // extra safety + thd->query_length = 0; VOID(pthread_mutex_unlock(&LOCK_thread_count)); if (mysql) { @@ -3599,6 +3604,7 @@ the slave SQL thread with \"SLAVE START\". We stopped at log \ variables is supposed to set them to 0 before terminating)). */ thd->query= thd->db= thd->catalog= 0; + thd->query_length = 0; VOID(pthread_mutex_unlock(&LOCK_thread_count)); thd->proc_info = "Waiting for slave mutex on exit"; pthread_mutex_lock(&rli->run_lock); diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index b532da9006e..dfd54d66fa0 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -1033,7 +1033,7 @@ ulong acl_get(const char *host, const char *ip, end=strmov((tmp_db=strmov(strmov(key, ip ? ip : "")+1,user)+1),db); if (lower_case_table_names) { - my_casedn_str(&my_charset_latin1, tmp_db); + my_casedn_str(files_charset_info, tmp_db); db=tmp_db; } key_length=(uint) (end-key); @@ -1812,8 +1812,8 @@ GRANT_TABLE::GRANT_TABLE(const char *h, const char *d,const char *u, tname= strdup_root(&memex,t); if (lower_case_table_names) { - my_casedn_str(&my_charset_latin1, db); - my_casedn_str(&my_charset_latin1, tname); + my_casedn_str(files_charset_info, db); + my_casedn_str(files_charset_info, tname); } key_length =(uint) strlen(d)+(uint) strlen(u)+(uint) strlen(t)+3; hash_key = (char*) alloc_root(&memex,key_length); @@ -1847,8 +1847,8 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs) } if (lower_case_table_names) { - my_casedn_str(&my_charset_latin1, db); - my_casedn_str(&my_charset_latin1, tname); + my_casedn_str(files_charset_info, db); + my_casedn_str(files_charset_info, tname); } key_length = ((uint) strlen(db) + (uint) strlen(user) + (uint) strlen(tname) + 3); @@ -1899,7 +1899,7 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs) } my_hash_insert(&hash_columns, (byte *) mem_check); } while (!col_privs->file->index_next(col_privs->record[0]) && - !key_cmp(col_privs,key,0,key_len)); + !key_cmp_if_same(col_privs,key,0,key_len)); } } @@ -1997,7 +1997,8 @@ static int replace_column_table(GRANT_TABLE *g_t, ulong privileges = xx->rights; bool old_row_exists=0; key_restore(table,key,0,key_length); - table->field[4]->store(xx->column.ptr(),xx->column.length(),&my_charset_latin1); + table->field[4]->store(xx->column.ptr(),xx->column.length(), + &my_charset_latin1); if (table->file->index_read(table->record[0],(byte*) table->field[0]->ptr, 0, HA_READ_KEY_EXACT)) @@ -2010,9 +2011,10 @@ static int replace_column_table(GRANT_TABLE *g_t, continue; /* purecov: inspected */ } old_row_exists = 0; - restore_record(table,default_values); // Get empty record + restore_record(table,default_values); // Get empty record key_restore(table,key,0,key_length); - table->field[4]->store(xx->column.ptr(),xx->column.length(), &my_charset_latin1); + table->field[4]->store(xx->column.ptr(),xx->column.length(), + &my_charset_latin1); } else { @@ -2120,7 +2122,7 @@ static int replace_column_table(GRANT_TABLE *g_t, } } } while (!table->file->index_next(table->record[0]) && - !key_cmp(table,key,0,key_length)); + !key_cmp_if_same(table,key,0,key_length)); } end: @@ -2493,7 +2495,7 @@ int mysql_grant(THD *thd, const char *db, List <LEX_USER> &list, if (lower_case_table_names && db) { strmov(tmp_db,db); - my_casedn_str(&my_charset_latin1, tmp_db); + my_casedn_str(files_charset_info, tmp_db); db=tmp_db; } @@ -3141,7 +3143,7 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) /* Add first global access grants */ { - String global(buff,sizeof(buff),&my_charset_latin1); + String global(buff,sizeof(buff),system_charset_info); global.length(0); global.append("GRANT ",6); @@ -3166,7 +3168,8 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) } } global.append (" ON *.* TO '",12); - global.append(lex_user->user.str,lex_user->user.length); + global.append(lex_user->user.str, lex_user->user.length, + system_charset_info); global.append ("'@'",3); global.append(lex_user->host.str,lex_user->host.length); global.append ('\''); @@ -3254,7 +3257,7 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) want_access=acl_db->access; if (want_access) { - String db(buff,sizeof(buff),&my_charset_latin1); + String db(buff,sizeof(buff),system_charset_info); db.length(0); db.append("GRANT ",6); @@ -3280,7 +3283,8 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) db.append (" ON ",4); append_identifier(thd, &db, acl_db->db, strlen(acl_db->db)); db.append (".* TO '",7); - db.append(lex_user->user.str,lex_user->user.length); + db.append(lex_user->user.str, lex_user->user.length, + system_charset_info); db.append ("'@'",3); db.append(lex_user->host.str, lex_user->host.length); db.append ('\''); @@ -3314,7 +3318,7 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) ulong table_access= grant_table->privs; if ((table_access | grant_table->cols) != 0) { - String global(buff,sizeof(buff),&my_charset_latin1); + String global(buff, sizeof(buff), system_charset_info); ulong test_access= (table_access | grant_table->cols) & ~GRANT_ACL; global.length(0); @@ -3368,7 +3372,8 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) else global.append(", ",2); global.append(grant_column->column, - grant_column->key_length); + grant_column->key_length, + system_charset_info); } } if (found_col) @@ -3384,7 +3389,8 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) append_identifier(thd, &global, grant_table->tname, strlen(grant_table->tname)); global.append(" TO '",5); - global.append(lex_user->user.str,lex_user->user.length); + global.append(lex_user->user.str, lex_user->user.length, + system_charset_info); global.append("'@'",3); global.append(lex_user->host.str,lex_user->host.length); global.append('\''); diff --git a/sql/sql_analyse.cc b/sql/sql_analyse.cc index 3c9563165fe..68f7d45e81c 100644 --- a/sql/sql_analyse.cc +++ b/sql/sql_analyse.cc @@ -187,7 +187,9 @@ bool test_if_number(NUM_INFO *info, const char *str, uint str_len) } if (str == end && info->integers) { - info->ullval = (ulonglong) strtoull(begin ,NULL, 10); + char *endpos= (char*) end; + int error; + info->ullval= (ulonglong) my_strtoll10(begin, &endpos, &error); if (info->integers == 1) return 0; // a single number can't be zerofill info->maybe_zerofill = 1; @@ -199,7 +201,9 @@ bool test_if_number(NUM_INFO *info, const char *str, uint str_len) return 0; if ((str + 1) == end) // number was something like '123[.eE]' { - info->ullval = (ulonglong) strtoull(begin, NULL, 10); + char *endpos= (char*) str; + int error; + info->ullval= (ulonglong) my_strtoll10(begin, &endpos, &error); return 1; } if (*str == 'e' || *str == 'E') // number may be something like '1e+50' @@ -218,7 +222,9 @@ bool test_if_number(NUM_INFO *info, const char *str, uint str_len) for (str++; *(end - 1) == '0'; end--); // jump over zeros at the end if (str == end) // number was something like '123.000' { - info->ullval = (ulonglong) strtoull(begin, NULL, 10); + char *endpos= (char*) str; + int error; + info->ullval= (ulonglong) my_strtoll10(begin, &endpos, &error); return 1; } for (; str != end && my_isdigit(system_charset_info,*str); str++) diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 1c8c310e36a..e4dc3065b86 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -2096,7 +2096,16 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter, if (field_name && item->type() == Item::FIELD_ITEM) { Item_field *item_field= (Item_field*) item; - if (!my_strcasecmp(system_charset_info, item_field->name, field_name)) + /* + In case of group_concat() with ORDER BY condition in the QUERY + item_field can be field of temporary table without item name + (if this field created from expression argument of group_concat()), + => we have to check presence of name before compare + */ + if (item_field->name && + (!my_strcasecmp(system_charset_info, item_field->name, field_name) || + !my_strcasecmp(system_charset_info, + item_field->field_name, field_name))) { if (!table_name) { diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 2cbc1072ebd..50bcca4b57e 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -1517,13 +1517,28 @@ ulong Query_cache::init_cache() VOID(hash_init(&queries, &my_charset_bin, def_query_hash_size, 0, 0, query_cache_query_get_key, 0, 0)); #ifndef FN_NO_CASE_SENCE + /* + If lower_case_table_names!=0 then db and table names are already + converted to lower case and we can use binary collation for their + comparison (no matter if file system case sensitive or not). + If we have case-sensitive file system (like on most Unixes) and + lower_case_table_names == 0 then we should distinguish my_table + and MY_TABLE cases and so again can use binary collation. + */ VOID(hash_init(&tables, &my_charset_bin, def_table_hash_size, 0, 0, query_cache_table_get_key, 0, 0)); #else - // windows, OS/2 or other case insensitive file names work around + /* + On windows, OS/2, MacOS X with HFS+ or any other case insensitive + file system if lower_case_table_names!=0 we have same situation as + in previous case, but if lower_case_table_names==0 then we should + not distinguish cases (to be compatible in behavior with underlaying + file system) and so should use case insensitive collation for + comparison. + */ VOID(hash_init(&tables, lower_case_table_names ? &my_charset_bin : - system_charset_info, + files_charset_info, def_table_hash_size, 0, 0,query_cache_table_get_key, 0, 0)); #endif diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 00a9c44d8d9..7a4f3dff845 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -82,6 +82,79 @@ extern "C" void free_user_var(user_var_entry *entry) } +bool key_part_spec::operator==(const key_part_spec& other) const +{ + return length == other.length && !strcmp(field_name, other.field_name); +} + + +/* + Test if a foreign key (= generated key) is a prefix of the given key + (ignoring key name, key type and order of columns) + + NOTES: + This is only used to test if an index for a FOREIGN KEY exists + + IMPLEMENTATION + We only compare field names + + RETURN + 0 Generated key is a prefix of other key + 1 Not equal +*/ + +bool foreign_key_prefix(Key *a, Key *b) +{ + /* Ensure that 'a' is the generated key */ + if (a->generated) + { + if (b->generated && a->columns.elements > b->columns.elements) + swap_variables(Key*, a, b); // Put shorter key in 'a' + } + else + { + if (!b->generated) + return TRUE; // No foreign key + swap_variables(Key*, a, b); // Put generated key in 'a' + } + + /* Test if 'a' is a prefix of 'b' */ + if (a->columns.elements > b->columns.elements) + return TRUE; // Can't be prefix + + List_iterator<key_part_spec> col_it1(a->columns); + List_iterator<key_part_spec> col_it2(b->columns); + const key_part_spec *col1, *col2; + +#ifdef ENABLE_WHEN_INNODB_CAN_HANDLE_SWAPED_FOREIGN_KEY_COLUMNS + while ((col1= col_it1++)) + { + bool found= 0; + col_it2.rewind(); + while ((col2= col_it2++)) + { + if (*col1 == *col2) + { + found= TRUE; + break; + } + } + if (!found) + return TRUE; // Error + } + return FALSE; // Is prefix +#else + while ((col1= col_it1++)) + { + col2= col_it2++; + if (!(*col1 == *col2)) + return TRUE; + } + return FALSE; // Is prefix +#endif +} + + /**************************************************************************** ** Thread specific functions ****************************************************************************/ @@ -447,6 +520,35 @@ bool THD::convert_string(LEX_STRING *to, CHARSET_INFO *to_cs, /* + Convert string from source character set to target character set inplace. + + SYNOPSIS + THD::convert_string + + DESCRIPTION + Convert string using convert_buffer - buffer for character set + conversion shared between all protocols. + + RETURN + 0 ok + !0 out of memory +*/ + +bool THD::convert_string(String *s, CHARSET_INFO *from_cs, CHARSET_INFO *to_cs) +{ + if (convert_buffer.copy(s->ptr(), s->length(), from_cs, to_cs)) + return TRUE; + /* If convert_buffer >> s copying is more efficient long term */ + if (convert_buffer.alloced_length() >= convert_buffer.length() * 2 || + !s->is_alloced()) + { + return s->copy(convert_buffer); + } + s->swap(convert_buffer); + return FALSE; +} + +/* Update some cache variables when character set changes */ diff --git a/sql/sql_class.h b/sql/sql_class.h index d5140f83752..1e9b1d04351 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -31,7 +31,7 @@ class sp_rcontext; class sp_cache; enum enum_enable_or_disable { LEAVE_AS_IS, ENABLE, DISABLE }; -enum enum_ha_read_modes { RFIRST, RNEXT, RPREV, RLAST, RKEY }; +enum enum_ha_read_modes { RFIRST, RNEXT, RPREV, RLAST, RKEY, RNEXT_SAME }; enum enum_duplicates { DUP_ERROR, DUP_REPLACE, DUP_IGNORE, DUP_UPDATE }; enum enum_log_type { LOG_CLOSED, LOG_TO_BE_OPENED, LOG_NORMAL, LOG_NEW, LOG_BIN}; enum enum_delay_key_write { DELAY_KEY_WRITE_NONE, DELAY_KEY_WRITE_ON, @@ -233,6 +233,7 @@ public: const char *field_name; uint length; key_part_spec(const char *name,uint len=0) :field_name(name), length(len) {} + bool operator==(const key_part_spec& other) const; }; @@ -262,12 +263,16 @@ public: enum ha_key_alg algorithm; List<key_part_spec> columns; const char *name; + bool generated; Key(enum Keytype type_par, const char *name_arg, enum ha_key_alg alg_par, - List<key_part_spec> &cols) - :type(type_par), algorithm(alg_par), columns(cols), name(name_arg) + bool generated_arg, List<key_part_spec> &cols) + :type(type_par), algorithm(alg_par), columns(cols), name(name_arg), + generated(generated_arg) {} ~Key() {} + /* Equality comparison of keys (ignoring name) */ + friend bool foreign_key_prefix(Key *a, Key *b); }; class Table_ident; @@ -285,7 +290,7 @@ public: foreign_key(const char *name_arg, List<key_part_spec> &cols, Table_ident *table, List<key_part_spec> &ref_cols, uint delete_opt_arg, uint update_opt_arg, uint match_opt_arg) - :Key(FOREIGN_KEY, name_arg, HA_KEY_ALG_UNDEF, cols), + :Key(FOREIGN_KEY, name_arg, HA_KEY_ALG_UNDEF, 0, cols), ref_table(table), ref_columns(cols), delete_opt(delete_opt_arg), update_opt(update_opt_arg), match_opt(match_opt_arg) @@ -625,11 +630,29 @@ public: Protocol_prep protocol_prep; // Binary protocol HASH user_vars; // hash for user variables String packet; // dynamic buffer for network I/O + String convert_buffer; // buffer for charset conversions struct sockaddr_in remote; // client socket address struct rand_struct rand; // used for authentication struct system_variables variables; // Changeable local variables pthread_mutex_t LOCK_delete; // Locked before thd is deleted - + /* + Note that (A) if we set query = NULL, we must at the same time set + query_length = 0, and protect the whole operation with the + LOCK_thread_count mutex. And (B) we are ONLY allowed to set query to a + non-NULL value if its previous value is NULL. We do not need to protect + operation (B) with any mutex. To avoid crashes in races, if we do not + know that thd->query cannot change at the moment, one should print + thd->query like this: + (1) reserve the LOCK_thread_count mutex; + (2) check if thd->query is NULL; + (3) if not NULL, then print at most thd->query_length characters from + it. We will see the query_length field as either 0, or the right value + for it. + Assuming that the write and read of an n-bit memory field in an n-bit + computer is atomic, we can avoid races in the above way. + This printing is needed at least in SHOW PROCESSLIST and SHOW INNODB + STATUS. + */ /* all prepared statements and cursors of this connection */ Statement_map stmt_map; /* @@ -955,6 +978,9 @@ public: bool convert_string(LEX_STRING *to, CHARSET_INFO *to_cs, const char *from, uint from_length, CHARSET_INFO *from_cs); + + bool convert_string(String *s, CHARSET_INFO *from_cs, CHARSET_INFO *to_cs); + void add_changed_table(TABLE *table); void add_changed_table(const char *key, long key_length); CHANGED_TABLE_LIST * changed_table_dup(const char *key, long key_length); @@ -1211,7 +1237,6 @@ class select_union :public select_result { TABLE *table; COPY_INFO info; TMP_TABLE_PARAM tmp_table_param; - bool not_describe; select_union(TABLE *table_par); ~select_union(); diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 300a2a455a4..e2c337e5064 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -18,6 +18,7 @@ /* create and drop of databases */ #include "mysql_priv.h" +#include <mysys_err.h> #include "sql_acl.h" #include "sp.h" #include <my_dir.h> @@ -186,7 +187,7 @@ int mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create_info, strxmov(path, mysql_data_home, "/", db, NullS); unpack_dirname(path,path); // Convert if not unix - if (my_stat(path,&stat_info,MYF(MY_WME))) + if (my_stat(path,&stat_info,MYF(0))) { if (!(create_options & HA_LEX_CREATE_IF_NOT_EXISTS)) { @@ -198,6 +199,11 @@ int mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create_info, } else { + if (my_errno != ENOENT) + { + my_error(EE_STAT, MYF(0),path,my_errno); + goto exit; + } strend(path)[-1]=0; // Remove last '/' from path if (my_mkdir(path,0777,MYF(0)) < 0) { @@ -365,7 +371,7 @@ int mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) { /* Convert database to lower case */ strmov(tmp_db, db); - my_casedn_str(system_charset_info, tmp_db); + my_casedn_str(files_charset_info, tmp_db); db= tmp_db; } diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 1579ac3b5c8..fcc56cbf9c9 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -103,7 +103,7 @@ int mysql_ha_closeall(THD *thd, TABLE_LIST *tables) } static enum enum_ha_read_modes rkey_to_rnext[]= - { RNEXT, RNEXT, RPREV, RNEXT, RPREV, RNEXT, RPREV, RPREV }; + { RNEXT_SAME, RNEXT, RPREV, RNEXT, RPREV, RNEXT, RPREV, RPREV }; int mysql_ha_read(THD *thd, TABLE_LIST *tables, @@ -146,7 +146,12 @@ int mysql_ha_read(THD *thd, TABLE_LIST *tables, char buff[MAX_FIELD_WIDTH]; String buffer(buff, sizeof(buff), system_charset_info); uint num_rows; - it++; + byte *key; + uint key_len; + LINT_INIT(key); + LINT_INIT(key_len); + + it++; // Skip first NULL field insert_fields(thd,tables,tables->db,tables->alias,&it); @@ -194,13 +199,16 @@ int mysql_ha_read(THD *thd, TABLE_LIST *tables, DBUG_ASSERT(keyname != 0); err=table->file->index_prev(table->record[0]); break; + case RNEXT_SAME: + /* Continue scan on "(keypart1,keypart2,...)=(c1, c2, ...) */ + DBUG_ASSERT(keyname != 0); + err= table->file->index_next_same(table->record[0], key, key_len); + break; case RKEY: { DBUG_ASSERT(keyname != 0); KEY *keyinfo=table->key_info+keyno; KEY_PART_INFO *key_part=keyinfo->key_part; - uint key_len; - byte *key; if (key_expr->elements > keyinfo->key_parts) { my_printf_error(ER_TOO_MANY_KEY_PARTS,ER(ER_TOO_MANY_KEY_PARTS), diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index b9a1f0cebd9..19fb439c5cb 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -408,7 +408,7 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, (ulong) (info.records - info.copied), (ulong) thd->cuted_fields); else sprintf(buff, ER(ER_INSERT_INFO), (ulong) info.records, - (ulong) info.deleted+info.updated, (ulong) thd->cuted_fields); + (ulong) (info.deleted+info.updated), (ulong) thd->cuted_fields); thd->row_count_func= info.copied+info.deleted+info.updated; ::send_ok(thd, thd->row_count_func, (ulonglong)id,buff); } @@ -431,7 +431,7 @@ abort: Prepare items in INSERT statement SYNOPSIS - mysql_prepare_update() + mysql_prepare_insert() thd - thread handler table_list - global table list insert_table_list - local table list of INSERT SELECT_LEX @@ -1580,7 +1580,7 @@ bool select_insert::send_eof() (ulong) (info.records - info.copied), (ulong) thd->cuted_fields); else sprintf(buff, ER(ER_INSERT_INFO), (ulong) info.records, - (ulong) info.deleted+info.updated, (ulong) thd->cuted_fields); + (ulong) (info.deleted+info.updated), (ulong) thd->cuted_fields); thd->row_count_func= info.copied+info.deleted+info.updated; ::send_ok(thd, thd->row_count_func, last_insert_id, buff); DBUG_RETURN(0); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 1a3558b5eeb..08b72173cae 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -24,8 +24,6 @@ #include "sp.h" #include "sp_head.h" -LEX_STRING tmp_table_alias= {(char*) "tmp-table",8}; - /* Macros to look like lex */ #define yyGet() *(lex->ptr++) @@ -997,6 +995,7 @@ void st_select_lex_unit::init_query() fake_select_lex= 0; cleaned= 0; item_list.empty(); + describe= 0; found_rows_for_union= 0; } @@ -1724,6 +1723,7 @@ TABLE_LIST *st_lex::link_first_table_back(TABLE_LIST *tables, st_select_lex::print is in sql_select.h st_select_lex_unit::prepare, st_select_lex_unit::exec, - st_select_lex_unit::cleanup, st_select_lex_unit::reinit_exec_mechanism + st_select_lex_unit::cleanup, st_select_lex_unit::reinit_exec_mechanism, + st_select_lex_unit::change_result are in sql_union.cc */ diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 0f0c39ab493..8d864c68f25 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -351,6 +351,7 @@ public: st_select_lex *fake_select_lex; st_select_lex *union_distinct; /* pointer to the last UNION DISTINCT */ + bool describe; /* union exec() called for EXPLAIN */ void init_query(); bool create_total_list(THD *thd, st_lex *lex, TABLE_LIST **result); @@ -383,6 +384,7 @@ public: void print(String *str); ulong init_prepare_fake_select_lex(THD *thd); + int change_result(select_subselect *result, select_subselect *old_result); void set_limit(st_select_lex *values, st_select_lex *sl); friend void mysql_init_query(THD *thd, bool lexonly); @@ -549,6 +551,20 @@ typedef class st_select_lex SELECT_LEX; #define ALTER_ORDER 64 #define ALTER_OPTIONS 128 +typedef struct st_alter_info +{ + List<Alter_drop> drop_list; + List<Alter_column> alter_list; + uint flags; + enum enum_enable_or_disable keys_onoff; + enum tablespace_op_type tablespace_op; + bool is_simple; + + st_alter_info(){clear();} + void clear(){keys_onoff= LEAVE_AS_IS;tablespace_op= NO_TABLESPACE_OP;} + void reset(){drop_list.empty();alter_list.empty();clear();} +} ALTER_INFO; + struct st_sp_chistics { LEX_STRING comment; @@ -589,8 +605,6 @@ typedef struct st_lex List<key_part_spec> col_list; List<key_part_spec> ref_list; - List<Alter_drop> drop_list; - List<Alter_column> alter_list; List<String> interval_list; List<LEX_USER> users_list; List<LEX_COLUMN> columns; @@ -618,19 +632,17 @@ typedef struct st_lex enum enum_tx_isolation tx_isolation; enum enum_ha_read_modes ha_read_mode; enum ha_rkey_function ha_rkey_mode; - enum enum_enable_or_disable alter_keys_onoff; enum enum_var_type option_type; - enum tablespace_op_type tablespace_op; uint uint_geom_type; uint grant, grant_tot_col, which_columns; uint fk_delete_opt, fk_update_opt, fk_match_option; uint slave_thd_opt; - uint alter_flags; uint8 describe; bool drop_if_exists, drop_temporary, local_file; - bool in_comment, ignore_space, verbose, simple_alter, no_write_to_binlog; + bool in_comment, ignore_space, verbose, no_write_to_binlog; bool derived_tables; bool safe_to_cache_query; + ALTER_INFO alter_info; sp_head *sphead; sp_name *spname; bool sp_lex_in_use; /* Keep track on lex usage in SPs for error handling */ @@ -684,6 +696,4 @@ void lex_end(LEX *lex); extern pthread_key(LEX*,THR_LEX); -extern LEX_STRING tmp_table_alias; - #define current_lex (current_thd->lex) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index bd36880433f..3669b14ad28 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1493,9 +1493,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd, packet++; length--; } + VOID(pthread_mutex_lock(&LOCK_thread_count)); thd->query_length= length; thd->query= packet; - VOID(pthread_mutex_lock(&LOCK_thread_count)); thd->query_id= query_id++; VOID(pthread_mutex_unlock(&LOCK_thread_count)); #ifndef EMBEDDED_LIBRARY @@ -1511,7 +1511,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, thd->query_rest.length(length); } else - thd->query_rest.copy(length); + thd->query_rest.copy(packet, length, thd->query_rest.charset()); break; #endif /*EMBEDDED_LIBRARY*/ } @@ -1784,6 +1784,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, thd->proc_info=0; thd->command=COM_SLEEP; thd->query=0; + thd->query_length=0; thread_running--; VOID(pthread_mutex_unlock(&LOCK_thread_count)); thd->packet.shrink(thd->variables.net_buffer_length); // Reclaim some memory @@ -1824,6 +1825,7 @@ bool alloc_query(THD *thd, char *packet, ulong packet_length) packet_length--; } /* We must allocate some extra memory for query cache */ + thd->query_length= 0; // Extra safety: Avoid races if (!(thd->query= (char*) thd->memdup_w_gap((gptr) (packet), packet_length, thd->db_length+ 1 + @@ -1831,7 +1833,10 @@ bool alloc_query(THD *thd, char *packet, ulong packet_length) return 1; thd->query[packet_length]=0; thd->query_length= packet_length; - thd->packet.shrink(thd->variables.net_buffer_length);// Reclaim some memory + + /* Reclaim some memory */ + thd->packet.shrink(thd->variables.net_buffer_length); + thd->convert_buffer.shrink(thd->variables.net_buffer_length); if (!(specialflag & SPECIAL_NO_PRIOR)) my_pthread_setprio(pthread_self(),QUERY_PRIOR); @@ -2374,14 +2379,10 @@ unsent_create_error: res= mysql_alter_table(thd, select_lex->db, lex->name, &lex->create_info, tables, lex->create_list, - lex->key_list, lex->drop_list, lex->alter_list, + lex->key_list, select_lex->order_list.elements, (ORDER *) select_lex->order_list.first, - lex->alter_flags, - lex->duplicates, - lex->alter_keys_onoff, - lex->tablespace_op, - lex->simple_alter); + lex->duplicates, &lex->alter_info); } break; } @@ -2518,17 +2519,15 @@ unsent_create_error: lex->create_list.empty(); lex->key_list.empty(); lex->col_list.empty(); - lex->drop_list.empty(); - lex->alter_list.empty(); + lex->alter_info.reset(); bzero((char*) &create_info,sizeof(create_info)); create_info.db_type=DB_TYPE_DEFAULT; create_info.row_type=ROW_TYPE_DEFAULT; create_info.default_table_charset=default_charset_info; res= mysql_alter_table(thd, NullS, NullS, &create_info, tables, lex->create_list, - lex->key_list, lex->drop_list, lex->alter_list, - 0, (ORDER *) 0, 0, - DUP_ERROR); + lex->key_list, 0, (ORDER *) 0, + DUP_ERROR, &lex->alter_info); } else res = mysql_optimize_table(thd, tables, &lex->check_opt); @@ -2754,7 +2753,7 @@ unsent_create_error: if (end_active_trans(thd)) res= -1; else - res = mysql_drop_index(thd, tables, lex->drop_list); + res = mysql_drop_index(thd, tables, &lex->alter_info); break; case SQLCOM_SHOW_DATABASES: #if defined(DONT_ALLOW_SHOW_COMMANDS) @@ -4262,13 +4261,13 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, { lex->col_list.push_back(new key_part_spec(field_name,0)); lex->key_list.push_back(new Key(Key::PRIMARY, NullS, HA_KEY_ALG_UNDEF, - lex->col_list)); + 0, lex->col_list)); lex->col_list.empty(); } if (type_modifier & (UNIQUE_FLAG | UNIQUE_KEY_FLAG)) { lex->col_list.push_back(new key_part_spec(field_name,0)); - lex->key_list.push_back(new Key(Key::UNIQUE, NullS, HA_KEY_ALG_UNDEF, + lex->key_list.push_back(new Key(Key::UNIQUE, NullS, HA_KEY_ALG_UNDEF, 0, lex->col_list)); lex->col_list.empty(); } @@ -4914,7 +4913,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, mysql_bin_log.new_file(1); mysql_slow_log.new_file(1); #ifdef HAVE_REPLICATION - if (expire_logs_days) + if (mysql_bin_log.is_open() && expire_logs_days) { long purge_time= time(0) - expire_logs_days*24*60*60; if (purge_time >= 0) @@ -5204,8 +5203,9 @@ Item * all_any_subquery_creator(Item *left_expr, int mysql_create_index(THD *thd, TABLE_LIST *table_list, List<Key> &keys) { List<create_field> fields; - List<Alter_drop> drop; - List<Alter_column> alter; + ALTER_INFO alter_info; + alter_info.flags= ALTER_ADD_INDEX; + alter_info.is_simple= 0; HA_CREATE_INFO create_info; DBUG_ENTER("mysql_create_index"); bzero((char*) &create_info,sizeof(create_info)); @@ -5213,25 +5213,27 @@ int mysql_create_index(THD *thd, TABLE_LIST *table_list, List<Key> &keys) create_info.default_table_charset= thd->variables.collation_database; DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->real_name, &create_info, table_list, - fields, keys, drop, alter, 0, (ORDER*)0, - ALTER_ADD_INDEX, DUP_ERROR)); + fields, keys, 0, (ORDER*)0, + DUP_ERROR, &alter_info)); } -int mysql_drop_index(THD *thd, TABLE_LIST *table_list, List<Alter_drop> &drop) +int mysql_drop_index(THD *thd, TABLE_LIST *table_list, ALTER_INFO *alter_info) { List<create_field> fields; List<Key> keys; - List<Alter_column> alter; HA_CREATE_INFO create_info; DBUG_ENTER("mysql_drop_index"); bzero((char*) &create_info,sizeof(create_info)); create_info.db_type=DB_TYPE_DEFAULT; create_info.default_table_charset= thd->variables.collation_database; + alter_info->clear(); + alter_info->flags= ALTER_DROP_INDEX; + alter_info->is_simple= 0; DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->real_name, &create_info, table_list, - fields, keys, drop, alter, 0, (ORDER*)0, - ALTER_DROP_INDEX, DUP_ERROR)); + fields, keys, 0, (ORDER*)0, + DUP_ERROR, alter_info)); } diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index a6e6e1deae2..87fb379321f 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -57,9 +57,9 @@ Long data handling: - Server gets the long data in pieces with command type 'COM_LONG_DATA'. - The packet recieved will have the format as: - [COM_LONG_DATA:1][STMT_ID:4][parameter_number:2][type:2][data] - - Checks if the type is specified by client, and if yes reads the type, - and stores the data in that format. + [COM_LONG_DATA:1][STMT_ID:4][parameter_number:2][data] + - data from the packet is appended to long data value buffer for this + placeholder. - It's up to the client to check for read data ended. The server doesn't care; and also server doesn't notify to the client that it got the data or not; if there is any error; then during execute; the error @@ -77,8 +77,6 @@ Long data handling: #include <mysql.h> #endif -const String my_null_string("NULL", 4, default_charset_info); - /****************************************************************************** Prepared_statement: statement which can contain placeholders ******************************************************************************/ @@ -92,7 +90,6 @@ public: uint last_errno; char last_error[MYSQL_ERRMSG_SIZE]; bool get_longdata_error; - bool log_full_query; #ifndef EMBEDDED_LIBRARY bool (*set_params)(Prepared_statement *st, uchar *data, uchar *data_end, uchar *read_pos); @@ -240,7 +237,7 @@ static ulong get_param_length(uchar **packet, ulong len) none */ -void set_param_tiny(Item_param *param, uchar **pos, ulong len) +static void set_param_tiny(Item_param *param, uchar **pos, ulong len) { #ifndef EMBEDDED_LIBRARY if (len < 1) @@ -248,45 +245,55 @@ void set_param_tiny(Item_param *param, uchar **pos, ulong len) #endif int8 value= (int8) **pos; param->set_int(param->unsigned_flag ? (longlong) ((uint8) value) : - (longlong) value); + (longlong) value, 4); *pos+= 1; } -void set_param_short(Item_param *param, uchar **pos, ulong len) +static void set_param_short(Item_param *param, uchar **pos, ulong len) { + int16 value; #ifndef EMBEDDED_LIBRARY if (len < 2) return; + value= sint2korr(*pos); +#else + shortget(value, *pos); #endif - int16 value= sint2korr(*pos); param->set_int(param->unsigned_flag ? (longlong) ((uint16) value) : - (longlong) value); + (longlong) value, 6); *pos+= 2; } -void set_param_int32(Item_param *param, uchar **pos, ulong len) +static void set_param_int32(Item_param *param, uchar **pos, ulong len) { + int32 value; #ifndef EMBEDDED_LIBRARY if (len < 4) return; + value= sint4korr(*pos); +#else + longget(value, *pos); #endif - int32 value= sint4korr(*pos); param->set_int(param->unsigned_flag ? (longlong) ((uint32) value) : - (longlong) value); + (longlong) value, 11); *pos+= 4; } -void set_param_int64(Item_param *param, uchar **pos, ulong len) +static void set_param_int64(Item_param *param, uchar **pos, ulong len) { + longlong value; #ifndef EMBEDDED_LIBRARY if (len < 8) return; + value= (longlong) sint8korr(*pos); +#else + longlongget(value, *pos); #endif - param->set_int((longlong)sint8korr(*pos)); + param->set_int(value, 21); *pos+= 8; } -void set_param_float(Item_param *param, uchar **pos, ulong len) +static void set_param_float(Item_param *param, uchar **pos, ulong len) { #ifndef EMBEDDED_LIBRARY if (len < 4) @@ -298,7 +305,7 @@ void set_param_float(Item_param *param, uchar **pos, ulong len) *pos+= 4; } -void set_param_double(Item_param *param, uchar **pos, ulong len) +static void set_param_double(Item_param *param, uchar **pos, ulong len) { #ifndef EMBEDDED_LIBRARY if (len < 8) @@ -310,9 +317,11 @@ void set_param_double(Item_param *param, uchar **pos, ulong len) *pos+= 8; } -void set_param_time(Item_param *param, uchar **pos, ulong len) +#ifndef EMBEDDED_LIBRARY +static void set_param_time(Item_param *param, uchar **pos, ulong len) { ulong length; + uint day; if ((length= get_param_length(pos, len)) >= 8) { @@ -322,20 +331,33 @@ void set_param_time(Item_param *param, uchar **pos, ulong len) /* TODO: why length is compared with 8 here? */ tm.second_part= (length > 8 ) ? (ulong) sint4korr(to+7): 0; - tm.day= (ulong) sint4korr(to+1); - tm.hour= (uint) to[5]; + /* + Note, that though ranges of hour, minute and second are not checked + here we rely on them being < 256: otherwise + we'll get buffer overflow in make_{date,time} functions, + which are called when time value is converted to string. + */ + day= (uint) sint4korr(to+1); + tm.hour= (uint) to[5] + day * 24; tm.minute= (uint) to[6]; tm.second= (uint) to[7]; - - tm.year= tm.month= 0; + if (tm.hour > 838) + { + /* TODO: add warning 'Data truncated' here */ + tm.hour= 838; + tm.minute= 59; + tm.second= 59; + } + tm.day= tm.year= tm.month= 0; tm.neg= (bool)to[0]; - param->set_time(&tm, TIMESTAMP_TIME); + param->set_time(&tm, TIMESTAMP_TIME, + MAX_TIME_WIDTH * MY_CHARSET_BIN_MB_MAXLEN); } *pos+= length; } -void set_param_datetime(Item_param *param, uchar **pos, ulong len) +static void set_param_datetime(Item_param *param, uchar **pos, ulong len) { uint length; @@ -346,6 +368,11 @@ void set_param_datetime(Item_param *param, uchar **pos, ulong len) tm.second_part= (length > 7 ) ? (ulong) sint4korr(to+7): 0; + /* + Note, that though ranges of hour, minute and second are not checked + here we rely on them being < 256: otherwise + we'll get buffer overflow in make_{date,time} functions. + */ if (length > 4) { tm.hour= (uint) to[4]; @@ -360,12 +387,13 @@ void set_param_datetime(Item_param *param, uchar **pos, ulong len) tm.day= (uint) to[3]; tm.neg= 0; - param->set_time(&tm, TIMESTAMP_DATETIME); + param->set_time(&tm, TIMESTAMP_DATETIME, + MAX_DATETIME_WIDTH * MY_CHARSET_BIN_MB_MAXLEN); } *pos+= length; } -void set_param_date(Item_param *param, uchar **pos, ulong len) +static void set_param_date(Item_param *param, uchar **pos, ulong len) { ulong length; @@ -373,7 +401,11 @@ void set_param_date(Item_param *param, uchar **pos, ulong len) { uchar *to= *pos; TIME tm; - + /* + Note, that though ranges of hour, minute and second are not checked + here we rely on them being < 256: otherwise + we'll get buffer overflow in make_{date,time} functions. + */ tm.year= (uint) sint2korr(to); tm.month= (uint) to[2]; tm.day= (uint) to[3]; @@ -382,61 +414,171 @@ void set_param_date(Item_param *param, uchar **pos, ulong len) tm.second_part= 0; tm.neg= 0; - param->set_time(&tm, TIMESTAMP_DATE); + param->set_time(&tm, TIMESTAMP_DATE, + MAX_DATE_WIDTH * MY_CHARSET_BIN_MB_MAXLEN); } *pos+= length; } -void set_param_str(Item_param *param, uchar **pos, ulong len) +#else/*!EMBEDDED_LIBRARY*/ +void set_param_time(Item_param *param, uchar **pos, ulong len) +{ + TIME tm; + MYSQL_TIME *to= (MYSQL_TIME*)*pos; + + tm.second_part= to->second_part; + + tm.day= to->day; + tm.hour= to->hour; + tm.minute= to->minute; + tm.second= to->second; + + tm.year= tm.month= 0; + tm.neg= to->neg; + param->set_time(&tm, TIMESTAMP_TIME, + MAX_TIME_WIDTH * MY_CHARSET_BIN_MB_MAXLEN); + +} + +void set_param_datetime(Item_param *param, uchar **pos, ulong len) +{ + TIME tm; + MYSQL_TIME *to= (MYSQL_TIME*)*pos; + + tm.second_part= to->second_part; + + tm.day= to->day; + tm.hour= to->hour; + tm.minute= to->minute; + tm.second= to->second; + tm.year= to->year; + tm.month= to->month; + tm.neg= 0; + + param->set_time(&tm, TIMESTAMP_DATETIME, + MAX_DATETIME_WIDTH * MY_CHARSET_BIN_MB_MAXLEN); +} + +void set_param_date(Item_param *param, uchar **pos, ulong len) +{ + TIME tm; + MYSQL_TIME *to= (MYSQL_TIME*)*pos; + + tm.second_part= to->second_part; + + tm.day= to->day; + tm.year= to->year; + tm.month= to->month; + tm.neg= 0; + tm.hour= tm.minute= tm.second= 0; + tm.second_part= 0; + tm.neg= 0; + + param->set_time(&tm, TIMESTAMP_DATE, + MAX_DATE_WIDTH * MY_CHARSET_BIN_MB_MAXLEN); +} +#endif /*!EMBEDDED_LIBRARY*/ + + +static void set_param_str(Item_param *param, uchar **pos, ulong len) { ulong length= get_param_length(pos, len); - param->set_value((const char *)*pos, length); + param->set_str((const char *)*pos, length); *pos+= length; } -static void setup_one_conversion_function(Item_param *param, uchar param_type) + +#undef get_param_length + +static void setup_one_conversion_function(THD *thd, Item_param *param, + uchar param_type) { switch (param_type) { - case FIELD_TYPE_TINY: + case MYSQL_TYPE_TINY: param->set_param_func= set_param_tiny; + param->item_type= Item::INT_ITEM; param->item_result_type= INT_RESULT; break; - case FIELD_TYPE_SHORT: + case MYSQL_TYPE_SHORT: param->set_param_func= set_param_short; + param->item_type= Item::INT_ITEM; param->item_result_type= INT_RESULT; break; - case FIELD_TYPE_LONG: + case MYSQL_TYPE_LONG: param->set_param_func= set_param_int32; + param->item_type= Item::INT_ITEM; param->item_result_type= INT_RESULT; break; - case FIELD_TYPE_LONGLONG: + case MYSQL_TYPE_LONGLONG: param->set_param_func= set_param_int64; + param->item_type= Item::INT_ITEM; param->item_result_type= INT_RESULT; break; - case FIELD_TYPE_FLOAT: + case MYSQL_TYPE_FLOAT: param->set_param_func= set_param_float; + param->item_type= Item::REAL_ITEM; param->item_result_type= REAL_RESULT; break; - case FIELD_TYPE_DOUBLE: + case MYSQL_TYPE_DOUBLE: param->set_param_func= set_param_double; + param->item_type= Item::REAL_ITEM; param->item_result_type= REAL_RESULT; break; - case FIELD_TYPE_TIME: + case MYSQL_TYPE_TIME: param->set_param_func= set_param_time; + param->item_type= Item::STRING_ITEM; param->item_result_type= STRING_RESULT; break; - case FIELD_TYPE_DATE: + case MYSQL_TYPE_DATE: param->set_param_func= set_param_date; + param->item_type= Item::STRING_ITEM; param->item_result_type= STRING_RESULT; break; case MYSQL_TYPE_DATETIME: case MYSQL_TYPE_TIMESTAMP: param->set_param_func= set_param_datetime; + param->item_type= Item::STRING_ITEM; param->item_result_type= STRING_RESULT; break; - default: + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: + case MYSQL_TYPE_BLOB: param->set_param_func= set_param_str; + param->value.cs_info.character_set_client= &my_charset_bin; + param->value.cs_info.final_character_set_of_str_value= &my_charset_bin; + param->item_type= Item::STRING_ITEM; param->item_result_type= STRING_RESULT; + break; + default: + /* + The client library ensures that we won't get any other typecodes + except typecodes above and typecodes for string types. Marking + label as 'default' lets us to handle malformed packets as well. + */ + { + CHARSET_INFO *fromcs= thd->variables.character_set_client; + CHARSET_INFO *tocs= thd->variables.collation_connection; + uint32 dummy_offset; + + param->value.cs_info.character_set_client= fromcs; + + /* + Setup source and destination character sets so that they + are different only if conversion is necessary: this will + make later checks easier. + */ + param->value.cs_info.final_character_set_of_str_value= + String::needs_conversion(0, fromcs, tocs, &dummy_offset) ? + tocs : fromcs; + param->set_param_func= set_param_str; + /* + Exact value of max_length is not known unless data is converted to + charset of connection, so we have to set it later. + */ + param->item_type= Item::STRING_ITEM; + param->item_result_type= STRING_RESULT; + } } } @@ -465,23 +607,21 @@ static bool insert_params_withlog(Prepared_statement *stmt, uchar *null_array, for (Item_param **it= begin; it < end; ++it) { Item_param *param= *it; - if (param->long_data_supplied) - res= param->query_val_str(&str); - else + if (param->state != Item_param::LONG_DATA_VALUE) { if (is_param_null(null_array, it - begin)) - { param->set_null(); - res= &my_null_string; - } else { if (read_pos >= data_end) DBUG_RETURN(1); param->set_param_func(param, &read_pos, data_end - read_pos); - res= param->query_val_str(&str); } } + res= param->query_val_str(&str); + if (param->convert_str_value(thd)) + DBUG_RETURN(1); /* out of memory */ + if (query.replace(param->pos_in_query+length, 1, *res)) DBUG_RETURN(1); @@ -505,7 +645,7 @@ static bool insert_params(Prepared_statement *stmt, uchar *null_array, for (Item_param **it= begin; it < end; ++it) { Item_param *param= *it; - if (!param->long_data_supplied) + if (param->state != Item_param::LONG_DATA_VALUE) { if (is_param_null(null_array, it - begin)) param->set_null(); @@ -516,6 +656,8 @@ static bool insert_params(Prepared_statement *stmt, uchar *null_array, param->set_param_func(param, &read_pos, data_end - read_pos); } } + if (param->convert_str_value(stmt->thd)) + DBUG_RETURN(1); /* out of memory */ } DBUG_RETURN(0); } @@ -537,6 +679,7 @@ static bool setup_conversion_functions(Prepared_statement *stmt, */ Item_param **it= stmt->param_array; Item_param **end= it + stmt->param_count; + THD *thd= stmt->thd; for (; it < end; ++it) { ushort typecode; @@ -548,7 +691,7 @@ static bool setup_conversion_functions(Prepared_statement *stmt, typecode= sint2korr(read_pos); read_pos+= 2; (**it).unsigned_flag= test(typecode & signed_bit); - setup_one_conversion_function(*it, (uchar) (typecode & ~signed_bit)); + setup_one_conversion_function(thd, *it, (uchar) (typecode & ~signed_bit)); } } *data= read_pos; @@ -559,6 +702,7 @@ static bool setup_conversion_functions(Prepared_statement *stmt, static bool emb_insert_params(Prepared_statement *stmt) { + THD *thd= stmt->thd; Item_param **it= stmt->param_array; Item_param **end= it + stmt->param_count; MYSQL_BIND *client_param= stmt->thd->client_params; @@ -568,20 +712,22 @@ static bool emb_insert_params(Prepared_statement *stmt) for (; it < end; ++it, ++client_param) { Item_param *param= *it; - setup_one_conversion_function(param, client_param->buffer_type); - if (!param->long_data_supplied) + setup_one_conversion_function(thd, param, client_param->buffer_type); + if (param->state != Item_param::LONG_DATA_VALUE) { if (*client_param->is_null) param->set_null(); else { - uchar *buff= (uchar*)client_param->buffer; + uchar *buff= (uchar*) client_param->buffer; param->set_param_func(param, &buff, client_param->length ? *client_param->length : client_param->buffer_length); } } + if (param->convert_str_value(thd)) + DBUG_RETURN(1); /* out of memory */ } DBUG_RETURN(0); } @@ -606,25 +752,22 @@ static bool emb_insert_params_withlog(Prepared_statement *stmt) for (; it < end; ++it, ++client_param) { Item_param *param= *it; - setup_one_conversion_function(param, client_param->buffer_type); - if (param->long_data_supplied) - res= param->query_val_str(&str); - else + setup_one_conversion_function(thd, param, client_param->buffer_type); + if (param->state != Item_param::LONG_DATA_VALUE) { if (*client_param->is_null) - { param->set_null(); - res= &my_null_string; - } else { - uchar *buff= (uchar*)client_param->buffer; + uchar *buff= (uchar*)client_param->buffer; param->set_param_func(param, &buff, client_param->length ? *client_param->length : client_param->buffer_length); - res= param->query_val_str(&str); } + res= param->query_val_str(&str); + if (param->convert_str_value(thd)) + DBUG_RETURN(1); /* out of memory */ } if (query.replace(param->pos_in_query+length, 1, *res)) DBUG_RETURN(1); @@ -669,7 +812,7 @@ static int mysql_test_insert(Prepared_statement *stmt, TABLE_LIST *insert_table_list= (TABLE_LIST*) lex->select_lex.table_list.first; my_bool update= (lex->value_list.elements ? UPDATE_ACL : 0); - DBUG_ENTER("mysql_test_insert_fields"); + DBUG_ENTER("mysql_test_insert"); if ((res= insert_precheck(thd, table_list, update))) DBUG_RETURN(res); @@ -725,7 +868,7 @@ error: Validate UPDATE statement SYNOPSIS - mysql_test_delete() + mysql_test_update() stmt prepared statemen handler tables list of tables queries @@ -1038,7 +1181,7 @@ end: /* - Validate and prepare for execution CRETE TABLE statement + Validate and prepare for execution CREATE TABLE statement SYNOPSIS mysql_test_create_table() @@ -1075,7 +1218,7 @@ static int mysql_test_create_table(Prepared_statement *stmt, /* - Validate and prepare for execution multy update statement + Validate and prepare for execution multi update statement SYNOPSIS mysql_test_multiupdate() @@ -1098,7 +1241,7 @@ static int mysql_test_multiupdate(Prepared_statement *stmt, /* - Validate and prepare for execution multy delete statement + Validate and prepare for execution multi delete statement SYNOPSIS mysql_test_multidelete() @@ -1270,8 +1413,8 @@ error: } /* - Initialize array of parametes in statement from LEX. - (We need to have quick access to items by number in mysql_send_longdata). + Initialize array of parameters in statement from LEX. + (We need to have quick access to items by number in mysql_stmt_get_longdata). This is to avoid using malloc/realloc in the parser. */ @@ -1469,7 +1612,6 @@ static void reset_stmt_params(Prepared_statement *stmt) mysql_stmt_execute() */ - void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) { ulong stmt_id= uint4korr(packet); @@ -1481,7 +1623,8 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) packet+= 9; /* stmt_id + 5 bytes of flags */ - if (!(stmt= find_prepared_statement(thd, stmt_id, "execute", SEND_ERROR))) + if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_execute", + SEND_ERROR))) DBUG_VOID_RETURN; DBUG_PRINT("exec_query:", ("%s", stmt->query)); @@ -1535,7 +1678,7 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) reset_stmt_params(stmt); close_thread_tables(thd); // to close derived tables thd->set_statement(&thd->stmt_backup); - /* + /* Free Items that were created during this execution of the PS by query optimizer. */ @@ -1545,7 +1688,7 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) set_params_data_err: reset_stmt_params(stmt); thd->set_statement(&thd->stmt_backup); - my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_execute"); + my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_stmt_execute"); send_error(thd); DBUG_VOID_RETURN; } @@ -1576,7 +1719,8 @@ void mysql_stmt_reset(THD *thd, char *packet) DBUG_ENTER("mysql_stmt_reset"); - if (!(stmt= find_prepared_statement(thd, stmt_id, "reset", SEND_ERROR))) + if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_reset", + SEND_ERROR))) DBUG_VOID_RETURN; stmt->get_longdata_error= 0; @@ -1606,7 +1750,8 @@ void mysql_stmt_free(THD *thd, char *packet) DBUG_ENTER("mysql_stmt_free"); - if (!(stmt= find_prepared_statement(thd, stmt_id, "close", DONT_SEND_ERROR))) + if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_close", + DONT_SEND_ERROR))) DBUG_VOID_RETURN; /* Statement map deletes statement on erase */ @@ -1634,43 +1779,50 @@ void mysql_stmt_free(THD *thd, char *packet) to the server. (No checking that we get a 'end of column' in the server) */ -void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length) +void mysql_stmt_get_longdata(THD *thd, char *packet, ulong packet_length) { + ulong stmt_id; + uint param_number; Prepared_statement *stmt; + Item_param *param; + char *packet_end= packet + packet_length - 1; DBUG_ENTER("mysql_stmt_get_longdata"); #ifndef EMBEDDED_LIBRARY - /* The following should never happen */ - if (packet_length < MYSQL_LONG_DATA_HEADER+1) + /* Minimal size of long data packet is 6 bytes */ + if ((ulong) (packet_end - packet) < MYSQL_LONG_DATA_HEADER) { - my_error(ER_WRONG_ARGUMENTS, MYF(0), "get_longdata"); + my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_stmt_send_long_data"); DBUG_VOID_RETURN; } #endif - ulong stmt_id= uint4korr(pos); - uint param_number= uint2korr(pos+4); + stmt_id= uint4korr(packet); + packet+= 4; - if (!(stmt=find_prepared_statement(thd, stmt_id, "get_longdata", + if (!(stmt=find_prepared_statement(thd, stmt_id, "mysql_stmt_send_long_data", DONT_SEND_ERROR))) DBUG_VOID_RETURN; + param_number= uint2korr(packet); + packet+= 2; #ifndef EMBEDDED_LIBRARY if (param_number >= stmt->param_count) { /* Error will be sent in execute call */ stmt->get_longdata_error= 1; stmt->last_errno= ER_WRONG_ARGUMENTS; - sprintf(stmt->last_error, ER(ER_WRONG_ARGUMENTS), "get_longdata"); + sprintf(stmt->last_error, ER(ER_WRONG_ARGUMENTS), + "mysql_stmt_send_long_data"); DBUG_VOID_RETURN; } - pos+= MYSQL_LONG_DATA_HEADER; // Point to data #endif - Item_param *param= stmt->param_array[param_number]; + param= stmt->param_array[param_number]; + #ifndef EMBEDDED_LIBRARY - param->set_longdata(pos, packet_length-MYSQL_LONG_DATA_HEADER-1); + param->set_longdata(packet, (ulong) (packet_end - packet)); #else param->set_longdata(thd->extra_data, thd->extra_length); #endif @@ -1684,13 +1836,11 @@ Prepared_statement::Prepared_statement(THD *thd_arg) param_array(0), param_count(0), last_errno(0), - get_longdata_error(0), - log_full_query(0) + get_longdata_error(0) { *last_error= '\0'; if (mysql_bin_log.is_open()) { - log_full_query= 1; #ifndef EMBEDDED_LIBRARY set_params= insert_params_withlog; #else diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 9d6a6999591..dd5bbc01d44 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000-2003 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2004 MySQL AB & MySQL Finland AB & TCX DataKonsult AB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -398,7 +398,7 @@ JOIN::prepare(Item ***rref_pointer_array, { if (item->with_sum_func) flag|=1; - else if (!(flag & 2) && !item->const_item()) + else if (!(flag & 2) && !item->const_during_execution()) flag|=2; } if (flag == 3) @@ -701,7 +701,6 @@ JOIN::optimize() if (!order && org_order) skip_sort_order= 1; } - order= remove_const(this, order, conds, &simple_order); if (group_list || tmp_table_param.sum_func_count) { if (! hidden_group_fields) @@ -1616,8 +1615,7 @@ mysql_select(THD *thd, Item ***rref_pointer_array, if (select_lex->linkage != GLOBAL_OPTIONS_TYPE) { //here is EXPLAIN of subselect or derived table - join->result= result; - if (!join->procedure && result->prepare(join->fields_list, unit)) + if (join->change_result(result)) { DBUG_RETURN(-1); } @@ -4013,12 +4011,12 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, best_record_count=current_record_count; best_read_time=current_read_time; } - swap(JOIN_TAB*,join->best_ref[idx],*pos); + swap_variables(JOIN_TAB*, join->best_ref[idx], *pos); find_best(join,rest_tables & ~real_table_bit,idx+1, current_record_count,current_read_time); if (thd->killed) return; - swap(JOIN_TAB*,join->best_ref[idx],*pos); + swap_variables(JOIN_TAB*, join->best_ref[idx], *pos); } if (join->select_options & SELECT_STRAIGHT_JOIN) break; // Don't test all combinations @@ -5878,6 +5876,9 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, else // if we run out of slots or we are not using tempool sprintf(path,"%s%s%lx_%lx_%x",mysql_tmpdir,tmp_file_prefix,current_pid, thd->thread_id, thd->tmp_table++); + + if (lower_case_table_names) + my_casedn_str(files_charset_info, path); if (group) { @@ -6516,6 +6517,8 @@ bool create_myisam_from_heap(THD *thd, TABLE *table, TMP_TABLE_PARAM *param, goto err2; if (open_tmp_table(&new_table)) goto err1; + if (table->file->indexes_are_disabled()) + new_table.file->disable_indexes(HA_KEY_SWITCH_ALL); table->file->index_end(); table->file->rnd_init(); if (table->no_rows) @@ -7127,8 +7130,8 @@ join_read_prev_same(READ_RECORD *info) if ((error=table->file->index_prev(table->record[0]))) return report_error(table, error); - if (key_cmp(table, tab->ref.key_buff, tab->ref.key, - tab->ref.key_length)) + if (key_cmp_if_same(table, tab->ref.key_buff, tab->ref.key, + tab->ref.key_length)) { table->status=STATUS_NOT_FOUND; error= -1; @@ -10101,7 +10104,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, THD *thd=join->thd; select_result *result=join->result; Item *item_null= new Item_null(); - CHARSET_INFO *cs= &my_charset_latin1; + CHARSET_INFO *cs= system_charset_info; DBUG_ENTER("select_describe"); DBUG_PRINT("info", ("Select 0x%lx, type %s, message %s", (ulong)join->select_lex, join->select_lex->type, @@ -10112,7 +10115,8 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, if (message) { - item_list.push_back(new Item_int((int32) join->select_lex->select_number)); + item_list.push_back(new Item_int((int32) + join->select_lex->select_number)); item_list.push_back(new Item_string(join->select_lex->type, strlen(join->select_lex->type), cs)); for (uint i=0 ; i < 7; i++) @@ -10121,6 +10125,70 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, if (result->send_data(item_list)) join->error= 1; } + else if (join->select_lex == join->unit->fake_select_lex) + { + /* + here we assume that the query will return at least two rows, so we + show "filesort" in EXPLAIN. Of course, sometimes we'll be wrong + and no filesort will be actually done, but executing all selects in + the UNION to provide precise EXPLAIN information will hardly be + appreciated :) + */ + char table_name_buffer[NAME_LEN]; + item_list.empty(); + /* id */ + item_list.push_back(new Item_null); + /* select_type */ + item_list.push_back(new Item_string(join->select_lex->type, + strlen(join->select_lex->type), + cs)); + /* table */ + { + SELECT_LEX *sl= join->unit->first_select(); + uint len= 6, lastop= 0; + memcpy(table_name_buffer, "<union", 6); + for (; sl && len + lastop + 5 < NAME_LEN; sl= sl->next_select()) + { + len+= lastop; + lastop= my_snprintf(table_name_buffer + len, NAME_LEN - len, + "%u,", sl->select_number); + } + if (sl || len + lastop >= NAME_LEN) + { + memcpy(table_name_buffer + len, "...>", 5); + len+= 4; + } + else + { + len+= lastop; + table_name_buffer[len - 1]= '>'; // change ',' to '>' + } + item_list.push_back(new Item_string(table_name_buffer, len, cs)); + } + /* type */ + item_list.push_back(new Item_string(join_type_str[JT_ALL], + strlen(join_type_str[JT_ALL]), + cs)); + /* possible_keys */ + item_list.push_back(item_null); + /* key*/ + item_list.push_back(item_null); + /* key_len */ + item_list.push_back(item_null); + /* ref */ + item_list.push_back(item_null); + /* rows */ + item_list.push_back(item_null); + /* extra */ + if (join->unit->global_parameters->order_list.first) + item_list.push_back(new Item_string("Using filesort", + 14, cs)); + else + item_list.push_back(new Item_string("", 0, cs)); + + if (result->send_data(item_list)) + join->error= 1; + } else { table_map used_tables=0; @@ -10131,7 +10199,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, char buff[512],*buff_ptr=buff; char buff1[512], buff2[512], buff3[512]; char keylen_str_buf[64]; - char derived_name[64]; + char table_name_buffer[NAME_LEN]; String tmp1(buff1,sizeof(buff1),cs); String tmp2(buff2,sizeof(buff2),cs); String tmp3(buff3,sizeof(buff3),cs); @@ -10140,8 +10208,10 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, tmp3.length(0); item_list.empty(); - item_list.push_back(new Item_int((int32) + /* id */ + item_list.push_back(new Item_uint((uint32) join->select_lex->select_number)); + /* select_type */ item_list.push_back(new Item_string(join->select_lex->type, strlen(join->select_lex->type), cs)); @@ -10153,22 +10223,25 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, else tab->type = JT_RANGE; } + /* table */ if (table->derived_select_number) { /* Derived table name generation */ - int len= my_snprintf(derived_name, sizeof(derived_name)-1, + int len= my_snprintf(table_name_buffer, sizeof(table_name_buffer)-1, "<derived%u>", table->derived_select_number); - item_list.push_back(new Item_string(derived_name, len, cs)); + item_list.push_back(new Item_string(table_name_buffer, len, cs)); } else item_list.push_back(new Item_string(table->table_name, strlen(table->table_name), cs)); + /* type */ item_list.push_back(new Item_string(join_type_str[tab->type], strlen(join_type_str[tab->type]), cs)); uint j; + /* possible_keys */ if (!tab->keys.is_clear_all()) { for (j=0 ; j < table->keys ; j++) @@ -10177,7 +10250,9 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, { if (tmp1.length()) tmp1.append(','); - tmp1.append(table->key_info[j].name); + tmp1.append(table->key_info[j].name, + strlen(table->key_info[j].name), + system_charset_info); } } } @@ -10185,6 +10260,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, item_list.push_back(new Item_string(tmp1.ptr(),tmp1.length(),cs)); else item_list.push_back(item_null); + /* key key_len ref */ if (tab->ref.key_parts) { KEY *key_info=table->key_info+ tab->ref.key; @@ -10200,7 +10276,8 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, { if (tmp2.length()) tmp2.append(','); - tmp2.append((*ref)->name()); + tmp2.append((*ref)->name(), strlen((*ref)->name()), + system_charset_info); } item_list.push_back(new Item_string(tmp2.ptr(),tmp2.length(),cs)); } @@ -10269,9 +10346,11 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, item_list.push_back(item_null); item_list.push_back(item_null); } + /* rows */ item_list.push_back(new Item_int((longlong) (ulonglong) join->best_positions[i]. records_read, 21)); + /* extra */ my_bool key_read=table->key_read; if ((tab->type == JT_NEXT || tab->type == JT_CONST) && table->used_keys.is_set(tab->index)) @@ -10336,67 +10415,56 @@ int mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result) DBUG_ENTER("mysql_explain_union"); int res= 0; SELECT_LEX *first= unit->first_select(); + for (SELECT_LEX *sl= first; sl; sl= sl->next_select()) { // drop UNCACHEABLE_EXPLAIN, because it is for internal usage only uint8 uncacheable= (sl->uncacheable & ~UNCACHEABLE_EXPLAIN); - - res= mysql_explain_select(thd, sl, - (((&thd->lex->select_lex)==sl)? - ((thd->lex->all_selects_list != sl) ? - primary_key_name : "SIMPLE"): - ((sl == first)? - ((sl->linkage == DERIVED_TABLE_TYPE) ? - "DERIVED": - ((uncacheable & UNCACHEABLE_DEPENDENT) ? - "DEPENDENT SUBQUERY": - (uncacheable?"UNCACHEABLE SUBQUERY": - "SUBQUERY"))): - ((uncacheable & UNCACHEABLE_DEPENDENT) ? - "DEPENDENT UNION": - uncacheable?"UNCACHEABLE UNION": - "UNION"))), - result); - if (res) - break; - - } - if (res > 0 || thd->net.report_error) - res= -1; // mysql_explain_select do not report error - DBUG_RETURN(res); -} - - -int mysql_explain_select(THD *thd, SELECT_LEX *select_lex, char const *type, - select_result *result) -{ - DBUG_ENTER("mysql_explain_select"); - DBUG_PRINT("info", ("Select 0x%lx, type %s", (ulong)select_lex, type)) - select_lex->type= type; - thd->lex->current_select= select_lex; - SELECT_LEX_UNIT *unit= select_lex->master_unit(); - if (select_lex == unit->global_parameters && - unit->first_select()->next_select()) - { - unit->offset_limit_cnt= 0; - unit->select_limit_cnt= HA_POS_ERROR; + sl->type= (((&thd->lex->select_lex)==sl)? + ((thd->lex->all_selects_list != sl) ? + primary_key_name : "SIMPLE"): + ((sl == first)? + ((sl->linkage == DERIVED_TABLE_TYPE) ? + "DERIVED": + ((uncacheable & UNCACHEABLE_DEPENDENT) ? + "DEPENDENT SUBQUERY": + (uncacheable?"UNCACHEABLE SUBQUERY": + "SUBQUERY"))): + ((uncacheable & UNCACHEABLE_DEPENDENT) ? + "DEPENDENT UNION": + uncacheable?"UNCACHEABLE UNION": + "UNION"))); + sl->options|= SELECT_DESCRIBE; + } + if (first->next_select()) + { + unit->fake_select_lex->select_number= UINT_MAX; // jost for initialization + unit->fake_select_lex->type= "UNION RESULT"; + unit->fake_select_lex->options|= SELECT_DESCRIBE; + if (!(res= unit->prepare(thd, result, SELECT_NO_UNLOCK | SELECT_DESCRIBE))) + res= unit->exec(); + res|= unit->cleanup(); } else - unit->set_limit(select_lex, select_lex); - int res= mysql_select(thd, &select_lex->ref_pointer_array, - (TABLE_LIST*) select_lex->table_list.first, - select_lex->with_wild, select_lex->item_list, - select_lex->where, - select_lex->order_list.elements + - select_lex->group_list.elements, - (ORDER*) select_lex->order_list.first, - (ORDER*) select_lex->group_list.first, - select_lex->having, + { + thd->lex->current_select= first; + res= mysql_select(thd, &first->ref_pointer_array, + (TABLE_LIST*) first->table_list.first, + first->with_wild, first->item_list, + first->where, + first->order_list.elements + + first->group_list.elements, + (ORDER*) first->order_list.first, + (ORDER*) first->group_list.first, + first->having, (ORDER*) thd->lex->proc_list.first, - select_lex->options | thd->options | SELECT_DESCRIBE, - result, unit, select_lex); + first->options | thd->options | SELECT_DESCRIBE, + result, unit, first); + } + if (res > 0 || thd->net.report_error) + res= -1; // mysql_explain_select do not report error DBUG_RETURN(res); } @@ -10411,23 +10479,23 @@ void st_select_lex::print(THD *thd, String *str) //options if (options & SELECT_STRAIGHT_JOIN) str->append("straight_join ", 14); - if ((thd->lex->lock_option & TL_READ_HIGH_PRIORITY) && + if ((thd->lex->lock_option == TL_READ_HIGH_PRIORITY) && (this == &thd->lex->select_lex)) str->append("high_priority ", 14); if (options & SELECT_DISTINCT) str->append("distinct ", 9); if (options & SELECT_SMALL_RESULT) - str->append("small_result ", 13); + str->append("sql_small_result ", 17); if (options & SELECT_BIG_RESULT) - str->append("big_result ", 11); + str->append("sql_big_result ", 15); if (options & OPTION_BUFFER_RESULT) - str->append("buffer_result ", 14); + str->append("sql_buffer_result ", 18); if (options & OPTION_FOUND_ROWS) - str->append("calc_found_rows ", 16); + str->append("sql_calc_found_rows ", 20); if (!thd->lex->safe_to_cache_query) - str->append("no_cache ", 9); + str->append("sql_no_cache ", 13); if (options & OPTION_TO_QUERY_CACHE) - str->append("cache ", 6); + str->append("sql_cache ", 10); //Item List bool first= 1; @@ -10555,3 +10623,27 @@ void st_select_lex::print(THD *thd, String *str) // PROCEDURE unsupported here } + + +/* + change select_result object of JOIN + + SYNOPSIS + JOIN::change_result() + res new select_result object + + RETURN + 0 - OK + -1 - error +*/ + +int JOIN::change_result(select_result *res) +{ + DBUG_ENTER("JOIN::change_result"); + result= res; + if (!procedure && result->prepare(fields_list, select_lex->master_unit())) + { + DBUG_RETURN(-1); + } + DBUG_RETURN(0); +} diff --git a/sql/sql_select.h b/sql/sql_select.h index b8a56fda757..85033f1e167 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -306,6 +306,7 @@ class JOIN :public Sql_alloc return (do_send_rows && tmp_table_param.sum_func_count != 0 && !group_list); } + int change_result(select_result *result); }; diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 95e9da6ec62..e481a961288 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -422,7 +422,7 @@ mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path, { if (lower_case_table_names) { - if (wild_case_compare(system_charset_info,file->name,wild)) + if (wild_case_compare(files_charset_info, file->name, wild)) continue; } else if (wild_compare(file->name,wild,0)) @@ -1207,7 +1207,7 @@ store_create_info(THD *thd, TABLE *table, String *packet) { List<Item> field_list; char tmp[MAX_FIELD_WIDTH], *for_str, buff[128], *end, *alias; - String type(tmp, sizeof(tmp),&my_charset_bin); + String type(tmp, sizeof(tmp), system_charset_info); Field **ptr,*field; uint primary_key; KEY *key_info; @@ -1254,7 +1254,7 @@ store_create_info(THD *thd, TABLE *table, String *packet) type.set(tmp, sizeof(tmp),&my_charset_bin); field->sql_type(type); - packet->append(type.ptr(),type.length()); + packet->append(type.ptr(), type.length(), system_charset_info); if (field->has_charset() && !limited_mysql_mode && !foreign_db_mode) { @@ -1313,7 +1313,7 @@ store_create_info(THD *thd, TABLE *table, String *packet) else if (field->maybe_null()) packet->append("NULL", 4); // Null as default else - packet->append(tmp,0); + packet->append(tmp); } if (!foreign_db_mode && !limited_mysql_mode && @@ -1597,10 +1597,13 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose) thd_info->query=0; if (tmp->query) { - /* query_length is always set before tmp->query */ + /* + query_length is always set to 0 when we set query = NULL; see + the comment in sql_class.h why this prevents crashes in possible + races with query_length + */ uint length= min(max_query_length, tmp->query_length); - thd_info->query=(char*) thd->memdup(tmp->query,length+1); - thd_info->query[length]=0; + thd_info->query=(char*) thd->strmake(tmp->query,length); } thread_infos.append(thd_info); } diff --git a/sql/sql_string.cc b/sql/sql_string.cc index 7c3f9bc5cde..4d85438b03f 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -416,16 +416,18 @@ bool String::append(const String &s) /* - Append a latin1 string to the a string of the current character set + Append an ASCII string to the a string of the current character set */ - bool String::append(const char *s,uint32 arg_length) { - if (!arg_length) // Default argument - if (!(arg_length= (uint32) strlen(s))) - return FALSE; - if (str_charset->mbmaxlen > 1) + if (!arg_length) + return FALSE; + + /* + For an ASCII incompatible string, e.g. UCS-2, we need to convert + */ + if (str_charset->mbminlen > 1) { uint32 add_length=arg_length * str_charset->mbmaxlen; if (realloc(str_length+ add_length)) @@ -434,6 +436,10 @@ bool String::append(const char *s,uint32 arg_length) s, arg_length, &my_charset_latin1); return FALSE; } + + /* + For an ASCII compatinble string we can just append. + */ if (realloc(str_length+arg_length)) return TRUE; memcpy(Ptr+str_length,s,arg_length); @@ -443,29 +449,39 @@ bool String::append(const char *s,uint32 arg_length) /* + Append a 0-terminated ASCII string +*/ + +bool String::append(const char *s) +{ + return append(s, strlen(s)); +} + + +/* Append a string in the given charset to the string with character set recoding */ - bool String::append(const char *s,uint32 arg_length, CHARSET_INFO *cs) { - if (!arg_length) // Default argument - if (!(arg_length= (uint32) strlen(s))) - return FALSE; - if (cs != str_charset && str_charset->mbmaxlen > 1) + uint32 dummy_offset; + + if (needs_conversion(arg_length, cs, str_charset, &dummy_offset)) { - uint32 add_length=arg_length * str_charset->mbmaxlen; - if (realloc(str_length+ add_length)) + uint32 add_length= arg_length / cs->mbminlen * str_charset->mbmaxlen; + if (realloc(str_length + add_length)) return TRUE; str_length+= copy_and_convert(Ptr+str_length, add_length, str_charset, s, arg_length, cs); - return FALSE; } - if (realloc(str_length+arg_length)) - return TRUE; - memcpy(Ptr+str_length,s,arg_length); - str_length+=arg_length; + else + { + if (realloc(str_length + arg_length)) + return TRUE; + memcpy(Ptr + str_length, s, arg_length); + str_length+= arg_length; + } return FALSE; } @@ -554,40 +570,6 @@ skip: } /* - Search after a string without regarding to case - This needs to be replaced when we have character sets per string -*/ - -int String::strstr_case(const String &s,uint32 offset) -{ - if (s.length()+offset <= str_length) - { - if (!s.length()) - return ((int) offset); // Empty string is always found - - register const char *str = Ptr+offset; - register const char *search=s.ptr(); - const char *end=Ptr+str_length-s.length()+1; - const char *search_end=s.ptr()+s.length(); -skip: - while (str != end) - { - if (str_charset->sort_order[*str++] == str_charset->sort_order[*search]) - { - register char *i,*j; - i=(char*) str; j=(char*) search+1; - while (j != search_end) - if (str_charset->sort_order[*i++] != - str_charset->sort_order[*j++]) - goto skip; - return (int) (str-Ptr) -1; - } - } - } - return -1; -} - -/* ** Search string from end. Offset is offset to the end of string */ @@ -871,3 +853,23 @@ void String::print(String *str) } } } + + +/* + Exchange state of this object and argument. + + SYNOPSIS + String::swap() + + RETURN + Target string will contain state of this object and vice versa. +*/ + +void String::swap(String &s) +{ + swap_variables(char *, Ptr, s.Ptr); + swap_variables(uint32, str_length, s.str_length); + swap_variables(uint32, Alloced_length, s.Alloced_length); + swap_variables(bool, alloced, s.alloced); + swap_variables(CHARSET_INFO*, str_charset, s.str_charset); +} diff --git a/sql/sql_string.h b/sql/sql_string.h index 1c559b597ba..79365b7481b 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -190,13 +190,13 @@ public: bool copy(const char*s,uint32 arg_length, CHARSET_INFO *csfrom, CHARSET_INFO *csto); bool append(const String &s); - bool append(const char *s,uint32 arg_length=0); + bool append(const char *s); + bool append(const char *s,uint32 arg_length); bool append(const char *s,uint32 arg_length, CHARSET_INFO *cs); bool append(IO_CACHE* file, uint32 arg_length); bool append_with_prefill(const char *s, uint32 arg_length, uint32 full_length, char fill_char); int strstr(const String &search,uint32 offset=0); // Returns offset to substring or -1 - int strstr_case(const String &s,uint32 offset=0); int strrstr(const String &search,uint32 offset=0); // Returns offset to substring or -1 bool replace(uint32 offset,uint32 arg_length,const char *to,uint32 length); bool replace(uint32 offset,uint32 arg_length,const String &to); @@ -301,4 +301,7 @@ public: return FALSE; } void print(String *print); + + /* Swap two string objects. Efficient way to exchange data without memcpy. */ + void swap(String &s); }; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index f6087392dc1..d05b5a6cbc7 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000-2003 MySQL AB +/* Copyright (C) 2000-2004 MySQL AB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -635,12 +635,13 @@ int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, /* Create keys */ - List_iterator<Key> key_iterator(keys); + List_iterator<Key> key_iterator(keys), key_iterator2(keys); uint key_parts=0, fk_key_count=0; - List<Key> keys_in_order; // Add new keys here bool primary_key=0,unique_key=0; - Key *key; + Key *key, *key2; uint tmp, key_number; + /* special marker for keys to be ignored */ + static char ignore_key[1]; /* Calculate number of key segements */ *key_count= 0; @@ -673,7 +674,40 @@ int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, my_error(ER_TOO_LONG_IDENT, MYF(0), key->name); DBUG_RETURN(-1); } - key_parts+=key->columns.elements; + key_iterator2.rewind (); + if (key->type != Key::FOREIGN_KEY) + { + while ((key2 = key_iterator2++) != key) + { + /* + foreign_key_prefix(key, key2) returns 0 if key or key2, or both, is + 'generated', and a generated key is a prefix of the other key. + Then we do not need the generated shorter key. + */ + if ((key2->type != Key::FOREIGN_KEY && + key2->name != ignore_key && + !foreign_key_prefix(key, key2))) + { + /* TODO: issue warning message */ + /* mark that the generated key should be ignored */ + if (!key2->generated || + (key->generated && key->columns.elements < + key2->columns.elements)) + key->name= ignore_key; + else + { + key2->name= ignore_key; + key_parts-= key2->columns.elements; + (*key_count)--; + } + break; + } + } + } + if (key->name != ignore_key) + key_parts+=key->columns.elements; + else + (*key_count)--; if (key->name && !tmp_table && !my_strcasecmp(system_charset_info,key->name,primary_key_name)) { @@ -700,16 +734,26 @@ int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, uint key_length=0; key_part_spec *column; + if (key->name == ignore_key) + { + /* ignore redundant keys */ + do + key=key_iterator++; + while (key && key->name == ignore_key); + if (!key) + break; + } + switch(key->type){ case Key::MULTIPLE: - key_info->flags = 0; + key_info->flags= 0; break; case Key::FULLTEXT: - key_info->flags = HA_FULLTEXT; + key_info->flags= HA_FULLTEXT; break; case Key::SPATIAL: #ifdef HAVE_SPATIAL - key_info->flags = HA_SPATIAL; + key_info->flags= HA_SPATIAL; break; #else my_printf_error(ER_FEATURE_DISABLED,ER(ER_FEATURE_DISABLED),MYF(0), @@ -720,8 +764,11 @@ int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, key_number--; // Skip this key continue; default: - key_info->flags = HA_NOSAME; + key_info->flags = HA_NOSAME; + break; } + if (key->generated) + key_info->flags|= HA_GENERATED_KEY; key_info->key_parts=(uint8) key->columns.elements; key_info->key_part=key_part_info; @@ -745,7 +792,7 @@ int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, */ /* TODO: Add proper checks if handler supports key_type and algorithm */ - if (key_info->flags == HA_SPATIAL) + if (key_info->flags & HA_SPATIAL) { if (key_info->key_parts != 1) { @@ -1108,6 +1155,8 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, my_snprintf(path, sizeof(path), "%s%s%lx_%lx_%x%s", mysql_tmpdir, tmp_file_prefix, current_pid, thd->thread_id, thd->tmp_table++, reg_ext); + if (lower_case_table_names) + my_casedn_str(files_charset_info, path); create_info->table_options|=HA_CREATE_DELAY_KEY_WRITE; } else @@ -1339,11 +1388,11 @@ mysql_rename_table(enum db_type base, { /* Table handler expects to get all file names as lower case */ strmov(tmp_from, old_name); - my_casedn_str(system_charset_info, tmp_from); + my_casedn_str(files_charset_info, tmp_from); old_name= tmp_from; strmov(tmp_to, new_name); - my_casedn_str(system_charset_info, tmp_to); + my_casedn_str(files_charset_info, tmp_to); new_name= tmp_to; } my_snprintf(from, sizeof(from), "%s/%s/%s", @@ -2030,6 +2079,8 @@ int mysql_create_like_table(THD* thd, TABLE_LIST* table, my_snprintf(dst_path, sizeof(dst_path), "%s%s%lx_%lx_%x%s", mysql_tmpdir, tmp_file_prefix, current_pid, thd->thread_id, thd->tmp_table++, reg_ext); + if (lower_case_table_names) + my_casedn_str(files_charset_info, dst_path); create_info->table_options|= HA_CREATE_DELAY_KEY_WRITE; } else @@ -2409,14 +2460,10 @@ int mysql_drop_indexes(THD *thd, TABLE_LIST *table_list, int mysql_alter_table(THD *thd,char *new_db, char *new_name, HA_CREATE_INFO *create_info, TABLE_LIST *table_list, - List<create_field> &fields, - List<Key> &keys,List<Alter_drop> &drop_list, - List<Alter_column> &alter_list, - uint order_num, ORDER *order, uint alter_flags, + List<create_field> &fields, List<Key> &keys, + uint order_num, ORDER *order, enum enum_duplicates handle_duplicates, - enum enum_enable_or_disable keys_onoff, - enum tablespace_op_type tablespace_op, - bool simple_alter) + ALTER_INFO *alter_info) { TABLE *table,*new_table; int error; @@ -2441,9 +2488,9 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, mysql_ha_closeall(thd, table_list); /* DISCARD/IMPORT TABLESPACE is always alone in an ALTER TABLE */ - if (tablespace_op != NO_TABLESPACE_OP) + if (alter_info->tablespace_op != NO_TABLESPACE_OP) DBUG_RETURN(mysql_discard_or_import_tablespace(thd,table_list, - tablespace_op)); + alter_info->tablespace_op)); if (!(table=open_ltable(thd,table_list,TL_WRITE_ALLOW_READ))) DBUG_RETURN(-1); @@ -2456,10 +2503,10 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, { if (lower_case_table_names != 2) { - my_casedn_str(system_charset_info, new_name_buff); + my_casedn_str(files_charset_info, new_name_buff); new_alias= new_name; // Create lower case table name } - my_casedn_str(system_charset_info, new_name); + my_casedn_str(files_charset_info, new_name); } if (new_db == db && !my_strcasecmp(table_alias_charset, new_name_buff, table_name)) @@ -2512,7 +2559,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, create_info->row_type=table->row_type; thd->proc_info="setup"; - if (simple_alter && !table->tmp_table) + if (alter_info->is_simple && !table->tmp_table) { error=0; if (new_name != table_name || new_db != db) @@ -2538,21 +2585,21 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, if (!error) { - switch (keys_onoff) { + switch (alter_info->keys_onoff) { case LEAVE_AS_IS: break; case ENABLE: VOID(pthread_mutex_lock(&LOCK_open)); wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN); VOID(pthread_mutex_unlock(&LOCK_open)); - error= table->file->enable_indexes(); + error= table->file->enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE); /* COND_refresh will be signaled in close_thread_tables() */ break; case DISABLE: VOID(pthread_mutex_lock(&LOCK_open)); wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN); VOID(pthread_mutex_unlock(&LOCK_open)); - error=table->file->disable_indexes(0, 1); + error=table->file->disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE); /* COND_refresh will be signaled in close_thread_tables() */ break; } @@ -2597,9 +2644,9 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, create_info->default_table_charset= table->table_charset; restore_record(table,default_values); // Empty record for DEFAULT - List_iterator<Alter_drop> drop_it(drop_list); + List_iterator<Alter_drop> drop_it(alter_info->drop_list); List_iterator<create_field> def_it(fields); - List_iterator<Alter_column> alter_it(alter_list); + List_iterator<Alter_column> alter_it(alter_info->alter_list); List<create_field> create_list; // Add new fields here List<Key> key_list; // Add new keys here create_field *def; @@ -2703,9 +2750,10 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, find_it.after(def); // Put element after this } } - if (alter_list.elements) + if (alter_info->alter_list.elements) { - my_error(ER_BAD_FIELD_ERROR,MYF(0),alter_list.head()->name,table_name); + my_error(ER_BAD_FIELD_ERROR,MYF(0),alter_info->alter_list.head()->name, + table_name); DBUG_RETURN(-1); } if (!create_list.elements) @@ -2787,6 +2835,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, Key::FULLTEXT : Key::MULTIPLE)), key_name, key_info->algorithm, + test(key_info->flags & HA_GENERATED_KEY), key_parts)); } { @@ -2804,14 +2853,16 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, } } - if (drop_list.elements) + if (alter_info->drop_list.elements) { - my_error(ER_CANT_DROP_FIELD_OR_KEY,MYF(0),drop_list.head()->name); + my_error(ER_CANT_DROP_FIELD_OR_KEY,MYF(0), + alter_info->drop_list.head()->name); goto err; } - if (alter_list.elements) + if (alter_info->alter_list.elements) { - my_error(ER_CANT_DROP_FIELD_OR_KEY,MYF(0),alter_list.head()->name); + my_error(ER_CANT_DROP_FIELD_OR_KEY,MYF(0), + alter_info->alter_list.head()->name); goto err; } @@ -2820,7 +2871,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, current_pid, thd->thread_id); /* Safety fix for innodb */ if (lower_case_table_names) - my_casedn_str(system_charset_info, tmp_name); + my_casedn_str(files_charset_info, tmp_name); create_info->db_type=new_db_type; if (!create_info->comment) create_info->comment=table->comment; @@ -2984,6 +3035,8 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, thd->proc_info="rename result table"; my_snprintf(old_name, sizeof(old_name), "%s2-%lx-%lx", tmp_file_prefix, current_pid, thd->thread_id); + if (lower_case_table_names) + my_casedn_str(files_charset_info, old_name); if (new_name != table_name || new_db != db) { if (!access(new_name_buff,F_OK)) diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 794fbc74c73..73d4616b313 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -41,7 +41,7 @@ int mysql_union(THD *thd, LEX *lex, select_result *result, ***************************************************************************/ select_union::select_union(TABLE *table_par) - :table(table_par), not_describe(0) + :table(table_par) { bzero((char*) &info,sizeof(info)); /* @@ -120,7 +120,7 @@ bool select_union::flush() ulong st_select_lex_unit::init_prepare_fake_select_lex(THD *thd) { - ulong options_tmp= thd->options; + ulong options_tmp= thd->options | fake_select_lex->options; thd->lex->current_select= fake_select_lex; offset_limit_cnt= global_parameters->offset_limit; select_limit_cnt= global_parameters->select_limit + @@ -149,6 +149,8 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, select_result *tmp_result; DBUG_ENTER("st_select_lex_unit::prepare"); + describe= test(additional_options & SELECT_DESCRIBE); + /* result object should be reassigned even if preparing already done for max/min subquery (ALL/ANY optimization) @@ -156,7 +158,26 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, result= sel_result; if (prepared) + { + if (describe) + { + /* fast reinit for EXPLAIN */ + for (sl= first_select_in_union(); sl; sl= sl->next_select()) + { + sl->join->result= result; + select_limit_cnt= HA_POS_ERROR; + offset_limit_cnt= 0; + if (!sl->join->procedure && + result->prepare(sl->join->fields_list, this)) + { + DBUG_RETURN(1); + } + sl->join->select_options|= SELECT_DESCRIBE; + sl->join->reinit(); + } + } DBUG_RETURN(0); + } prepared= 1; res= 0; @@ -169,8 +190,9 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, { if (!(tmp_result= union_result= new select_union(0))) goto err; - union_result->not_describe= 1; union_result->tmp_table_param.init(); + if (describe) + tmp_result= sel_result; } else { @@ -329,20 +351,24 @@ int st_select_lex_unit::exec() ulonglong add_rows=0; DBUG_ENTER("st_select_lex_unit::exec"); - if (executed && !uncacheable) + if (executed && !uncacheable && !describe) DBUG_RETURN(0); executed= 1; - if (uncacheable || !item || !item->assigned()) + if (uncacheable || !item || !item->assigned() || describe) { - if (optimized && item && item->assigned()) + if (optimized && item) { - item->assigned(0); // We will reinit & rexecute unit - item->reset(); - table->file->delete_all_rows(); + if (item->assigned()) + { + item->assigned(0); // We will reinit & rexecute unit + item->reset(); + table->file->delete_all_rows(); + } + /* re-enabling indexes for next subselect iteration */ + if (union_distinct && table->file->enable_indexes(HA_KEY_SWITCH_ALL)) + DBUG_ASSERT(1); } - if (union_distinct) // for subselects - table->file->extra(HA_EXTRA_CHANGE_KEY_TO_UNIQUE); for (SELECT_LEX *sl= select_cursor; sl; sl= sl->next_select()) { ha_rows records_at_start= 0; @@ -352,7 +378,7 @@ int st_select_lex_unit::exec() res= sl->join->reinit(); else { - if (sl != global_parameters) + if (sl != global_parameters && !describe) { offset_limit_cnt= sl->offset_limit; select_limit_cnt= sl->select_limit+sl->offset_limit; @@ -364,7 +390,7 @@ int st_select_lex_unit::exec() We can't use LIMIT at this stage if we are using ORDER BY for the whole query */ - if (sl->order_list.first) + if (sl->order_list.first || describe) select_limit_cnt= HA_POS_ERROR; else select_limit_cnt= sl->select_limit+sl->offset_limit; @@ -394,7 +420,11 @@ int st_select_lex_unit::exec() records_at_start= table->file->records; sl->join->exec(); if (sl == union_distinct) - table->file->extra(HA_EXTRA_CHANGE_KEY_TO_DUP); + { + if (table->file->disable_indexes(HA_KEY_SWITCH_ALL)) + DBUG_RETURN(1); + table->no_keyread=1; + } res= sl->join->error; offset_limit_cnt= sl->offset_limit; if (!res && union_result->flush()) @@ -557,3 +587,32 @@ void st_select_lex_unit::reinit_exec_mechanism() } #endif } + + +/* + change select_result object of unit + + SYNOPSIS + st_select_lex_unit::change_result() + result new select_result object + old_result old select_result object + + RETURN + 0 - OK + -1 - error +*/ + +int st_select_lex_unit::change_result(select_subselect *result, + select_subselect *old_result) +{ + int res= 0; + for (SELECT_LEX *sl= first_select_in_union(); sl; sl= sl->next_select()) + { + if (sl->join && sl->join->result == old_result) + if ((res= sl->join->change_result(result))) + return (res); + } + if (fake_select_lex && fake_select_lex->join) + res= fake_select_lex->join->change_result(result); + return (res); +} diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 0874abaca9c..40c0626b33c 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -977,11 +977,7 @@ create: THD *thd= YYTHD; LEX *lex=Lex; lex->sql_command= SQLCOM_CREATE_TABLE; - if (!lex->select_lex.add_table_to_list(thd,$5, - ($2 & - HA_LEX_CREATE_TMP_TABLE ? - &tmp_table_alias : - (LEX_STRING*) 0), + if (!lex->select_lex.add_table_to_list(thd, $5, NULL, TL_OPTION_UPDATING, (using_update_log ? TL_READ_NO_INSERT: @@ -1015,7 +1011,7 @@ create: { LEX *lex=Lex; - lex->key_list.push_back(new Key($2,$4.str, $5, lex->col_list)); + lex->key_list.push_back(new Key($2,$4.str, $5, 0, lex->col_list)); lex->col_list.empty(); } | CREATE DATABASE opt_if_not_exists ident @@ -2218,7 +2214,6 @@ merge_insert_types: opt_select_from: opt_limit_clause {} - | FROM DUAL_SYM {} | select_from select_lock_type; udf_func_type: @@ -2252,25 +2247,29 @@ key_def: key_type opt_ident key_alg '(' key_list ')' { LEX *lex=Lex; - lex->key_list.push_back(new Key($1,$2, $3, lex->col_list)); + lex->key_list.push_back(new Key($1,$2, $3, 0, lex->col_list)); lex->col_list.empty(); /* Alloced by sql_alloc */ } | opt_constraint constraint_key_type opt_ident key_alg '(' key_list ')' { LEX *lex=Lex; const char *key_name= $3 ? $3:$1; - lex->key_list.push_back(new Key($2, key_name, $4, lex->col_list)); + lex->key_list.push_back(new Key($2, key_name, $4, 0, + lex->col_list)); lex->col_list.empty(); /* Alloced by sql_alloc */ } | opt_constraint FOREIGN KEY_SYM opt_ident '(' key_list ')' references { LEX *lex=Lex; - lex->key_list.push_back(new foreign_key($4, lex->col_list, + lex->key_list.push_back(new foreign_key($4 ? $4:$1, lex->col_list, $8, lex->ref_list, lex->fk_delete_opt, lex->fk_update_opt, lex->fk_match_option)); + lex->key_list.push_back(new Key(Key::MULTIPLE, $4 ? $4 : $1, + HA_KEY_ALG_UNDEF, 1, + lex->col_list)); lex->col_list.empty(); /* Alloced by sql_alloc */ } | constraint opt_check_constraint @@ -2521,25 +2520,25 @@ attribute: { LEX *lex=Lex; lex->type|= AUTO_INCREMENT_FLAG | NOT_NULL_FLAG | UNIQUE_FLAG; - lex->alter_flags|= ALTER_ADD_INDEX; + lex->alter_info.flags|= ALTER_ADD_INDEX; } | opt_primary KEY_SYM { LEX *lex=Lex; lex->type|= PRI_KEY_FLAG | NOT_NULL_FLAG; - lex->alter_flags|= ALTER_ADD_INDEX; + lex->alter_info.flags|= ALTER_ADD_INDEX; } | UNIQUE_SYM { LEX *lex=Lex; lex->type|= UNIQUE_FLAG; - lex->alter_flags|= ALTER_ADD_INDEX; + lex->alter_info.flags|= ALTER_ADD_INDEX; } | UNIQUE_SYM KEY_SYM { LEX *lex=Lex; lex->type|= UNIQUE_KEY_FLAG; - lex->alter_flags|= ALTER_ADD_INDEX; + lex->alter_info.flags|= ALTER_ADD_INDEX; } | COMMENT_SYM TEXT_STRING_sys { Lex->comment= &$2; } | BINARY { Lex->type|= BINCMP_FLAG; } @@ -2787,18 +2786,15 @@ alter: lex->create_list.empty(); lex->key_list.empty(); lex->col_list.empty(); - lex->drop_list.empty(); - lex->alter_list.empty(); lex->select_lex.init_order(); lex->select_lex.db=lex->name=0; bzero((char*) &lex->create_info,sizeof(lex->create_info)); lex->create_info.db_type= DB_TYPE_DEFAULT; lex->create_info.default_table_charset= thd->variables.collation_database; lex->create_info.row_type= ROW_TYPE_NOT_USED; - lex->alter_keys_onoff=LEAVE_AS_IS; - lex->tablespace_op=NO_TABLESPACE_OP; - lex->simple_alter=1; - lex->alter_flags=0; + lex->alter_info.clear(); + lex->alter_info.is_simple= 1; + lex->alter_info.flags= 0; } alter_list {} @@ -2841,8 +2837,8 @@ alter: ; alter_list: - | DISCARD TABLESPACE { Lex->tablespace_op=DISCARD_TABLESPACE; } - | IMPORT TABLESPACE { Lex->tablespace_op=IMPORT_TABLESPACE; } + | DISCARD TABLESPACE { Lex->alter_info.tablespace_op= DISCARD_TABLESPACE; } + | IMPORT TABLESPACE { Lex->alter_info.tablespace_op= IMPORT_TABLESPACE; } | alter_list_item | alter_list ',' alter_list_item; @@ -2851,24 +2847,24 @@ add_column: { LEX *lex=Lex; lex->change=0; - lex->alter_flags|= ALTER_ADD_COLUMN; + lex->alter_info.flags|= ALTER_ADD_COLUMN; }; alter_list_item: - add_column column_def opt_place { Lex->simple_alter=0; } + add_column column_def opt_place { Lex->alter_info.is_simple= 0; } | ADD key_def { LEX *lex=Lex; - lex->simple_alter=0; - lex->alter_flags|= ALTER_ADD_INDEX; + lex->alter_info.is_simple= 0; + lex->alter_info.flags|= ALTER_ADD_INDEX; } - | add_column '(' field_list ')' { Lex->simple_alter=0; } + | add_column '(' field_list ')' { Lex->alter_info.is_simple= 0; } | CHANGE opt_column field_ident { LEX *lex=Lex; lex->change= $3.str; - lex->simple_alter=0; - lex->alter_flags|= ALTER_CHANGE_COLUMN; + lex->alter_info.is_simple= 0; + lex->alter_info.flags|= ALTER_CHANGE_COLUMN; } field_spec opt_place | MODIFY_SYM opt_column field_ident @@ -2878,8 +2874,8 @@ alter_list_item: lex->default_value= lex->on_update_value= 0; lex->comment=0; lex->charset= NULL; - lex->simple_alter=0; - lex->alter_flags|= ALTER_CHANGE_COLUMN; + lex->alter_info.is_simple= 0; + lex->alter_info.flags|= ALTER_CHANGE_COLUMN; } type opt_attribute { @@ -2897,50 +2893,51 @@ alter_list_item: | DROP opt_column field_ident opt_restrict { LEX *lex=Lex; - lex->drop_list.push_back(new Alter_drop(Alter_drop::COLUMN, - $3.str)); - lex->simple_alter=0; - lex->alter_flags|= ALTER_DROP_COLUMN; + lex->alter_info.drop_list.push_back(new Alter_drop(Alter_drop::COLUMN, + $3.str)); + lex->alter_info.is_simple= 0; + lex->alter_info.flags|= ALTER_DROP_COLUMN; } - | DROP FOREIGN KEY_SYM opt_ident { Lex->simple_alter=0; } + | DROP FOREIGN KEY_SYM opt_ident { Lex->alter_info.is_simple= 0; } | DROP PRIMARY_SYM KEY_SYM { LEX *lex=Lex; - lex->drop_list.push_back(new Alter_drop(Alter_drop::KEY, - primary_key_name)); - lex->simple_alter=0; - lex->alter_flags|= ALTER_DROP_INDEX; + lex->alter_info.drop_list.push_back(new Alter_drop(Alter_drop::KEY, + primary_key_name)); + lex->alter_info.is_simple= 0; + lex->alter_info.flags|= ALTER_DROP_INDEX; } | DROP key_or_index field_ident { LEX *lex=Lex; - lex->drop_list.push_back(new Alter_drop(Alter_drop::KEY, - $3.str)); - lex->simple_alter=0; - lex->alter_flags|= ALTER_DROP_INDEX; + lex->alter_info.drop_list.push_back(new Alter_drop(Alter_drop::KEY, + $3.str)); + lex->alter_info.is_simple= 0; + lex->alter_info.flags|= ALTER_DROP_INDEX; } - | DISABLE_SYM KEYS { Lex->alter_keys_onoff=DISABLE; } - | ENABLE_SYM KEYS { Lex->alter_keys_onoff=ENABLE; } + | DISABLE_SYM KEYS { Lex->alter_info.keys_onoff= DISABLE; } + | ENABLE_SYM KEYS { Lex->alter_info.keys_onoff= ENABLE; } | ALTER opt_column field_ident SET DEFAULT signed_literal { LEX *lex=Lex; - lex->alter_list.push_back(new Alter_column($3.str,$6)); - lex->simple_alter=0; - lex->alter_flags|= ALTER_CHANGE_COLUMN; + lex->alter_info.alter_list.push_back(new Alter_column($3.str,$6)); + lex->alter_info.is_simple= 0; + lex->alter_info.flags|= ALTER_CHANGE_COLUMN; } | ALTER opt_column field_ident DROP DEFAULT { LEX *lex=Lex; - lex->alter_list.push_back(new Alter_column($3.str,(Item*) 0)); - lex->simple_alter=0; - lex->alter_flags|= ALTER_CHANGE_COLUMN; + lex->alter_info.alter_list.push_back(new Alter_column($3.str, + (Item*) 0)); + lex->alter_info.is_simple= 0; + lex->alter_info.flags|= ALTER_CHANGE_COLUMN; } | RENAME opt_to table_ident { LEX *lex=Lex; lex->select_lex.db=$3->db.str; lex->name= $3->table.str; - lex->alter_flags|= ALTER_RENAME; + lex->alter_info.flags|= ALTER_RENAME; } | CONVERT_SYM TO_SYM charset charset_name_or_default opt_collate { @@ -2961,19 +2958,19 @@ alter_list_item: lex->create_info.default_table_charset= $5; lex->create_info.used_fields|= (HA_CREATE_USED_CHARSET | HA_CREATE_USED_DEFAULT_CHARSET); - lex->simple_alter= 0; + lex->alter_info.is_simple= 0; } | create_table_options_space_separated { LEX *lex=Lex; - lex->simple_alter=0; - lex->alter_flags|= ALTER_OPTIONS; + lex->alter_info.is_simple= 0; + lex->alter_info.flags|= ALTER_OPTIONS; } | order_clause { LEX *lex=Lex; - lex->simple_alter=0; - lex->alter_flags|= ALTER_ORDER; + lex->alter_info.is_simple= 0; + lex->alter_info.flags|= ALTER_ORDER; }; opt_column: @@ -3387,17 +3384,19 @@ select_part2: select_into: opt_limit_clause {} - | FROM DUAL_SYM /* oracle compatibility: oracle always requires FROM - clause, and DUAL is system table without fields. - Is "SELECT 1 FROM DUAL" any better than - "SELECT 1" ? Hmmm :) */ | into | select_from | into select_from | select_from into; select_from: - FROM join_table_list where_clause group_clause having_clause opt_order_clause opt_limit_clause procedure_clause; + FROM join_table_list where_clause group_clause having_clause + opt_order_clause opt_limit_clause procedure_clause + | FROM DUAL_SYM /* oracle compatibility: oracle always requires FROM + clause, and DUAL is system table without fields. + Is "SELECT 1 FROM DUAL" any better than + "SELECT 1" ? Hmmm :) */ + ; select_options: /* empty*/ @@ -4828,18 +4827,20 @@ delete_limit_clause: }; ULONG_NUM: - NUM { $$= strtoul($1.str,NULL,10); } - | LONG_NUM { $$= (ulong) strtoll($1.str,NULL,10); } - | ULONGLONG_NUM { $$= (ulong) strtoull($1.str,NULL,10); } - | REAL_NUM { $$= strtoul($1.str,NULL,10); } - | FLOAT_NUM { $$= strtoul($1.str,NULL,10); }; + NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } + | LONG_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } + | ULONGLONG_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } + | REAL_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } + | FLOAT_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } + ; ulonglong_num: - NUM { $$= (ulonglong) strtoul($1.str,NULL,10); } - | ULONGLONG_NUM { $$= strtoull($1.str,NULL,10); } - | LONG_NUM { $$= (ulonglong) strtoll($1.str,NULL,10); } - | REAL_NUM { $$= strtoull($1.str,NULL,10); } - | FLOAT_NUM { $$= strtoull($1.str,NULL,10); }; + NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } + | ULONGLONG_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } + | LONG_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } + | REAL_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } + | FLOAT_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } + ; procedure_clause: /* empty */ @@ -4990,9 +4991,9 @@ drop: { LEX *lex=Lex; lex->sql_command= SQLCOM_DROP_INDEX; - lex->drop_list.empty(); - lex->drop_list.push_back(new Alter_drop(Alter_drop::KEY, - $3.str)); + lex->alter_info.drop_list.empty(); + lex->alter_info.drop_list.push_back(new Alter_drop(Alter_drop::KEY, + $3.str)); if (!lex->current_select->add_table_to_list(lex->thd, $5, NULL, TL_OPTION_UPDATING)) YYABORT; @@ -5927,8 +5928,8 @@ literal: | TIMESTAMP text_literal { $$ = $2; }; NUM_literal: - NUM { $$ = new Item_int($1.str, (longlong) strtol($1.str, NULL, 10),$1.length); } - | LONG_NUM { $$ = new Item_int($1.str, (longlong) strtoll($1.str,NULL,10), $1.length); } + NUM { int error; $$ = new Item_int($1.str, (longlong) my_strtoll10($1.str, NULL, &error), $1.length); } + | LONG_NUM { int error; $$ = new Item_int($1.str, (longlong) my_strtoll10($1.str, NULL, &error), $1.length); } | ULONGLONG_NUM { $$ = new Item_uint($1.str, $1.length); } | REAL_NUM { $$ = new Item_real($1.str, $1.length); } | FLOAT_NUM { $$ = new Item_float($1.str, $1.length); } @@ -6207,7 +6208,6 @@ keyword: | DIRECTORY_SYM {} | DISCARD {} | DO_SYM {} - | DUAL_SYM {} | DUMPFILE {} | DUPLICATE_SYM {} | DYNAMIC_SYM {} diff --git a/sql/time.cc b/sql/time.cc index 7fb466f6b97..6d15fa184a1 100644 --- a/sql/time.cc +++ b/sql/time.cc @@ -663,11 +663,25 @@ time_t str_to_timestamp(const char *str,uint length) } +/* + Convert a string to datetime. + + SYNOPSIS + str_to_datetime() + str String to parse (see str_to_TIME() synopsis) + length Length of str + fuzzy_date Flags (see str_to_TIME() synopsis) + + RETURN + -1 if error + datetime value otherwise +*/ + longlong str_to_datetime(const char *str,uint length, uint fuzzy_date) { TIME l_time; if (str_to_TIME(str,length,&l_time,fuzzy_date) <= TIMESTAMP_DATETIME_ERROR) - return(0); + return -1; return (longlong) (l_time.year*LL(10000000000) + l_time.month*LL(100000000)+ l_time.day*LL(1000000)+ @@ -1241,9 +1255,15 @@ const char *get_date_time_format_str(KNOWN_DATE_TIME_FORMAT *format, MySQL doesn't support comparing of date/time/datetime strings that are not in arbutary order as dates are compared as strings in some context) + This functions don't check that given TIME structure members are + in valid range. If they are not, return value won't reflect any + valid date either. Additionally, make_time doesn't take into + account time->day member: it's assumed that days have been converted + to hours already. ****************************************************************************/ -void make_time(DATE_TIME_FORMAT *format, TIME *l_time, String *str) +void make_time(const DATE_TIME_FORMAT *format __attribute__((unused)), + const TIME *l_time, String *str) { long length= my_sprintf((char*) str->ptr(), ((char*) str->ptr(), @@ -1257,7 +1277,8 @@ void make_time(DATE_TIME_FORMAT *format, TIME *l_time, String *str) } -void make_date(DATE_TIME_FORMAT *format, TIME *l_time, String *str) +void make_date(const DATE_TIME_FORMAT *format __attribute__((unused)), + const TIME *l_time, String *str) { long length= my_sprintf((char*) str->ptr(), ((char*) str->ptr(), @@ -1270,7 +1291,8 @@ void make_date(DATE_TIME_FORMAT *format, TIME *l_time, String *str) } -void make_datetime(DATE_TIME_FORMAT *format, TIME *l_time, String *str) +void make_datetime(const DATE_TIME_FORMAT *format __attribute__((unused)), + const TIME *l_time, String *str) { long length= my_sprintf((char*) str->ptr(), ((char*) str->ptr(), @@ -1316,3 +1338,111 @@ void make_truncated_value_warning(THD *thd, const char *str_val, push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_TRUNCATED_WRONG_VALUE, warn_buff); } + + +/* Convert time value to integer in YYYYMMDDHHMMSS format */ + +ulonglong TIME_to_ulonglong_datetime(const TIME *time) +{ + return ((ulonglong) (time->year * 10000UL + + time->month * 100UL + + time->day) * ULL(1000000) + + (ulonglong) (time->hour * 10000UL + + time->minute * 100UL + + time->second)); +} + + +/* Convert TIME value to integer in YYYYMMDD format */ + +ulonglong TIME_to_ulonglong_date(const TIME *time) +{ + return (ulonglong) (time->year * 10000UL + time->month * 100UL + time->day); +} + + +/* + Convert TIME value to integer in HHMMSS format. + This function doesn't take into account time->day member: + it's assumed that days have been converted to hours already. +*/ + +ulonglong TIME_to_ulonglong_time(const TIME *time) +{ + return (ulonglong) (time->hour * 10000UL + + time->minute * 100UL + + time->second); +} + + +/* + Convert struct TIME (date and time split into year/month/day/hour/... + to a number in format YYYYMMDDHHMMSS (DATETIME), + YYYYMMDD (DATE) or HHMMSS (TIME). + + SYNOPSIS + TIME_to_ulonglong() + + DESCRIPTION + The function is used when we need to convert value of time item + to a number if it's used in numeric context, i. e.: + SELECT NOW()+1, CURDATE()+0, CURTIMIE()+0; + SELECT ?+1; + + NOTE + This function doesn't check that given TIME structure members are + in valid range. If they are not, return value won't reflect any + valid date either. +*/ + +ulonglong TIME_to_ulonglong(const TIME *time) +{ + switch (time->time_type) { + case TIMESTAMP_DATETIME: + return TIME_to_ulonglong_datetime(time); + case TIMESTAMP_DATE: + return TIME_to_ulonglong_date(time); + case TIMESTAMP_TIME: + return TIME_to_ulonglong_time(time); + case TIMESTAMP_NONE: + case TIMESTAMP_DATETIME_ERROR: + return ULL(0); + default: + DBUG_ASSERT(0); + } + return 0; +} + + +/* + Convert struct DATE/TIME/DATETIME value to string using built-in + MySQL time conversion formats. + + SYNOPSIS + TIME_to_string() + + NOTE + The string must have at least MAX_DATE_REP_LENGTH bytes reserved. +*/ + +void TIME_to_string(const TIME *time, String *str) +{ + switch (time->time_type) { + case TIMESTAMP_DATETIME: + make_datetime((DATE_TIME_FORMAT*) 0, time, str); + break; + case TIMESTAMP_DATE: + make_date((DATE_TIME_FORMAT*) 0, time, str); + break; + case TIMESTAMP_TIME: + make_time((DATE_TIME_FORMAT*) 0, time, str); + break; + case TIMESTAMP_NONE: + case TIMESTAMP_DATETIME_ERROR: + str->length(0); + str->set_charset(&my_charset_bin); + break; + default: + DBUG_ASSERT(0); + } +} |