From ec54dcb054d5de896b39d3e61a8afba1d8b35915 Mon Sep 17 00:00:00 2001 From: "jimw@mysql.com" <> Date: Fri, 17 Jun 2005 08:56:04 -0700 Subject: [PATCH 01/48] Fix spurious permissions problem when CONVERT_TZ() is used in a multi-table update query. (Bug #9979) --- mysql-test/r/timezone_grant.result | 7 +++++++ mysql-test/t/timezone_grant.test | 12 ++++++++++++ sql/sql_parse.cc | 5 +++-- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/timezone_grant.result b/mysql-test/r/timezone_grant.result index 685f8007ac7..471cacde300 100644 --- a/mysql-test/r/timezone_grant.result +++ b/mysql-test/r/timezone_grant.result @@ -45,6 +45,13 @@ select * from mysql.time_zone_name; ERROR 42000: SELECT command denied to user 'mysqltest_1'@'localhost' for table 'time_zone_name' select Name, convert_tz('2004-11-30 12:00:00', Name, 'UTC') from mysql.time_zone_name; ERROR 42000: SELECT command denied to user 'mysqltest_1'@'localhost' for table 'time_zone_name' +drop table t1, t2; +create table t1 (a int, b datetime); +create table t2 (a int, b varchar(40)); +update t1 set b = '2005-01-01 10:00'; +update t1 set b = convert_tz(b, 'UTC', 'UTC'); +update t1 join t2 on (t1.a = t2.a) set t1.b = '2005-01-01 10:00' where t2.b = 'foo'; +update t1 join t2 on (t1.a = t2.a) set t1.b = convert_tz('2005-01-01 10:00','UTC','UTC') where t2.b = 'foo'; delete from mysql.user where user like 'mysqltest\_%'; delete from mysql.db where user like 'mysqltest\_%'; delete from mysql.tables_priv where user like 'mysqltest\_%'; diff --git a/mysql-test/t/timezone_grant.test b/mysql-test/t/timezone_grant.test index 501315668f5..8627866a0bf 100644 --- a/mysql-test/t/timezone_grant.test +++ b/mysql-test/t/timezone_grant.test @@ -61,6 +61,18 @@ select * from mysql.time_zone_name; --error 1142 select Name, convert_tz('2004-11-30 12:00:00', Name, 'UTC') from mysql.time_zone_name; +# +# Bug #9979: Use of CONVERT_TZ in multiple-table UPDATE causes bogus +# privilege error +# +drop table t1, t2; +create table t1 (a int, b datetime); +create table t2 (a int, b varchar(40)); +update t1 set b = '2005-01-01 10:00'; +update t1 set b = convert_tz(b, 'UTC', 'UTC'); +update t1 join t2 on (t1.a = t2.a) set t1.b = '2005-01-01 10:00' where t2.b = 'foo'; +update t1 join t2 on (t1.a = t2.a) set t1.b = convert_tz('2005-01-01 10:00','UTC','UTC') where t2.b = 'foo'; + # Clean-up connection default; delete from mysql.user where user like 'mysqltest\_%'; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index f0ea0a762bd..a515441ee1b 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1938,7 +1938,8 @@ mysql_execute_command(THD *thd) that is not a SHOW command or a select that only access local variables, but for now this is probably good enough. */ - if (tables || &lex->select_lex != lex->all_selects_list) + if (tables || &lex->select_lex != lex->all_selects_list || + lex->time_zone_tables_used) mysql_reset_errors(thd); #ifdef HAVE_REPLICATION @@ -5354,7 +5355,7 @@ int multi_update_precheck(THD *thd, TABLE_LIST *tables) /* Is there tables of subqueries? */ - if (&lex->select_lex != lex->all_selects_list) + if (&lex->select_lex != lex->all_selects_list || lex->time_zone_tables_used) { DBUG_PRINT("info",("Checking sub query list")); for (table= tables; table; table= table->next) From 343e8effa98bbcfbf3efa6912a741630b1b5f089 Mon Sep 17 00:00:00 2001 From: "igor@rurik.mysql.com" <> Date: Fri, 1 Jul 2005 07:40:22 -0700 Subject: [PATCH 02/48] olap.result, olap.test: Added a test case for bug #11543. sql_select.cc: Fixed bug #11543. A ROLLUP query could return a wrong result set when its GROUP BY clause contained references to the same column. --- mysql-test/r/olap.result | 12 ++++++++++++ mysql-test/t/olap.test | 11 +++++++++++ sql/sql_select.cc | 4 ++-- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/olap.result b/mysql-test/r/olap.result index 0c6c4684853..7178895cf80 100644 --- a/mysql-test/r/olap.result +++ b/mysql-test/r/olap.result @@ -504,3 +504,15 @@ IFNULL(a, 'TEST') COALESCE(b, 'TEST') 4 TEST TEST TEST DROP TABLE t1,t2; +CREATE TABLE t1 (a INT(10) NOT NULL, b INT(10) NOT NULL); +INSERT INTO t1 VALUES (1, 1); +INSERT INTO t1 VALUES (1, 2); +SELECT a, b, a AS c, COUNT(*) AS count FROM t1 GROUP BY a, b, c WITH ROLLUP; +a b c count +1 1 1 1 +1 1 NULL 1 +1 2 1 1 +1 2 NULL 1 +1 NULL NULL 2 +NULL NULL NULL 2 +DROP TABLE t1; diff --git a/mysql-test/t/olap.test b/mysql-test/t/olap.test index b9387d15320..9e6adffffcf 100644 --- a/mysql-test/t/olap.test +++ b/mysql-test/t/olap.test @@ -238,3 +238,14 @@ SELECT IFNULL(a, 'TEST'), COALESCE(b, 'TEST') FROM t2 DROP TABLE t1,t2; +# +# Test for bug #11543: ROLLUP query with a repeated column in GROUP BY +# + +CREATE TABLE t1 (a INT(10) NOT NULL, b INT(10) NOT NULL); +INSERT INTO t1 VALUES (1, 1); +INSERT INTO t1 VALUES (1, 2); + +SELECT a, b, a AS c, COUNT(*) AS count FROM t1 GROUP BY a, b, c WITH ROLLUP; + +DROP TABLE t1; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 044dc60e4b6..5866585bb60 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -9322,7 +9322,7 @@ bool JOIN::rollup_init() ORDER *group_tmp; for (group_tmp= group_list; group_tmp; group_tmp= group_tmp->next) { - if (item->eq(*group_tmp->item,0)) + if (*group_tmp->item == item) item->maybe_null= 1; } if (item->type() == Item::FUNC_ITEM) @@ -9444,7 +9444,7 @@ bool JOIN::rollup_make_fields(List &fields_arg, List &sel_fields, for (group_tmp= start_group, i= pos ; group_tmp ; group_tmp= group_tmp->next, i++) { - if (item->eq(*group_tmp->item,0)) + if (*group_tmp->item == item) { /* This is an element that is used by the GROUP BY and should be From adfa1a259603f45da13fca4963b3bd90837b8bcf Mon Sep 17 00:00:00 2001 From: "reggie@linux.site" <> Date: Thu, 14 Jul 2005 06:04:29 -0600 Subject: [PATCH 03/48] Bug #7403 error "Forcing close of thread 1 user: 'xxx'" The problem was that on Windows, the socket was in a blocking state trying to read. Setting killed=1 is not enough to break the socket out of it's read loop. You have to cancel the read request or close the socket (which close_connection does). --- sql/mysqld.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/mysqld.cc b/sql/mysqld.cc index a757c47366d..1434775cff3 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -657,7 +657,7 @@ static void close_connections(void) { DBUG_PRINT("quit",("Informing thread %ld that it's time to die", tmp->thread_id)); - tmp->killed=1; + close_connection(tmp, 0, 1); if (tmp->mysys_var) { tmp->mysys_var->abort=1; From 65a1d74fb149b04c9a536d3efda922202ecb2aff Mon Sep 17 00:00:00 2001 From: "reggie@linux.site" <> Date: Fri, 15 Jul 2005 03:51:55 -0600 Subject: [PATCH 04/48] added a comment referring to the bug report --- sql/mysqld.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 1434775cff3..02a775f64d1 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -657,6 +657,12 @@ static void close_connections(void) { DBUG_PRINT("quit",("Informing thread %ld that it's time to die", tmp->thread_id)); + /* + Re: bug 7403 - close_connection will be called mulitple times + bug a wholesale clean up of our network code is a very large + project. This will wake up the socket on Windows and prevent the + printing of the error message that we are force closing a connection. + */ close_connection(tmp, 0, 1); if (tmp->mysys_var) { From 02599bdf27f05b6f94186ee730c3cde8b2220c8f Mon Sep 17 00:00:00 2001 From: "georg@lmy002.wdf.sap.corp" <> Date: Fri, 15 Jul 2005 17:05:00 +0200 Subject: [PATCH 05/48] minor fix for restoring the previous characterset in test_client_character_set --- tests/mysql_client_test.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 010f467c4ad..77c8d9959e6 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -13617,12 +13617,12 @@ static void test_bug10214() static void test_client_character_set() { MY_CHARSET_INFO cs; - char *csname; + char *csname = (char*) "utf8"; + char *csdefault = (char*)mysql_character_set_name(mysql); int rc; myheader("test_client_character_set"); - csname= (char*) "utf8"; rc= mysql_set_character_set(mysql, csname); DIE_UNLESS(rc == 0); @@ -13630,7 +13630,7 @@ static void test_client_character_set() DIE_UNLESS(!strcmp(cs.csname, "utf8")); DIE_UNLESS(!strcmp(cs.name, "utf8_general_ci")); /* Restore the default character set */ - rc= mysql_query(mysql, "set names default"); + rc= mysql_set_character_set(mysql, csdefault); myquery(rc); } From 10db4d030f5fd1a919549479c7a59296245654f3 Mon Sep 17 00:00:00 2001 From: "pem@mysql.com" <> Date: Fri, 15 Jul 2005 17:19:25 +0200 Subject: [PATCH 06/48] Made .c files compile with a C compiler. --- ndb/src/common/portlib/NdbMutex.c | 4 ++-- ndb/src/common/portlib/NdbThread.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ndb/src/common/portlib/NdbMutex.c b/ndb/src/common/portlib/NdbMutex.c index 18f8548e4e6..4a170d87e5c 100644 --- a/ndb/src/common/portlib/NdbMutex.c +++ b/ndb/src/common/portlib/NdbMutex.c @@ -23,9 +23,9 @@ NdbMutex* NdbMutex_Create(void) { - DBUG_ENTER("NdbMutex_Create"); NdbMutex* pNdbMutex; int result; + DBUG_ENTER("NdbMutex_Create"); pNdbMutex = (NdbMutex*)NdbMem_Allocate(sizeof(NdbMutex)); DBUG_PRINT("info",("NdbMem_Allocate 0x%lx",pNdbMutex)); @@ -42,8 +42,8 @@ NdbMutex* NdbMutex_Create(void) int NdbMutex_Destroy(NdbMutex* p_mutex) { - DBUG_ENTER("NdbMutex_Destroy"); int result; + DBUG_ENTER("NdbMutex_Destroy"); if (p_mutex == NULL) DBUG_RETURN(-1); diff --git a/ndb/src/common/portlib/NdbThread.c b/ndb/src/common/portlib/NdbThread.c index cee4ec018a0..55ebc4c8111 100644 --- a/ndb/src/common/portlib/NdbThread.c +++ b/ndb/src/common/portlib/NdbThread.c @@ -71,10 +71,10 @@ struct NdbThread* NdbThread_Create(NDB_THREAD_FUNC *p_thread_func, const char* p_thread_name, NDB_THREAD_PRIO thread_prio) { - DBUG_ENTER("NdbThread_Create"); struct NdbThread* tmpThread; int result; pthread_attr_t thread_attr; + DBUG_ENTER("NdbThread_Create"); (void)thread_prio; /* remove warning for unused parameter */ From 7d7f34cf1c3aba2f0197101417070b4b049fb609 Mon Sep 17 00:00:00 2001 From: "pem@mysql.com" <> Date: Fri, 15 Jul 2005 17:24:19 +0200 Subject: [PATCH 07/48] Fixed BUG#9538: SProc: Creation fails if we try to SET system variable using @@var_name in proc Made sure we don't lose the tokenizer state when swapping lex in during SET parsing. --- mysql-test/r/sp.result | 13 +++++++++++++ mysql-test/t/sp.test | 19 +++++++++++++++++++ sql/sp_head.cc | 6 ++++++ 3 files changed, 38 insertions(+) diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index 345de49c21d..2bfe975f656 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -3079,4 +3079,17 @@ one 1 delete from t1| drop procedure bug9565_sub| drop procedure bug9565| +drop procedure if exists bug9538| +create procedure bug9538() +set @@sort_buffer_size = 1000000| +set @x = @@sort_buffer_size| +select @@sort_buffer_size| +@@sort_buffer_size +262136 +call bug9538()| +select @@sort_buffer_size| +@@sort_buffer_size +1000000 +set @@sort_buffer_size = @x| +drop procedure bug9538| drop table t1,t2; diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index 13de2090a84..342fd0d9538 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -3855,6 +3855,25 @@ drop procedure bug9565_sub| drop procedure bug9565| +# +# BUG#9538: SProc: Creation fails if we try to SET system variable +# using @@var_name in proc +# +--disable_warnings +drop procedure if exists bug9538| +--enable_warnings +create procedure bug9538() + set @@sort_buffer_size = 1000000| + +set @x = @@sort_buffer_size| +select @@sort_buffer_size| +call bug9538()| +select @@sort_buffer_size| +set @@sort_buffer_size = @x| + +drop procedure bug9538| + + # # BUG#NNNN: New bug synopsis # diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 272456d8c8e..02c006d01ee 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -1023,6 +1023,7 @@ sp_head::reset_lex(THD *thd) DBUG_ENTER("sp_head::reset_lex"); LEX *sublex; LEX *oldlex= thd->lex; + my_lex_states state= oldlex->next_state; // Keep original next_state (void)m_lex.push_front(oldlex); thd->lex= sublex= new st_lex; @@ -1030,6 +1031,11 @@ sp_head::reset_lex(THD *thd) /* Reset most stuff. The length arguments doesn't matter here. */ lex_start(thd, oldlex->buf, (ulong) (oldlex->end_of_query - oldlex->ptr)); + /* + * next_state is normally the same (0), but it happens that we swap lex in + * "mid-sentence", so we must restore it. + */ + sublex->next_state= state; /* We must reset ptr and end_of_query again */ sublex->ptr= oldlex->ptr; sublex->end_of_query= oldlex->end_of_query; From d503283863ba3534f3cf221bef1ac7bcdd0dee7c Mon Sep 17 00:00:00 2001 From: "pem@mysql.com" <> Date: Fri, 15 Jul 2005 19:46:44 +0200 Subject: [PATCH 08/48] Post-review fix: Made test bug9538 test robust. --- mysql-test/r/sp.result | 3 ++- mysql-test/t/sp.test | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index 2bfe975f656..158dd9bb949 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -3083,9 +3083,10 @@ drop procedure if exists bug9538| create procedure bug9538() set @@sort_buffer_size = 1000000| set @x = @@sort_buffer_size| +set @@sort_buffer_size = 2000000| select @@sort_buffer_size| @@sort_buffer_size -262136 +2000000 call bug9538()| select @@sort_buffer_size| @@sort_buffer_size diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index 342fd0d9538..209386fcba4 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -3866,6 +3866,7 @@ create procedure bug9538() set @@sort_buffer_size = 1000000| set @x = @@sort_buffer_size| +set @@sort_buffer_size = 2000000| select @@sort_buffer_size| call bug9538()| select @@sort_buffer_size| From aa032356e26466f271e8647056a5ee53e27865e4 Mon Sep 17 00:00:00 2001 From: "brian@zim.(none)" <> Date: Fri, 15 Jul 2005 11:15:44 -0700 Subject: [PATCH 09/48] Removed dead options, fixed pentium script to no longer ask for isam (its not included in 5.0). --- BUILD/SETUP.sh | 7 +++---- BUILD/compile-pentium-debug-max-no-embedded | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/BUILD/SETUP.sh b/BUILD/SETUP.sh index a507d30e518..32dd915e60e 100755 --- a/BUILD/SETUP.sh +++ b/BUILD/SETUP.sh @@ -48,10 +48,9 @@ global_warnings="-Wimplicit -Wreturn-type -Wswitch -Wtrigraphs -Wcomment -W -Wch c_warnings="$global_warnings -Wunused" cxx_warnings="$global_warnings -Woverloaded-virtual -Wsign-promo -Wreorder -Wctor-dtor-privacy -Wnon-virtual-dtor" -base_max_configs="--with-innodb --with-berkeley-db --with-ndbcluster --with-archive-storage-engine --with-raid --with-openssl --with-raid --with-big-tables" -max_leave_isam_configs="--with-innodb --with-berkeley-db --with-ndbcluster --with-archive-storage-engine --with-federated-storage-engine --with-raid --with-openssl --with-raid --with-embedded-server --with-big-tables" -max_no_es_configs="$max_leave_isam_configs --without-isam" -max_configs="$max_no_es_configs --with-embedded-server" +base_max_configs="--with-innodb --with-berkeley-db --with-ndbcluster --with-archive-storage-engine --with-openssl --with-big-tables --with-blackhole-storage-engine" +max_leave_isam_configs="--with-innodb --with-berkeley-db --with-ndbcluster --with-archive-storage-engine --with-federated-storage-engine --with-blackhole-storage-engine --with-openssl --with-embedded-server --with-big-tables" +max_configs="$base_max_configs --with-embedded-server" path=`dirname $0` . "$path/check-cpu" diff --git a/BUILD/compile-pentium-debug-max-no-embedded b/BUILD/compile-pentium-debug-max-no-embedded index 803a6a9d6d3..dfdf7d0a5e1 100755 --- a/BUILD/compile-pentium-debug-max-no-embedded +++ b/BUILD/compile-pentium-debug-max-no-embedded @@ -6,6 +6,6 @@ path=`dirname $0` extra_flags="$pentium_cflags $debug_cflags $max_cflags" c_warnings="$c_warnings $debug_extra_warnings" cxx_warnings="$cxx_warnings $debug_extra_warnings" -extra_configs="$pentium_configs $debug_configs $max_no_es_configs" +extra_configs="$pentium_configs $debug_configs $base_max_configs" . "$path/FINISH.sh" From eb785d70b8eb7c5792d064461167943f48c5f11f Mon Sep 17 00:00:00 2001 From: "jimw@mysql.com" <> Date: Fri, 15 Jul 2005 12:31:57 -0700 Subject: [PATCH 10/48] Fix error in formatting metadata in mysqltest. --- client/mysqltest.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/mysqltest.c b/client/mysqltest.c index b7f4ceb9c20..b23b77e9bca 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -3141,10 +3141,10 @@ static void run_query_display_metadata(MYSQL_FIELD *field, uint num_fields, int10_to_str((int) field->type, buff, 10); dynstr_append(ds, buff); dynstr_append_mem(ds, "\t", 1); - int10_to_str((int) field->length, buff, 10); + longlong10_to_str((unsigned int) field->length, buff, 10); dynstr_append(ds, buff); dynstr_append_mem(ds, "\t", 1); - int10_to_str((int) field->max_length, buff, 10); + longlong10_to_str((unsigned int) field->max_length, buff, 10); dynstr_append(ds, buff); dynstr_append_mem(ds, "\t", 1); dynstr_append_mem(ds, (char*) (IS_NOT_NULL(field->flags) ? From 6114bee47cbcb085f28844173ad382f865191928 Mon Sep 17 00:00:00 2001 From: "bell@sanja.is.com.ua" <> Date: Sat, 16 Jul 2005 00:01:44 +0300 Subject: [PATCH 11/48] stop evaluation constant functions in WHERE (BUG#4663) correct value of CURRENT_USER() in SP with "security definer" (BUG#7291) --- BitKeeper/etc/config | 2 +- mysql-test/r/sp-security.result | 24 ++++++++++++++++++++ mysql-test/r/view.result | 25 +++++++++++++++++++++ mysql-test/t/sp-security.test | 29 ++++++++++++++++++++++++ mysql-test/t/view.test | 21 +++++++++++++++++ sql/item.cc | 27 ++++++++++++++++++++++ sql/item.h | 1 + sql/item_cmpfunc.cc | 40 ++++++++++++++++++--------------- sql/item_create.cc | 12 ++-------- sql/item_strfunc.cc | 28 +++++++++++++++++------ sql/item_strfunc.h | 26 ++++++++++++++++----- sql/sql_class.h | 2 ++ sql/sql_yacc.yy | 2 +- 13 files changed, 197 insertions(+), 42 deletions(-) diff --git a/BitKeeper/etc/config b/BitKeeper/etc/config index 1ac24031dca..56ae08e5ffa 100644 --- a/BitKeeper/etc/config +++ b/BitKeeper/etc/config @@ -24,7 +24,7 @@ description: MySQL - fast and reliable SQL database # repository is commercial it can be an internal email address or "none" # to disable logging. # -logging: logging@openlogging.org +logging: none # # If this field is set, all checkins will appear to be made by this user, # in effect making this a single user package. Single user packages are diff --git a/mysql-test/r/sp-security.result b/mysql-test/r/sp-security.result index ee72fde7324..327d2ef1de1 100644 --- a/mysql-test/r/sp-security.result +++ b/mysql-test/r/sp-security.result @@ -194,3 +194,27 @@ use test; drop database sptest; delete from mysql.user where user='usera' or user='userb' or user='userc'; delete from mysql.procs_priv where user='usera' or user='userb' or user='userc'; +use test; +select current_user(); +current_user() +root@localhost +select user(); +user() +root@localhost +create procedure bug7291_0 () sql security invoker select current_user(), user(); +create procedure bug7291_1 () sql security definer call bug7291_0(); +create procedure bug7291_2 () sql security invoker call bug7291_0(); +grant execute on procedure bug7291_0 to user1@localhost; +grant execute on procedure bug7291_1 to user1@localhost; +grant execute on procedure bug7291_2 to user1@localhost; +call bug7291_2(); +current_user() user() +user1@localhost user1@localhost +call bug7291_1(); +current_user() user() +root@localhost user1@localhost +drop procedure bug7291_1; +drop procedure bug7291_2; +drop procedure bug7291_0; +REVOKE ALL PRIVILEGES, GRANT OPTION FROM user1@localhost; +drop user user1@localhost; diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index 68cc0c4cb57..75389483317 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -1831,3 +1831,28 @@ select * from v1; t 01:00 drop view v1; +create table t1 (a timestamp default now()); +create table t2 (b timestamp default now()); +create view v1 as select a,b,t1.a < now() from t1,t2 where t1.a < now(); +SHOW CREATE VIEW v1; +View Create View +v1 CREATE ALGORITHM=UNDEFINED VIEW `test`.`v1` AS select sql_no_cache `test`.`t1`.`a` AS `a`,`test`.`t2`.`b` AS `b`,(`test`.`t1`.`a` < now()) AS `t1.a < now()` from `test`.`t1` join `test`.`t2` where (`test`.`t1`.`a` < now()) +drop view v1; +drop table t1, t2; +CREATE TABLE t1 ( a varchar(50) ); +CREATE VIEW v1 AS SELECT * FROM t1 WHERE a = CURRENT_USER(); +SHOW CREATE VIEW v1; +View Create View +v1 CREATE ALGORITHM=UNDEFINED VIEW `test`.`v1` AS select sql_no_cache `test`.`t1`.`a` AS `a` from `test`.`t1` where (`test`.`t1`.`a` = current_user()) +DROP VIEW v1; +CREATE VIEW v1 AS SELECT * FROM t1 WHERE a = VERSION(); +SHOW CREATE VIEW v1; +View Create View +v1 CREATE ALGORITHM=UNDEFINED VIEW `test`.`v1` AS select `test`.`t1`.`a` AS `a` from `test`.`t1` where (`test`.`t1`.`a` = version()) +DROP VIEW v1; +CREATE VIEW v1 AS SELECT * FROM t1 WHERE a = DATABASE(); +SHOW CREATE VIEW v1; +View Create View +v1 CREATE ALGORITHM=UNDEFINED VIEW `test`.`v1` AS select sql_no_cache `test`.`t1`.`a` AS `a` from `test`.`t1` where (`test`.`t1`.`a` = database()) +DROP VIEW v1; +DROP TABLE t1; diff --git a/mysql-test/t/sp-security.test b/mysql-test/t/sp-security.test index e1d8043ccda..204ef68ad7e 100644 --- a/mysql-test/t/sp-security.test +++ b/mysql-test/t/sp-security.test @@ -304,3 +304,32 @@ drop database sptest; delete from mysql.user where user='usera' or user='userb' or user='userc'; delete from mysql.procs_priv where user='usera' or user='userb' or user='userc'; +# +# correct value from current_user() in function run from "security definer" +# (BUG#7291) +# +connection con1root; +use test; + +select current_user(); +select user(); +create procedure bug7291_0 () sql security invoker select current_user(), user(); +create procedure bug7291_1 () sql security definer call bug7291_0(); +create procedure bug7291_2 () sql security invoker call bug7291_0(); +grant execute on procedure bug7291_0 to user1@localhost; +grant execute on procedure bug7291_1 to user1@localhost; +grant execute on procedure bug7291_2 to user1@localhost; + +connect (user1,localhost,user1,,); +connection user1; + +call bug7291_2(); +call bug7291_1(); + +connection con1root; +drop procedure bug7291_1; +drop procedure bug7291_2; +drop procedure bug7291_0; +disconnect user1; +REVOKE ALL PRIVILEGES, GRANT OPTION FROM user1@localhost; +drop user user1@localhost; diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index 13a5f8cef1f..20059eff8d0 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -1673,3 +1673,24 @@ create view v1(k, K) as select 1,2; create view v1 as SELECT TIME_FORMAT(SEC_TO_TIME(3600),'%H:%i') as t; select * from v1; drop view v1; + +# +# evaluation constant functions in WHERE (BUG#4663) +# +create table t1 (a timestamp default now()); +create table t2 (b timestamp default now()); +create view v1 as select a,b,t1.a < now() from t1,t2 where t1.a < now(); +SHOW CREATE VIEW v1; +drop view v1; +drop table t1, t2; +CREATE TABLE t1 ( a varchar(50) ); +CREATE VIEW v1 AS SELECT * FROM t1 WHERE a = CURRENT_USER(); +SHOW CREATE VIEW v1; +DROP VIEW v1; +CREATE VIEW v1 AS SELECT * FROM t1 WHERE a = VERSION(); +SHOW CREATE VIEW v1; +DROP VIEW v1; +CREATE VIEW v1 AS SELECT * FROM t1 WHERE a = DATABASE(); +SHOW CREATE VIEW v1; +DROP VIEW v1; +DROP TABLE t1; diff --git a/sql/item.cc b/sql/item.cc index d3888cef9d5..7fbeb6f15db 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -635,6 +635,33 @@ Item *Item_string::safe_charset_converter(CHARSET_INFO *tocs) } +Item *Item_static_string_func::safe_charset_converter(CHARSET_INFO *tocs) +{ + Item_string *conv; + uint conv_errors; + String tmp, cstr, *ostr= val_str(&tmp); + cstr.copy(ostr->ptr(), ostr->length(), ostr->charset(), tocs, &conv_errors); + if (conv_errors || + !(conv= new Item_static_string_func(func_name, + cstr.ptr(), cstr.length(), + cstr.charset(), + collation.derivation))) + { + /* + Safe conversion is not possible (or EOM). + We could not convert a string into the requested character set + without data loss. The target charset does not cover all the + characters from the string. Operation cannot be done correctly. + */ + return NULL; + } + conv->str_value.copy(); + /* Ensure that no one is going to change the result string */ + conv->str_value.mark_as_const(); + return conv; +} + + bool Item_string::eq(const Item *item, bool binary_cmp) const { if (type() == item->type() && item->basic_const_item()) diff --git a/sql/item.h b/sql/item.h index c8180b4932a..dce331c6bc2 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1221,6 +1221,7 @@ public: Derivation dv= DERIVATION_COERCIBLE) :Item_string(NullS, str, length, cs, dv), func_name(name_par) {} + Item *safe_charset_converter(CHARSET_INFO *tocs); void print(String *str) { str->append(func_name); } }; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 5a2e14eef2e..d2e83264006 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -237,30 +237,33 @@ void Item_bool_func2::fix_length_and_dec() set_cmp_func(); return; } - - if (args[0]->type() == FIELD_ITEM) + + if (!thd->is_context_analysis_only()) { - Field *field=((Item_field*) args[0])->field; - if (field->can_be_compared_as_longlong()) + if (args[0]->type() == FIELD_ITEM) { - if (convert_constant_item(thd, field,&args[1])) + Field *field=((Item_field*) args[0])->field; + if (field->can_be_compared_as_longlong()) { - cmp.set_cmp_func(this, tmp_arg, tmp_arg+1, - INT_RESULT); // Works for all types. - return; + if (convert_constant_item(thd, field,&args[1])) + { + cmp.set_cmp_func(this, tmp_arg, tmp_arg+1, + INT_RESULT); // Works for all types. + return; + } } } - } - if (args[1]->type() == FIELD_ITEM /* && !args[1]->const_item() */) - { - Field *field=((Item_field*) args[1])->field; - if (field->can_be_compared_as_longlong()) + if (args[1]->type() == FIELD_ITEM /* && !args[1]->const_item() */) { - if (convert_constant_item(thd, field,&args[0])) + Field *field=((Item_field*) args[1])->field; + if (field->can_be_compared_as_longlong()) { - cmp.set_cmp_func(this, tmp_arg, tmp_arg+1, - INT_RESULT); // Works for all types. - return; + if (convert_constant_item(thd, field,&args[0])) + { + cmp.set_cmp_func(this, tmp_arg, tmp_arg+1, + INT_RESULT); // Works for all types. + return; + } } } } @@ -991,7 +994,8 @@ void Item_func_between::fix_length_and_dec() if (args[0]->type() == FIELD_ITEM) { Field *field=((Item_field*) args[0])->field; - if (field->can_be_compared_as_longlong()) + if (!thd->is_context_analysis_only() && + field->can_be_compared_as_longlong()) { /* The following can't be recoded with || as convert_constant_item diff --git a/sql/item_create.cc b/sql/item_create.cc index b9073a6c0b3..b7d8d50f9b3 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -299,16 +299,8 @@ Item *create_func_pow(Item* a, Item *b) Item *create_func_current_user() { - THD *thd=current_thd; - char buff[HOSTNAME_LENGTH+USERNAME_LENGTH+2]; - uint length; - - thd->lex->safe_to_cache_query= 0; - length= (uint) (strxmov(buff, thd->priv_user, "@", thd->priv_host, NullS) - - buff); - return new Item_static_string_func("current_user()", - thd->memdup(buff, length), length, - system_charset_info); + current_thd->lex->safe_to_cache_query= 0; + return new Item_func_user(TRUE); } Item *create_func_radians(Item *a) diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 9f7a44f6f47..a68d9fdb895 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -1522,9 +1522,11 @@ Item *Item_func_sysconst::safe_charset_converter(CHARSET_INFO *tocs) uint conv_errors; String tmp, cstr, *ostr= val_str(&tmp); cstr.copy(ostr->ptr(), ostr->length(), ostr->charset(), tocs, &conv_errors); - if (conv_errors || !(conv= new Item_string(cstr.ptr(), cstr.length(), - cstr.charset(), - collation.derivation))) + if (conv_errors || + !(conv= new Item_static_string_func(fully_qualified_func_name(), + cstr.ptr(), cstr.length(), + cstr.charset(), + collation.derivation))) { return NULL; } @@ -1554,13 +1556,24 @@ String *Item_func_user::val_str(String *str) DBUG_ASSERT(fixed == 1); THD *thd=current_thd; CHARSET_INFO *cs= system_charset_info; - const char *host= thd->host_or_ip; + const char *host, *user; uint res_length; + if (is_current) + { + user= thd->priv_user; + host= thd->priv_host; + } + else + { + user= thd->user; + host= thd->host_or_ip; + } + // For system threads (e.g. replication SQL thread) user may be empty - if (!thd->user) + if (!user) return &my_empty_string; - res_length= (strlen(thd->user)+strlen(host)+2) * cs->mbmaxlen; + res_length= (strlen(user)+strlen(host)+2) * cs->mbmaxlen; if (str->alloc(res_length)) { @@ -1568,12 +1581,13 @@ String *Item_func_user::val_str(String *str) return 0; } res_length=cs->cset->snprintf(cs, (char*)str->ptr(), res_length, "%s@%s", - thd->user, host); + user, host); str->length(res_length); str->set_charset(cs); return str; } + void Item_func_soundex::fix_length_and_dec() { collation.set(args[0]->collation); diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index 8d2eb269915..13073b25130 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -356,8 +356,15 @@ public: Item_func_sysconst() { collation.set(system_charset_info,DERIVATION_SYSCONST); } Item *safe_charset_converter(CHARSET_INFO *tocs); + /* + Used to create correct Item name in new converted item in + safe_charset_converter, return string representation of this function + call + */ + virtual const char *fully_qualified_func_name() const = 0; }; + class Item_func_database :public Item_func_sysconst { public: @@ -369,18 +376,27 @@ public: maybe_null=1; } const char *func_name() const { return "database"; } + const char *fully_qualified_func_name() const { return "database()"; } }; + class Item_func_user :public Item_func_sysconst { + bool is_current; + public: - Item_func_user() :Item_func_sysconst() {} + Item_func_user(bool is_current_arg) + :Item_func_sysconst(), is_current(is_current_arg) {} String *val_str(String *); - void fix_length_and_dec() - { - max_length= (USERNAME_LENGTH+HOSTNAME_LENGTH+1)*system_charset_info->mbmaxlen; + void fix_length_and_dec() + { + max_length= ((USERNAME_LENGTH + HOSTNAME_LENGTH + 1) * + system_charset_info->mbmaxlen); } - const char *func_name() const { return "user"; } + const char *func_name() const + { return is_current ? "current_user" : "user"; } + const char *fully_qualified_func_name() const + { return is_current ? "current_user()" : "user()"; } }; diff --git a/sql/sql_class.h b/sql/sql_class.h index a635a126f84..b00f6bcebd9 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1417,6 +1417,8 @@ public: (variables.sql_mode & MODE_STRICT_ALL_TABLES))); } void set_status_var_init(); + bool is_context_analysis_only() + { return current_arena->is_stmt_prepare() || lex->view_prepare_mode; } }; #define tmp_disable_binlog(A) \ diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 360bc421965..4cf4bd3c36b 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -4794,7 +4794,7 @@ simple_expr: | UNIX_TIMESTAMP '(' expr ')' { $$= new Item_func_unix_timestamp($3); } | USER '(' ')' - { $$= new Item_func_user(); Lex->safe_to_cache_query=0; } + { $$= new Item_func_user(FALSE); Lex->safe_to_cache_query=0; } | UTC_DATE_SYM optional_braces { $$= new Item_func_curdate_utc(); Lex->safe_to_cache_query=0;} | UTC_TIME_SYM optional_braces From b4a723e4d263e9974c2a2175b886f3c45ca42546 Mon Sep 17 00:00:00 2001 From: "bell@sanja.is.com.ua" <> Date: Sat, 16 Jul 2005 00:52:00 +0300 Subject: [PATCH 12/48] post merge changes: added safe_charset_converter to other static functions --- sql/item.cc | 32 ++++++++++++++++++++++++++++++++ sql/item.h | 2 ++ 2 files changed, 34 insertions(+) diff --git a/sql/item.cc b/sql/item.cc index 49ab2c13a88..d26e4ba7c20 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -638,6 +638,38 @@ Item *Item_num::safe_charset_converter(CHARSET_INFO *tocs) } +Item *Item_static_int_func::safe_charset_converter(CHARSET_INFO *tocs) +{ + Item_string *conv; + char buf[64]; + String *s, tmp(buf, sizeof(buf), &my_charset_bin); + s= val_str(&tmp); + if ((conv= new Item_static_string_func(func_name, s->ptr(), s->length(), + s->charset()))) + { + conv->str_value.copy(); + conv->str_value.mark_as_const(); + } + return conv; +} + + +Item *Item_static_float_func::safe_charset_converter(CHARSET_INFO *tocs) +{ + Item_string *conv; + char buf[64]; + String *s, tmp(buf, sizeof(buf), &my_charset_bin); + s= val_str(&tmp); + if ((conv= new Item_static_string_func(func_name, s->ptr(), s->length(), + s->charset()))) + { + conv->str_value.copy(); + conv->str_value.mark_as_const(); + } + return conv; +} + + Item *Item_string::safe_charset_converter(CHARSET_INFO *tocs) { Item_string *conv; diff --git a/sql/item.h b/sql/item.h index cf6c0052a98..f195557fb69 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1132,6 +1132,7 @@ public: Item_static_int_func(const char *str_arg, longlong i, uint length) :Item_int(NullS, i, length), func_name(str_arg) {} + Item *safe_charset_converter(CHARSET_INFO *tocs); void print(String *str) { str->append(func_name); } }; @@ -1242,6 +1243,7 @@ public: :Item_float(NullS, val_arg, decimal_par, length), func_name(str) {} void print(String *str) { str->append(func_name); } + Item *safe_charset_converter(CHARSET_INFO *tocs); }; From 9293bdc849bff6dcfcdb91f707de5c65382a85c4 Mon Sep 17 00:00:00 2001 From: "georg@lmy002.wdf.sap.corp" <> Date: Sat, 16 Jul 2005 00:23:33 +0200 Subject: [PATCH 13/48] fix for bug#11893 increased version number for shared lib to 15 --- configure.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.in b/configure.in index 21494999ca4..59a87c8705a 100644 --- a/configure.in +++ b/configure.in @@ -12,7 +12,7 @@ AM_CONFIG_HEADER(config.h) PROTOCOL_VERSION=10 DOT_FRM_VERSION=6 # See the libtool docs for information on how to do shared lib versions. -SHARED_LIB_VERSION=14:0:0 +SHARED_LIB_VERSION=15:0:0 # ndb version NDB_VERSION_MAJOR=5 From 0298bc347aad0ad0ed0083781d84d412a921459d Mon Sep 17 00:00:00 2001 From: "evgen@moonbone.local" <> Date: Sat, 16 Jul 2005 03:29:12 +0400 Subject: [PATCH 14/48] Fix bug#11482 4.1.12 produces different resultset for a complex query than in previous 4.1.x Wrongly applied optimization were adding NOT NULL constraint which results in rejecting valid rows and reduced result set. The problem was that add_notnull_conds() while checking subquery were adding NOT NULL constraint to left joined table, to which, normally, optimization don't have to be applied. --- mysql-test/r/select.result | 11 +++++++++++ mysql-test/t/select.test | 11 +++++++++++ sql/sql_select.cc | 20 +++++++++++++++++++- 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result index 8160c5a2f3d..5c0616f9e54 100644 --- a/mysql-test/r/select.result +++ b/mysql-test/r/select.result @@ -2559,3 +2559,14 @@ WHERE COUNT(*) 4 drop table t1,t2,t3; +create table t1 (f1 int); +insert into t1 values (1),(NULL); +create table t2 (f2 int, f3 int, f4 int); +create index idx1 on t2 (f4); +insert into t2 values (1,2,3),(2,4,6); +select A.f2 from t1 left join t2 A on A.f2 = f1 where A.f3=(select min(f3) +from t2 C where A.f4 = C.f4) or A.f3 IS NULL; +f2 +1 +NULL +drop table t1,t2; diff --git a/mysql-test/t/select.test b/mysql-test/t/select.test index 37e4324152b..2e261d0611f 100644 --- a/mysql-test/t/select.test +++ b/mysql-test/t/select.test @@ -2116,3 +2116,14 @@ WHERE drop table t1,t2,t3; +# +# Bug #11482 4.1.12 produces different resultset for a complex query +# than in previous 4.1.x +create table t1 (f1 int); +insert into t1 values (1),(NULL); +create table t2 (f2 int, f3 int, f4 int); +create index idx1 on t2 (f4); +insert into t2 values (1,2,3),(2,4,6); +select A.f2 from t1 left join t2 A on A.f2 = f1 where A.f3=(select min(f3) +from t2 C where A.f4 = C.f4) or A.f3 IS NULL; +drop table t1,t2; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 75fc189b21f..43cf649685e 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -3508,7 +3508,23 @@ inline void add_cond_and_fix(Item **e1, Item *e2) (where othertbl is a non-const table and othertbl.field may be NULL) and add them to conditions on correspoding tables (othertbl in this example). - + + Exception from that is the case when referred_tab->join != join. + I.e. don't add NOT NULL constraints from any embedded subquery. + Consider this query: + SELECT A.f2 FROM t1 LEFT JOIN t2 A ON A.f2 = f1 + WHERE A.f3=(SELECT MIN(f3) FROM t2 C WHERE A.f4 = C.f4) OR A.f3 IS NULL; + Here condition A.f3 IS NOT NULL is going to be added to the WHERE + condition of the embedding query. + Another example: + SELECT * FROM t10, t11 WHERE (t10.a < 10 OR t10.a IS NULL) + AND t11.b <=> t10.b AND (t11.a = (SELECT MAX(a) FROM t12 + WHERE t12.b = t10.a )); + Here condition t10.a IS NOT NULL is going to be added. + In both cases addition of NOT NULL condition will erroneously reject + some rows of the result set. + referred_tab->join != join constraint would disallow such additions. + This optimization doesn't affect the choices that ref, range, or join optimizer make. This was intentional because this was added after 4.1 was GA. @@ -3539,6 +3555,8 @@ static void add_not_null_conds(JOIN *join) DBUG_ASSERT(item->type() == Item::FIELD_ITEM); Item_field *not_null_item= (Item_field*)item; JOIN_TAB *referred_tab= not_null_item->field->table->reginfo.join_tab; + if (referred_tab->join != join) + continue; Item *notnull; if (!(notnull= new Item_func_isnotnull(not_null_item))) DBUG_VOID_RETURN; From e08caeeee2b6250d4e4adfed34c99de08eaacecc Mon Sep 17 00:00:00 2001 From: "konstantin@mysql.com" <> Date: Sat, 16 Jul 2005 03:29:13 +0400 Subject: [PATCH 15/48] A fix and a test case for Bug#9359 "Prepared statements take snapshot of system vars at PREPARE time": implement a special Item to handle system variables. This item substitutes itself with a basic constant containing variable value at fix_fields. --- mysql-test/r/ps.result | 25 ++++++++++++++++ mysql-test/t/ps.test | 18 ++++++++++- sql/item_func.cc | 68 +++++++++++++++++++++++------------------- sql/item_func.h | 23 ++++++++++++++ sql/mysql_priv.h | 10 ++++--- sql/set_var.cc | 14 ++------- sql/set_var.h | 5 ---- 7 files changed, 112 insertions(+), 51 deletions(-) diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index 6cd32cbe8d0..c17015df757 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -676,3 +676,28 @@ ERROR 42000: You have an error in your SQL syntax; check the manual that corresp select ? from t1; ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '? from t1' at line 1 drop table t1; +prepare stmt from "select @@time_zone"; +execute stmt; +@@time_zone +SYSTEM +set @@time_zone:='Japan'; +execute stmt; +@@time_zone +Japan +prepare stmt from "select @@tx_isolation"; +execute stmt; +@@tx_isolation +REPEATABLE-READ +set transaction isolation level read committed; +execute stmt; +@@tx_isolation +READ-COMMITTED +set transaction isolation level serializable; +execute stmt; +@@tx_isolation +SERIALIZABLE +set @@tx_isolation=default; +execute stmt; +@@tx_isolation +REPEATABLE-READ +deallocate prepare stmt; diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test index beae95d462a..a4911115c9f 100644 --- a/mysql-test/t/ps.test +++ b/mysql-test/t/ps.test @@ -702,4 +702,20 @@ select ??; select ? from t1; --enable_ps_protocol drop table t1; -# +# +# Bug#9359 "Prepared statements take snapshot of system vars at PREPARE +# time" +# +prepare stmt from "select @@time_zone"; +execute stmt; +set @@time_zone:='Japan'; +execute stmt; +prepare stmt from "select @@tx_isolation"; +execute stmt; +set transaction isolation level read committed; +execute stmt; +set transaction isolation level serializable; +execute stmt; +set @@tx_isolation=default; +execute stmt; +deallocate prepare stmt; diff --git a/sql/item_func.cc b/sql/item_func.cc index 0128209cb22..c8f1d209d26 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -3031,6 +3031,36 @@ bool Item_func_get_user_var::eq(const Item *item, bool binary_cmp) const } +Item_func_get_system_var:: +Item_func_get_system_var(sys_var *var_arg, enum_var_type var_type_arg, + LEX_STRING *component_arg, const char *name_arg, + size_t name_len_arg) + :var(var_arg), var_type(var_type_arg), component(*component_arg) +{ + /* set_name() will allocate the name */ + set_name(name_arg, name_len_arg, system_charset_info); +} + + +bool +Item_func_get_system_var::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) +{ + Item *item= var->item(thd, var_type, &component); + DBUG_ENTER("Item_func_get_system_var::fix_fields"); + /* + Evaluate the system variable and substitute the result (a basic constant) + instead of this item. If the variable can not be evaluated, + the error is reported in sys_var::item(). + */ + if (item == 0) + DBUG_RETURN(1); // Impossible + item->set_name(name, 0, system_charset_info); // don't allocate a new name + thd->change_item_tree(ref, item); + + DBUG_RETURN(0); +} + + longlong Item_func_inet_aton::val_int() { DBUG_ASSERT(fixed == 1); @@ -3375,22 +3405,21 @@ longlong Item_func_bit_xor::val_int() 0 error # constant item */ - + Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name, LEX_STRING component) { + sys_var *var; + char buff[MAX_SYS_VAR_LENGTH*2+4+8], *pos; + LEX_STRING *base_name, *component_name; + if (component.str == 0 && !my_strcasecmp(system_charset_info, name.str, "VERSION")) return new Item_string("@@VERSION", server_version, (uint) strlen(server_version), system_charset_info, DERIVATION_SYSCONST); - Item *item; - sys_var *var; - char buff[MAX_SYS_VAR_LENGTH*2+4+8], *pos; - LEX_STRING *base_name, *component_name; - if (component.str) { base_name= &component; @@ -3412,9 +3441,8 @@ Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name, return 0; } } - if (!(item=var->item(thd, var_type, component_name))) - return 0; // Impossible thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); + buff[0]='@'; buff[1]='@'; pos=buff+2; @@ -3435,28 +3463,8 @@ Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name, memcpy(pos, base_name->str, base_name->length); pos+= base_name->length; - // set_name() will allocate the name - item->set_name(buff,(uint) (pos-buff), system_charset_info); - return item; -} - - -Item *get_system_var(THD *thd, enum_var_type var_type, const char *var_name, - uint length, const char *item_name) -{ - Item *item; - sys_var *var; - LEX_STRING null_lex_string; - - null_lex_string.str= 0; - - var= find_sys_var(var_name, length); - DBUG_ASSERT(var != 0); - if (!(item=var->item(thd, var_type, &null_lex_string))) - return 0; // Impossible - thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); - item->set_name(item_name, 0, system_charset_info); // Will use original name - return item; + return new Item_func_get_system_var(var, var_type, component_name, + buff, pos - buff); } diff --git a/sql/item_func.h b/sql/item_func.h index 6b6e5d4b8ec..5e36f9863bb 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -988,6 +988,29 @@ public: }; +/* A system variable */ + +class Item_func_get_system_var :public Item_func +{ + sys_var *var; + enum_var_type var_type; + LEX_STRING component; +public: + Item_func_get_system_var(sys_var *var_arg, enum_var_type var_type_arg, + LEX_STRING *component_arg, const char *name_arg, + size_t name_len_arg); + bool fix_fields(THD *thd, TABLE_LIST *tables, Item **ref); + /* + Stubs for pure virtual methods. Should never be called: this + item is always substituted with a constant in fix_fields(). + */ + double val() { DBUG_ASSERT(0); return 0.0; } + longlong val_int() { DBUG_ASSERT(0); return 0; } + String* val_str(String*) { DBUG_ASSERT(0); return 0; } + void fix_length_and_dec() { DBUG_ASSERT(0); } +}; + + class Item_func_inet_aton : public Item_int_func { public: diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 722b76796b2..351f12acad2 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -358,6 +358,11 @@ inline THD *_current_thd(void) #include "protocol.h" #include "sql_udf.h" class user_var_entry; +enum enum_var_type +{ + OPT_DEFAULT, OPT_SESSION, OPT_GLOBAL +}; +class sys_var; #include "item.h" typedef Comp_creator* (*chooser_compare_func_creator)(bool invert); /* sql_parse.cc */ @@ -1119,12 +1124,9 @@ extern bool sql_cache_init(); extern void sql_cache_free(); extern int sql_cache_hit(THD *thd, char *inBuf, uint length); -/* item.cc */ +/* item_func.cc */ Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name, LEX_STRING component); -Item *get_system_var(THD *thd, enum_var_type var_type, const char *var_name, - uint length, const char *item_name); -/* item_func.cc */ int get_var_with_binlog(THD *thd, LEX_STRING &name, user_var_entry **out_entry); /* log.cc */ diff --git a/sql/set_var.cc b/sql/set_var.cc index b0fa61a12bc..f7700d18607 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -1551,15 +1551,7 @@ err: /* Return an Item for a variable. Used with @@[global.]variable_name - If type is not given, return local value if exists, else global - - We have to use netprintf() instead of my_error() here as this is - called on the parsing stage. - - TODO: - With prepared statements/stored procedures this has to be fixed - to create an item that gets the current value at fix_fields() stage. */ Item *sys_var::item(THD *thd, enum_var_type var_type, LEX_STRING *base) @@ -1568,8 +1560,8 @@ Item *sys_var::item(THD *thd, enum_var_type var_type, LEX_STRING *base) { if (var_type != OPT_DEFAULT) { - net_printf(thd, ER_INCORRECT_GLOBAL_LOCAL_VAR, - name, var_type == OPT_GLOBAL ? "SESSION" : "GLOBAL"); + my_error(ER_INCORRECT_GLOBAL_LOCAL_VAR, MYF(0), + name, var_type == OPT_GLOBAL ? "SESSION" : "GLOBAL"); return 0; } /* As there was no local variable, return the global value */ @@ -1613,7 +1605,7 @@ Item *sys_var::item(THD *thd, enum_var_type var_type, LEX_STRING *base) return tmp; } default: - net_printf(thd, ER_VAR_CANT_BE_READ, name); + my_error(ER_VAR_CANT_BE_READ, MYF(0), name); } return 0; } diff --git a/sql/set_var.h b/sql/set_var.h index d452ba03367..2ac4fc2c260 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -30,11 +30,6 @@ class set_var; typedef struct system_variables SV; extern TYPELIB bool_typelib, delay_key_write_typelib, sql_mode_typelib; -enum enum_var_type -{ - OPT_DEFAULT, OPT_SESSION, OPT_GLOBAL -}; - typedef int (*sys_check_func)(THD *, set_var *); typedef bool (*sys_update_func)(THD *, set_var *); typedef void (*sys_after_update_func)(THD *,enum_var_type); From db1fb6a7e52e2e42e18f32933265024ec1921112 Mon Sep 17 00:00:00 2001 From: "patg@krsna.patg.net" <> Date: Fri, 15 Jul 2005 17:33:47 -0700 Subject: [PATCH 16/48] This is the same patch as 1.1937, with small variable declaration changes, memory deallocation cleanup --- mysql-test/include/federated.inc | 21 + mysql-test/include/federated_cleanup.inc | 11 + mysql-test/r/federated.result | 801 ++++++++++- mysql-test/t/federated.test | 425 +++++- sql/ha_federated.cc | 1546 ++++++++++++++++------ sql/ha_federated.h | 80 +- sql/share/errmsg.txt | 12 + 7 files changed, 2377 insertions(+), 519 deletions(-) create mode 100644 mysql-test/include/federated.inc create mode 100644 mysql-test/include/federated_cleanup.inc diff --git a/mysql-test/include/federated.inc b/mysql-test/include/federated.inc new file mode 100644 index 00000000000..1c53b9ed2c5 --- /dev/null +++ b/mysql-test/include/federated.inc @@ -0,0 +1,21 @@ +--source ./include/have_federated_db.inc + +source ./include/master-slave.inc; + +# remote table creation + +connection slave; +--replicate-ignore-db=federated +stop slave; + +--disable_warnings +# at this point, we are connected to master +DROP DATABASE IF EXISTS federated; +--enable_warnings +CREATE DATABASE federated; + +connection master; +--disable_warnings +DROP DATABASE IF EXISTS federated; +--enable_warnings +CREATE DATABASE federated; diff --git a/mysql-test/include/federated_cleanup.inc b/mysql-test/include/federated_cleanup.inc new file mode 100644 index 00000000000..17a6e1e5100 --- /dev/null +++ b/mysql-test/include/federated_cleanup.inc @@ -0,0 +1,11 @@ +connection master; +--disable_warnings +DROP TABLE IF EXISTS federated.t1; +DROP DATABASE IF EXISTS federated; +--enable_warnings + +connection slave; +--disable_warnings +DROP TABLE IF EXISTS federated.t1; +DROP DATABASE IF EXISTS federated; +--enable_warnings diff --git a/mysql-test/r/federated.result b/mysql-test/r/federated.result index d9c86a89c75..1cf4b4707cb 100644 --- a/mysql-test/r/federated.result +++ b/mysql-test/r/federated.result @@ -7,41 +7,47 @@ start slave; stop slave; DROP DATABASE IF EXISTS federated; CREATE DATABASE federated; +DROP DATABASE IF EXISTS federated; +CREATE DATABASE federated; +DROP TABLE IF EXISTS federated.t1; +Warnings: +Note 1051 Unknown table 't1' CREATE TABLE federated.t1 ( `id` int(20) NOT NULL, `name` varchar(32) NOT NULL default '' ) DEFAULT CHARSET=latin1; -DROP DATABASE IF EXISTS federated; -CREATE DATABASE federated; +DROP TABLE IF EXISTS federated.t1; +Warnings: +Note 1051 Unknown table 't1' CREATE TABLE federated.t1 ( `id` int(20) NOT NULL, `name` varchar(32) NOT NULL default '' ) ENGINE="FEDERATED" DEFAULT CHARSET=latin1 COMMENT='mysql://root@127.0.0.1:@/too/many/items/federated/t1'; -ERROR HY000: Can't create table 'connection string is not in the correct format' (errno: 0) +ERROR HY000: Can't create federated table. The data source connection string 'mysql://root@127.0.0.1:@/too/many/items/federated/t1' is not in the correct format CREATE TABLE federated.t1 ( `id` int(20) NOT NULL, `name` varchar(32) NOT NULL default '' ) ENGINE="FEDERATED" DEFAULT CHARSET=latin1 COMMENT='mysql://root@127.0.0.1'; -ERROR HY000: Can't create table 'connection string is not in the correct format' (errno: 0) +ERROR HY000: Can't create federated table. The data source connection string 'mysql://root@127.0.0.1' is not in the correct format CREATE TABLE federated.t1 ( `id` int(20) NOT NULL, `name` varchar(32) NOT NULL default '' ) ENGINE="FEDERATED" DEFAULT CHARSET=latin1 COMMENT='mysql://root@127.0.0.1:SLAVE_PORT/federated/t3'; -ERROR HY000: Error running query on master: foreign table 't3' does not exist! +ERROR HY000: Can't create federated table. Foreign data src error : ': 1146 : Table 'federated.t3' doesn't exist' CREATE TABLE federated.t1 ( `id` int(20) NOT NULL, `name` varchar(32) NOT NULL default '' ) ENGINE="FEDERATED" DEFAULT CHARSET=latin1 COMMENT='mysql://user:pass@127.0.0.1:SLAVE_PORT/federated/t1'; -ERROR 08S01: Error connecting to master: unable to connect to database 'federated' on host '127.0.0.1 as user 'user' ! +ERROR HY000: Unable to connect to foreign data source - database ' database federated username user hostname 127.0.0.1'! DROP TABLE IF EXISTS federated.t1; Warnings: Note 1051 Unknown table 't1' @@ -124,18 +130,14 @@ CREATE TABLE federated.t1 ( `name` varchar(32) NOT NULL default '', `other` int(20) NOT NULL default '0', `created` datetime default '2004-04-04 04:04:04', -PRIMARY KEY (`id`), -KEY `name` (`name`), -KEY `other_key` (`other`)) +PRIMARY KEY (`id`)) DEFAULT CHARSET=latin1; CREATE TABLE federated.t1 ( `id` int(20) NOT NULL auto_increment, `name` varchar(32) NOT NULL default '', `other` int(20) NOT NULL default '0', `created` datetime default '2004-04-04 04:04:04', -PRIMARY KEY (`id`), -KEY `name` (`name`), -KEY `other_key` (`other`)) +PRIMARY KEY (`id`)) ENGINE="FEDERATED" DEFAULT CHARSET=latin1 COMMENT='mysql://root@127.0.0.1:SLAVE_PORT/federated/t1'; INSERT INTO federated.t1 (name, other) VALUES ('First Name', 11111); @@ -148,6 +150,9 @@ INSERT INTO federated.t1 (name, other) VALUES ('Seventh Name', 77777); INSERT INTO federated.t1 (name, other) VALUES ('Eigth Name', 88888); INSERT INTO federated.t1 (name, other) VALUES ('Ninth Name', 99999); INSERT INTO federated.t1 (name, other) VALUES ('Tenth Name', 101010); +EXPLAIN SELECT * FROM federated.t1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 10 SELECT * FROM federated.t1; id name other created 1 First Name 11111 2004-04-04 04:04:04 @@ -160,18 +165,32 @@ id name other created 8 Eigth Name 88888 2004-04-04 04:04:04 9 Ninth Name 99999 2004-04-04 04:04:04 10 Tenth Name 101010 2004-04-04 04:04:04 +EXPLAIN SELECT * FROM federated.t1 WHERE id = 5; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 const PRIMARY PRIMARY 4 const 1 SELECT * FROM federated.t1 WHERE id = 5; id name other created 5 Fifth Name 55555 2004-04-04 04:04:04 +EXPLAIN SELECT * FROM federated.t1 WHERE name = 'Sixth Name'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 10 Using where SELECT * FROM federated.t1 WHERE name = 'Sixth Name'; id name other created 6 Sixth Name 66666 2004-04-04 04:04:04 +EXPLAIN SELECT * FROM federated.t1 WHERE id = 6 and name = 'Sixth Name'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 const PRIMARY PRIMARY 4 const 1 SELECT * FROM federated.t1 WHERE id = 6 and name = 'Sixth Name'; id name other created 6 Sixth Name 66666 2004-04-04 04:04:04 -SELECT * FROM federated.t1 WHERE other = 44444; +EXPLAIN SELECT * FROM federated.t1 WHERE name = 'Sixth Name' AND other = 44444; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 10 Using where +SELECT * FROM federated.t1 WHERE name = 'Sixth Name' AND other = 44444; id name other created -4 Fourth Name 44444 2004-04-04 04:04:04 +EXPLAIN SELECT * FROM federated.t1 WHERE name like '%th%'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 10 Using where SELECT * FROM federated.t1 WHERE name like '%th%'; id name other created 3 Third Name 33333 2004-04-04 04:04:04 @@ -190,6 +209,9 @@ UPDATE federated.t1 SET name = 'Third name' WHERE name = '3rd name'; SELECT * FROM federated.t1 WHERE name = 'Third name'; id name other created 3 Third name 33333 2004-04-04 04:04:04 +EXPLAIN SELECT * FROM federated.t1 ORDER BY id DESC; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 10 Using filesort SELECT * FROM federated.t1 ORDER BY id DESC; id name other created 10 Tenth Name 101010 2004-04-04 04:04:04 @@ -202,6 +224,9 @@ id name other created 3 Third name 33333 2004-04-04 04:04:04 2 Second Name 22222 2004-04-04 04:04:04 1 First Name 11111 2004-04-04 04:04:04 +EXPLAIN SELECT * FROM federated.t1 ORDER BY name; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 10 Using filesort SELECT * FROM federated.t1 ORDER BY name; id name other created 8 Eigth Name 88888 2004-04-04 04:04:04 @@ -214,6 +239,9 @@ id name other created 6 Sixth Name 66666 2004-04-04 04:04:04 10 Tenth Name 101010 2004-04-04 04:04:04 3 Third name 33333 2004-04-04 04:04:04 +EXPLAIN SELECT * FROM federated.t1 ORDER BY name DESC; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 10 Using filesort SELECT * FROM federated.t1 ORDER BY name DESC; id name other created 3 Third name 33333 2004-04-04 04:04:04 @@ -226,6 +254,9 @@ id name other created 1 First Name 11111 2004-04-04 04:04:04 5 Fifth Name 55555 2004-04-04 04:04:04 8 Eigth Name 88888 2004-04-04 04:04:04 +EXPLAIN SELECT * FROM federated.t1 ORDER BY name ASC; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 10 Using filesort SELECT * FROM federated.t1 ORDER BY name ASC; id name other created 8 Eigth Name 88888 2004-04-04 04:04:04 @@ -238,6 +269,9 @@ id name other created 6 Sixth Name 66666 2004-04-04 04:04:04 10 Tenth Name 101010 2004-04-04 04:04:04 3 Third name 33333 2004-04-04 04:04:04 +EXPLAIN SELECT * FROM federated.t1 GROUP BY other; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 10 Using temporary; Using filesort SELECT * FROM federated.t1 GROUP BY other; id name other created 1 First Name 11111 2004-04-04 04:04:04 @@ -257,6 +291,175 @@ DELETE FROM federated.t1; SELECT * FROM federated.t1 WHERE id = 5; id name other created DROP TABLE IF EXISTS federated.t1; +CREATE TABLE federated.t1 ( +`id` int(20) NOT NULL auto_increment, +`name` varchar(32) NOT NULL default '', +`other` int(20) NOT NULL default '0', +`created` datetime NOT NULL, +PRIMARY KEY (`id`), +key name(`name`), +key other(`other`), +key created(`created`)) +DEFAULT CHARSET=latin1; +DROP TABLE IF EXISTS federated.t1; +CREATE TABLE federated.t1 ( +`id` int(20) NOT NULL auto_increment, +`name` varchar(32) NOT NULL default '', +`other` int(20) NOT NULL default '0', +`created` datetime NOT NULL, +PRIMARY KEY (`id`), +key name(`name`), +key other(`other`), +key created(`created`)) +ENGINE="FEDERATED" DEFAULT CHARSET=latin1 +COMMENT='mysql://root@127.0.0.1:SLAVE_PORT/federated/t1'; +INSERT INTO federated.t1 (name, other, created) +VALUES ('First Name', 11111, '2004-01-01 01:01:01'); +INSERT INTO federated.t1 (name, other, created) +VALUES ('Second Name', 22222, '2004-01-23 02:43:00'); +INSERT INTO federated.t1 (name, other, created) +VALUES ('Third Name', 33333, '2004-02-14 02:14:00'); +INSERT INTO federated.t1 (name, other, created) +VALUES ('Fourth Name', 44444, '2003-04-05 00:00:00'); +INSERT INTO federated.t1 (name, other, created) +VALUES ('Fifth Name', 55555, '2001-02-02 02:02:02'); +INSERT INTO federated.t1 (name, other, created) +VALUES ('Sixth Name', 66666, '2005-06-06 15:30:00'); +INSERT INTO federated.t1 (name, other, created) +VALUES ('Seventh Name', 77777, '2003-12-12 18:32:00'); +INSERT INTO federated.t1 (name, other, created) +VALUES ('Eigth Name', 88888, '2005-03-12 11:00:00'); +INSERT INTO federated.t1 (name, other, created) +VALUES ('Ninth Name', 99999, '2005-03-12 11:00:01'); +INSERT INTO federated.t1 (name, other, created) +VALUES ('Tenth Name', 101010, '2005-03-12 12:00:01'); +EXPLAIN SELECT * FROM federated.t1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 10 +SELECT * FROM federated.t1; +id name other created +1 First Name 11111 2004-01-01 01:01:01 +2 Second Name 22222 2004-01-23 02:43:00 +3 Third Name 33333 2004-02-14 02:14:00 +4 Fourth Name 44444 2003-04-05 00:00:00 +5 Fifth Name 55555 2001-02-02 02:02:02 +6 Sixth Name 66666 2005-06-06 15:30:00 +7 Seventh Name 77777 2003-12-12 18:32:00 +8 Eigth Name 88888 2005-03-12 11:00:00 +9 Ninth Name 99999 2005-03-12 11:00:01 +10 Tenth Name 101010 2005-03-12 12:00:01 +EXPLAIN SELECT * FROM federated.t1 WHERE id = 5; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 const PRIMARY PRIMARY 4 const 1 +SELECT * FROM federated.t1 WHERE id = 5; +id name other created +5 Fifth Name 55555 2001-02-02 02:02:02 +EXPLAIN SELECT * FROM federated.t1 WHERE name = 'Sixth Name'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ref name name 34 const 2 Using where +SELECT * FROM federated.t1 WHERE name = 'Sixth Name'; +id name other created +6 Sixth Name 66666 2005-06-06 15:30:00 +EXPLAIN SELECT * FROM federated.t1 WHERE id = 6 and name = 'Sixth Name'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 const PRIMARY,name PRIMARY 4 const 1 +SELECT * FROM federated.t1 WHERE id = 6 and name = 'Sixth Name'; +id name other created +6 Sixth Name 66666 2005-06-06 15:30:00 +EXPLAIN SELECT * FROM federated.t1 WHERE other = 44444; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ref other other 4 const 2 +SELECT * FROM federated.t1 WHERE other = 44444; +id name other created +4 Fourth Name 44444 2003-04-05 00:00:00 +EXPLAIN SELECT * FROM federated.t1 WHERE name like '%th%'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 10 Using where +SELECT * FROM federated.t1 WHERE name like '%th%'; +id name other created +3 Third Name 33333 2004-02-14 02:14:00 +4 Fourth Name 44444 2003-04-05 00:00:00 +5 Fifth Name 55555 2001-02-02 02:02:02 +6 Sixth Name 66666 2005-06-06 15:30:00 +7 Seventh Name 77777 2003-12-12 18:32:00 +8 Eigth Name 88888 2005-03-12 11:00:00 +9 Ninth Name 99999 2005-03-12 11:00:01 +10 Tenth Name 101010 2005-03-12 12:00:01 +UPDATE federated.t1 SET name = '3rd name' WHERE id = 3; +SELECT * FROM federated.t1 WHERE name = '3rd name'; +id name other created +3 3rd name 33333 2004-02-14 02:14:00 +UPDATE federated.t1 SET name = 'Third name' WHERE name = '3rd name'; +SELECT * FROM federated.t1 WHERE name = 'Third name'; +id name other created +3 Third name 33333 2004-02-14 02:14:00 +SELECT * FROM federated.t1 ORDER BY id DESC; +id name other created +10 Tenth Name 101010 2005-03-12 12:00:01 +9 Ninth Name 99999 2005-03-12 11:00:01 +8 Eigth Name 88888 2005-03-12 11:00:00 +7 Seventh Name 77777 2003-12-12 18:32:00 +6 Sixth Name 66666 2005-06-06 15:30:00 +5 Fifth Name 55555 2001-02-02 02:02:02 +4 Fourth Name 44444 2003-04-05 00:00:00 +3 Third name 33333 2004-02-14 02:14:00 +2 Second Name 22222 2004-01-23 02:43:00 +1 First Name 11111 2004-01-01 01:01:01 +SELECT * FROM federated.t1 ORDER BY name; +id name other created +8 Eigth Name 88888 2005-03-12 11:00:00 +5 Fifth Name 55555 2001-02-02 02:02:02 +1 First Name 11111 2004-01-01 01:01:01 +4 Fourth Name 44444 2003-04-05 00:00:00 +9 Ninth Name 99999 2005-03-12 11:00:01 +2 Second Name 22222 2004-01-23 02:43:00 +7 Seventh Name 77777 2003-12-12 18:32:00 +6 Sixth Name 66666 2005-06-06 15:30:00 +10 Tenth Name 101010 2005-03-12 12:00:01 +3 Third name 33333 2004-02-14 02:14:00 +SELECT * FROM federated.t1 ORDER BY name DESC; +id name other created +3 Third name 33333 2004-02-14 02:14:00 +10 Tenth Name 101010 2005-03-12 12:00:01 +6 Sixth Name 66666 2005-06-06 15:30:00 +7 Seventh Name 77777 2003-12-12 18:32:00 +2 Second Name 22222 2004-01-23 02:43:00 +9 Ninth Name 99999 2005-03-12 11:00:01 +4 Fourth Name 44444 2003-04-05 00:00:00 +1 First Name 11111 2004-01-01 01:01:01 +5 Fifth Name 55555 2001-02-02 02:02:02 +8 Eigth Name 88888 2005-03-12 11:00:00 +SELECT * FROM federated.t1 ORDER BY name ASC; +id name other created +8 Eigth Name 88888 2005-03-12 11:00:00 +5 Fifth Name 55555 2001-02-02 02:02:02 +1 First Name 11111 2004-01-01 01:01:01 +4 Fourth Name 44444 2003-04-05 00:00:00 +9 Ninth Name 99999 2005-03-12 11:00:01 +2 Second Name 22222 2004-01-23 02:43:00 +7 Seventh Name 77777 2003-12-12 18:32:00 +6 Sixth Name 66666 2005-06-06 15:30:00 +10 Tenth Name 101010 2005-03-12 12:00:01 +3 Third name 33333 2004-02-14 02:14:00 +SELECT * FROM federated.t1 GROUP BY other; +id name other created +1 First Name 11111 2004-01-01 01:01:01 +2 Second Name 22222 2004-01-23 02:43:00 +3 Third name 33333 2004-02-14 02:14:00 +4 Fourth Name 44444 2003-04-05 00:00:00 +5 Fifth Name 55555 2001-02-02 02:02:02 +6 Sixth Name 66666 2005-06-06 15:30:00 +7 Seventh Name 77777 2003-12-12 18:32:00 +8 Eigth Name 88888 2005-03-12 11:00:00 +9 Ninth Name 99999 2005-03-12 11:00:01 +10 Tenth Name 101010 2005-03-12 12:00:01 +DELETE FROM federated.t1 WHERE id = 5; +SELECT * FROM federated.t1 WHERE id = 5; +id name other created +DELETE FROM federated.t1; +SELECT * FROM federated.t1 WHERE id = 5; +id name other created +DROP TABLE IF EXISTS federated.t1; CREATE TABLE federated.t1 ( `id` int(20) NOT NULL auto_increment, `name` varchar(32), @@ -303,15 +506,14 @@ UPDATE federated.t1 SET name = 'Fourth Name', other = 'four four four' WHERE name IS NULL AND other IS NULL; UPDATE federated.t1 SET other = 'two two two two' WHERE name = 'Second Name'; -UPDATE federated.t1 SET other = 'seven seven' WHERE name like 'Sec%'; -UPDATE federated.t1 SET other = 'seven seven' WHERE name = 'Seventh Name'; +UPDATE federated.t1 SET other = 'seven seven' WHERE name like 'Sev%'; UPDATE federated.t1 SET name = 'Tenth Name' WHERE other like 'fee fie%'; SELECT * FROM federated.t1 WHERE name IS NULL OR other IS NULL ; id name other SELECT * FROM federated.t1; id name other 1 First Name 11111 -2 Second Name seven seven +2 Second Name two two two two 3 Third Name 33333 4 Fourth Name four four four 5 Fifth Name 55555 @@ -418,6 +620,533 @@ id name bincol floatval other 3 third g 22.22 2222 DROP TABLE IF EXISTS federated.t1; CREATE TABLE federated.t1 ( +`id` int NOT NULL auto_increment, +`col1` int(10) NOT NULL DEFAULT 0, +`col2` varchar(64) NOT NULL DEFAULT '', +`col3` int(20) NOT NULL, +`col4` int(40) NOT NULL, +primary key (`id`, `col1`, `col2`, `col3`, `col4`), +key col1(col1), +key col2(col2), +key col3(col3), +key col4(col4)); +DROP TABLE IF EXISTS federated.t1; +CREATE TABLE federated.t1 ( +`id` int NOT NULL auto_increment, +`col1` int(10) NOT NULL DEFAULT 0, +`col2` varchar(64) NOT NULL DEFAULT '', +`col3` int(20) NOT NULL, +`col4` int(40) NOT NULL, +primary key (`id`, `col1`, `col2`, `col3`, `col4`), +key col1(col1), +key col2(col2), +key col3(col3), +key col4(col4)) +ENGINE="FEDERATED" + COMMENT='mysql://root@127.0.0.1:SLAVE_PORT/federated/t1'; +INSERT INTO federated.t1 (col1, col2, col3, col4) +VALUES (1, 'one One', 11, 1111); +INSERT INTO federated.t1 (col1, col2, col3, col4) +VALUES (2, 'Two two', 22, 2222); +INSERT INTO federated.t1 (col1, col2, col3, col4) +VALUES (3, 'three Three', 33, 33333); +INSERT INTO federated.t1 (col1, col2, col3, col4) +VALUES (4, 'fourfourfour', 444, 4444444); +INSERT INTO federated.t1 (col1, col2, col3, col4) +VALUES (5, 'five 5 five five 5', 5, 55555); +INSERT INTO federated.t1 (col1, col2, col3, col4) +VALUES (6, 'six six Sixsix', 6666, 6); +INSERT INTO federated.t1 (col1, col2, col3, col4) +VALUES (7, 'seven Sevenseven', 77777, 7777); +INSERT INTO federated.t1 (col1, col2, col3, col4) +VALUES (8, 'eight eight eight', 88888, 88); +INSERT INTO federated.t1 (col1, col2, col3, col4) +VALUES (9, 'nine Nine', 999999, 999999); +INSERT INTO federated.t1 (col1, col2, col3, col4) +VALUES (10, 'Tenth ten TEN', 1010101, 1010); +EXPLAIN SELECT * FROM federated.t1 WHERE col2 = 'two two'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ref col2 col2 66 const 2 Using where +SELECT * FROM federated.t1 WHERE col2 = 'two two'; +id col1 col2 col3 col4 +2 2 Two two 22 2222 +EXPLAIN SELECT * FROM federated.t1 WHERE col2 = 'two Two'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ref col2 col2 66 const 2 Using where +SELECT * FROM federated.t1 WHERE col2 = 'two Two'; +id col1 col2 col3 col4 +2 2 Two two 22 2222 +SELECT * FROM federated.t1 WHERE id = 3; +id col1 col2 col3 col4 +3 3 three Three 33 33333 +EXPLAIN SELECT * FROM federated.t1 WHERE id = 3; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ref PRIMARY PRIMARY 4 const 2 +SELECT * FROM federated.t1 WHERE id = 3 AND col1 = 3; +id col1 col2 col3 col4 +3 3 three Three 33 33333 +EXPLAIN SELECT * FROM federated.t1 WHERE id = 3 AND col1 = 3; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ref PRIMARY,col1 PRIMARY 8 const,const 2 +SELECT * FROM federated.t1 WHERE id = 4 AND col1 = 4 AND col2 = 'Two two'; +id col1 col2 col3 col4 +EXPLAIN SELECT * FROM federated.t1 WHERE id = 4 AND col1 = 4 AND col2 = 'Two two'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ref PRIMARY,col1,col2 PRIMARY 74 const,const,const 2 Using where +SELECT * FROM federated.t1 WHERE id = 4 AND col1 = 4 AND col2 = 'fourfourfour'; +id col1 col2 col3 col4 +4 4 fourfourfour 444 4444444 +EXPLAIN SELECT * FROM federated.t1 WHERE id = 4 AND col1 = 4 AND col2 = 'fourfourfour'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ref PRIMARY,col1,col2 PRIMARY 74 const,const,const 2 Using where +SELECT * FROM federated.t1 WHERE id = 5 AND col2 = 'five 5 five five 5' +AND col3 = 5; +id col1 col2 col3 col4 +5 5 five 5 five five 5 5 55555 +EXPLAIN SELECT * FROM federated.t1 WHERE id = 5 AND col2 = 'five 5 five five 5' +AND col3 = 5; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ref PRIMARY,col2,col3 PRIMARY 4 const 2 Using where +EXPLAIN SELECT * FROM federated.t1 WHERE id = 5 AND col2 = 'five 5 five five 5' +AND col3 = 5 +AND col4 = 55555; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ref PRIMARY,col2,col3,col4 PRIMARY 4 const 2 Using where +SELECT * FROM federated.t1 WHERE id = 5 AND col2 = 'five 5 five five 5' +AND col3 = 5 +AND col4 = 55555; +id col1 col2 col3 col4 +5 5 five 5 five five 5 5 55555 +EXPLAIN SELECT * FROM federated.t1 WHERE id = 5 +AND col2 = 'Two two' AND col3 = 22 +AND col4 = 33; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ref PRIMARY,col2,col3,col4 PRIMARY 4 const 2 Using where +SELECT * FROM federated.t1 WHERE id = 5 +AND col2 = 'Two two' AND col3 = 22 +AND col4 = 33; +id col1 col2 col3 col4 +EXPLAIN SELECT * FROM federated.t1 WHERE id = 5 +AND col2 = 'five 5 five five 5' AND col3 = 5 +AND col4 = 55555; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ref PRIMARY,col2,col3,col4 PRIMARY 4 const 2 Using where +SELECT * FROM federated.t1 WHERE id = 5 +AND col2 = 'five 5 five five 5' AND col3 = 5 +AND col4 = 55555; +id col1 col2 col3 col4 +5 5 five 5 five five 5 5 55555 +EXPLAIN SELECT * FROM federated.t1 WHERE (id = 5 AND col2 = 'five 5 five five 5') +OR (col2 = 'three Three' AND col3 = 33); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range PRIMARY,col2,col3 col2 66 NULL 4 Using where +SELECT * FROM federated.t1 WHERE (id = 5 AND col2 = 'five 5 five five 5') +OR (col2 = 'three Three' AND col3 = 33); +id col1 col2 col3 col4 +5 5 five 5 five five 5 5 55555 +3 3 three Three 33 33333 +EXPLAIN SELECT * FROM federated.t1 WHERE (id = 5 AND col2 = 'five 5 five five 5') +OR (col2 = 'three Three' AND col3 = 33) +OR col4 = 1010; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL PRIMARY,col2,col3,col4 NULL NULL NULL 10 Using where +SELECT * FROM federated.t1 WHERE (id = 5 AND col2 = 'five 5 five five 5') +OR (col2 = 'three Three' AND col3 = 33) +OR col4 = 1010; +id col1 col2 col3 col4 +3 3 three Three 33 33333 +5 5 five 5 five five 5 5 55555 +10 10 Tenth ten TEN 1010101 1010 +EXPLAIN SELECT * FROM federated.t1 WHERE (id = 5 AND col2 = 'Two two') +OR (col2 = 444 AND col3 = 4444444); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL PRIMARY,col2,col3 NULL NULL NULL 10 Using where +SELECT * FROM federated.t1 WHERE (id = 5 AND col2 = 'Two two') +OR (col2 = 444 AND col3 = 4444444); +id col1 col2 col3 col4 +EXPLAIN SELECT * FROM federated.t1 WHERE id = 1 +OR col1 = 10 +OR col2 = 'Two two' +OR col3 = 33 +OR col4 = 4444444; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL PRIMARY,col1,col2,col3,col4 NULL NULL NULL 10 Using where +SELECT * FROM federated.t1 WHERE id = 1 +OR col1 = 10 +OR col2 = 'Two two' +OR col3 = 33 +OR col4 = 4444444; +id col1 col2 col3 col4 +1 1 one One 11 1111 +2 2 Two two 22 2222 +3 3 three Three 33 33333 +4 4 fourfourfour 444 4444444 +10 10 Tenth ten TEN 1010101 1010 +EXPLAIN SELECT * FROM federated.t1 WHERE id > 5; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 2 Using where +SELECT * FROM federated.t1 WHERE id > 5; +id col1 col2 col3 col4 +6 6 six six Sixsix 6666 6 +7 7 seven Sevenseven 77777 7777 +8 8 eight eight eight 88888 88 +9 9 nine Nine 999999 999999 +10 10 Tenth ten TEN 1010101 1010 +EXPLAIN SELECT * FROM federated.t1 WHERE id >= 5; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 2 Using where +SELECT * FROM federated.t1 WHERE id >= 5; +id col1 col2 col3 col4 +5 5 five 5 five five 5 5 55555 +6 6 six six Sixsix 6666 6 +7 7 seven Sevenseven 77777 7777 +8 8 eight eight eight 88888 88 +9 9 nine Nine 999999 999999 +10 10 Tenth ten TEN 1010101 1010 +EXPLAIN SELECT * FROM federated.t1 WHERE id < 5; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 2 Using where +SELECT * FROM federated.t1 WHERE id < 5; +id col1 col2 col3 col4 +1 1 one One 11 1111 +2 2 Two two 22 2222 +3 3 three Three 33 33333 +4 4 fourfourfour 444 4444444 +EXPLAIN SELECT * FROM federated.t1 WHERE id <= 5; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 2 Using where +SELECT * FROM federated.t1 WHERE id <= 5; +id col1 col2 col3 col4 +1 1 one One 11 1111 +2 2 Two two 22 2222 +3 3 three Three 33 33333 +4 4 fourfourfour 444 4444444 +5 5 five 5 five five 5 5 55555 +EXPLAIN SELECT * FROM federated.t1 WHERE id != 5; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 4 Using where +SELECT * FROM federated.t1 WHERE id != 5; +id col1 col2 col3 col4 +1 1 one One 11 1111 +2 2 Two two 22 2222 +3 3 three Three 33 33333 +4 4 fourfourfour 444 4444444 +6 6 six six Sixsix 6666 6 +7 7 seven Sevenseven 77777 7777 +8 8 eight eight eight 88888 88 +9 9 nine Nine 999999 999999 +10 10 Tenth ten TEN 1010101 1010 +EXPLAIN SELECT * FROM federated.t1 WHERE id > 3 AND id < 7; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 2 Using where +SELECT * FROM federated.t1 WHERE id > 3 AND id < 7; +id col1 col2 col3 col4 +4 4 fourfourfour 444 4444444 +5 5 five 5 five five 5 5 55555 +6 6 six six Sixsix 6666 6 +EXPLAIN SELECT * FROM federated.t1 WHERE id > 3 AND id <= 7; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 2 Using where +SELECT * FROM federated.t1 WHERE id > 3 AND id <= 7; +id col1 col2 col3 col4 +4 4 fourfourfour 444 4444444 +5 5 five 5 five five 5 5 55555 +6 6 six six Sixsix 6666 6 +7 7 seven Sevenseven 77777 7777 +EXPLAIN SELECT * FROM federated.t1 WHERE id >= 3 AND id <= 7; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 2 Using where +SELECT * FROM federated.t1 WHERE id >= 3 AND id <= 7; +id col1 col2 col3 col4 +3 3 three Three 33 33333 +4 4 fourfourfour 444 4444444 +5 5 five 5 five five 5 5 55555 +6 6 six six Sixsix 6666 6 +7 7 seven Sevenseven 77777 7777 +EXPLAIN SELECT * FROM federated.t1 WHERE id < 3 AND id <= 7; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 2 Using where +SELECT * FROM federated.t1 WHERE id < 3 AND id <= 7; +id col1 col2 col3 col4 +1 1 one One 11 1111 +2 2 Two two 22 2222 +EXPLAIN SELECT * FROM federated.t1 WHERE id < 3 AND id > 7; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +SELECT * FROM federated.t1 WHERE id < 3 AND id > 7; +id col1 col2 col3 col4 +EXPLAIN SELECT * FROM federated.t1 WHERE id < 3 OR id > 7; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 4 Using where +SELECT * FROM federated.t1 WHERE id < 3 OR id > 7; +id col1 col2 col3 col4 +1 1 one One 11 1111 +2 2 Two two 22 2222 +8 8 eight eight eight 88888 88 +9 9 nine Nine 999999 999999 +10 10 Tenth ten TEN 1010101 1010 +EXPLAIN SELECT * FROM federated.t1 WHERE col2 = 'three Three'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ref col2 col2 66 const 2 Using where +SELECT * FROM federated.t1 WHERE col2 = 'three Three'; +id col1 col2 col3 col4 +3 3 three Three 33 33333 +EXPLAIN SELECT * FROM federated.t1 WHERE col2 > 'one'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range col2 col2 66 NULL 2 Using where +SELECT * FROM federated.t1 WHERE col2 > 'one'; +id col1 col2 col3 col4 +1 1 one One 11 1111 +2 2 Two two 22 2222 +3 3 three Three 33 33333 +6 6 six six Sixsix 6666 6 +7 7 seven Sevenseven 77777 7777 +10 10 Tenth ten TEN 1010101 1010 +EXPLAIN SELECT * FROM federated.t1 WHERE col2 LIKE 's%'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range col2 col2 66 NULL 2 Using where +SELECT * FROM federated.t1 WHERE col2 LIKE 's%'; +id col1 col2 col3 col4 +7 7 seven Sevenseven 77777 7777 +6 6 six six Sixsix 6666 6 +EXPLAIN SELECT * FROM federated.t1 WHERE col2 LIKE 'si%'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range col2 col2 66 NULL 2 Using where +SELECT * FROM federated.t1 WHERE col2 LIKE 'si%'; +id col1 col2 col3 col4 +6 6 six six Sixsix 6666 6 +EXPLAIN SELECT * FROM federated.t1 WHERE col2 LIKE 'se%'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range col2 col2 66 NULL 2 Using where +SELECT * FROM federated.t1 WHERE col2 LIKE 'se%'; +id col1 col2 col3 col4 +7 7 seven Sevenseven 77777 7777 +EXPLAIN SELECT * FROM federated.t1 WHERE col2 NOT LIKE 'e%'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 10 Using where +SELECT * FROM federated.t1 WHERE col2 NOT LIKE 'e%'; +id col1 col2 col3 col4 +1 1 one One 11 1111 +2 2 Two two 22 2222 +3 3 three Three 33 33333 +4 4 fourfourfour 444 4444444 +5 5 five 5 five five 5 5 55555 +6 6 six six Sixsix 6666 6 +7 7 seven Sevenseven 77777 7777 +9 9 nine Nine 999999 999999 +10 10 Tenth ten TEN 1010101 1010 +EXPLAIN SELECT * FROM federated.t1 WHERE col2 <> 'one One'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range col2 col2 66 NULL 4 Using where +SELECT * FROM federated.t1 WHERE col2 <> 'one One'; +id col1 col2 col3 col4 +4 4 fourfourfour 444 4444444 +5 5 five 5 five five 5 5 55555 +8 8 eight eight eight 88888 88 +9 9 nine Nine 999999 999999 +2 2 Two two 22 2222 +3 3 three Three 33 33333 +6 6 six six Sixsix 6666 6 +7 7 seven Sevenseven 77777 7777 +10 10 Tenth ten TEN 1010101 1010 +DROP TABLE IF EXISTS federated.t1; +CREATE TABLE federated.t1 ( +`col1` varchar(8) NOT NULL DEFAULT '', +`col2` varchar(128) NOT NULL DEFAULT '', +`col3` varchar(20) NOT NULL DEFAULT '', +`col4` varchar(40) NOT NULL DEFAULT '', +primary key (`col1`, `col2`, `col3`, `col4`), +key 3key(`col2`,`col3`,`col4`), +key 2key (`col3`,`col4`), +key col4(col4)); +DROP TABLE IF EXISTS federated.t1; +CREATE TABLE federated.t1 ( +`col1` varchar(8) NOT NULL DEFAULT '', +`col2` varchar(128) NOT NULL DEFAULT '', +`col3` varchar(20) NOT NULL DEFAULT '', +`col4` varchar(40) NOT NULL DEFAULT '', +primary key (`col1`, `col2`, `col3`, `col4`), +key 3key(`col2`,`col3`,`col4`), +key 2key (`col3`,`col4`), +key col4(col4)) +ENGINE="FEDERATED" + COMMENT='mysql://root@127.0.0.1:SLAVE_PORT/federated/t1'; +INSERT INTO federated.t1 (col1, col2, col3, col4) +VALUES ('aaaa', 'aaaaaaaaaaaaaaaaaaa', 'ababababab', 'acacacacacacacac'); +INSERT INTO federated.t1 (col1, col2, col3, col4) +VALUES ('bbbb', 'bbbbbbbbbbbbbbbbbbb', 'bababababa', 'bcbcbcbcbcbcbcbc'); +INSERT INTO federated.t1 (col1, col2, col3, col4) +VALUES ('cccc', 'ccccccccccccccccccc', 'cacacacaca', 'cbcbcbcbcbcbcbcb'); +INSERT INTO federated.t1 (col1, col2, col3, col4) +VALUES ('dddd', 'ddddddddddddddddddd', 'dadadadada', 'dcdcdcdcdcdcdcdc'); +INSERT INTO federated.t1 (col1, col2, col3, col4) +VALUES ('eeee', 'eeeeeeeeeeeeeeeeeee', 'eaeaeaeaea', 'ecececececececec'); +INSERT INTO federated.t1 (col1, col2, col3, col4) +VALUES ('ffff', 'fffffffffffffffffff', 'fafafafafa', 'fcfcfcfcfcfcfcfc'); +INSERT INTO federated.t1 (col1, col2, col3, col4) +VALUES ('gggg', 'ggggggggggggggggggg', 'gagagagaga', 'gcgcgcgcgcgcgcgc'); +INSERT INTO federated.t1 (col1, col2, col3, col4) +VALUES ('hhhh', 'hhhhhhhhhhhhhhhhhhh', 'hahahahaha', 'hchchchchchchchc'); +EXPLAIN SELECT * FROM federated.t1 WHERE col1 = 'cccc'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ref PRIMARY PRIMARY 10 const 2 Using where +SELECT * FROM federated.t1 WHERE col1 = 'cccc'; +col1 col2 col3 col4 +cccc ccccccccccccccccccc cacacacaca cbcbcbcbcbcbcbcb +EXPLAIN SELECT * FROM federated.t1 WHERE col2 = 'eeeeeeeeeeeeeeeeeee'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ref 3key 3key 130 const 2 Using where +SELECT * FROM federated.t1 WHERE col2 = 'eeeeeeeeeeeeeeeeeee'; +col1 col2 col3 col4 +eeee eeeeeeeeeeeeeeeeeee eaeaeaeaea ecececececececec +EXPLAIN SELECT * FROM federated.t1 WHERE col3 = 'bababababa'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ref 2key 2key 22 const 2 Using where +SELECT * FROM federated.t1 WHERE col3 = 'bababababa'; +col1 col2 col3 col4 +bbbb bbbbbbbbbbbbbbbbbbb bababababa bcbcbcbcbcbcbcbc +EXPLAIN SELECT * FROM federated.t1 WHERE col1 = 'gggg' AND col2 = 'ggggggggggggggggggg'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ref PRIMARY,3key PRIMARY 140 const,const 2 Using where +SELECT * FROM federated.t1 WHERE col1 = 'gggg' AND col2 = 'ggggggggggggggggggg'; +col1 col2 col3 col4 +gggg ggggggggggggggggggg gagagagaga gcgcgcgcgcgcgcgc +EXPLAIN SELECT * FROM federated.t1 WHERE col1 = 'gggg' AND col3 = 'gagagagaga'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ref PRIMARY,2key PRIMARY 10 const 2 Using where +SELECT * FROM federated.t1 WHERE col1 = 'gggg' AND col3 = 'gagagagaga'; +col1 col2 col3 col4 +gggg ggggggggggggggggggg gagagagaga gcgcgcgcgcgcgcgc +EXPLAIN SELECT * FROM federated.t1 WHERE col1 = 'ffff' AND col4 = 'fcfcfcfcfcfcfcfc'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ref PRIMARY,col4 PRIMARY 10 const 2 Using where +SELECT * FROM federated.t1 WHERE col1 = 'ffff' AND col4 = 'fcfcfcfcfcfcfcfc'; +col1 col2 col3 col4 +ffff fffffffffffffffffff fafafafafa fcfcfcfcfcfcfcfc +EXPLAIN SELECT * FROM federated.t1 WHERE col1 > 'bbbb'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range PRIMARY PRIMARY 10 NULL 2 Using where +SELECT * FROM federated.t1 WHERE col1 > 'bbbb'; +col1 col2 col3 col4 +cccc ccccccccccccccccccc cacacacaca cbcbcbcbcbcbcbcb +dddd ddddddddddddddddddd dadadadada dcdcdcdcdcdcdcdc +eeee eeeeeeeeeeeeeeeeeee eaeaeaeaea ecececececececec +ffff fffffffffffffffffff fafafafafa fcfcfcfcfcfcfcfc +gggg ggggggggggggggggggg gagagagaga gcgcgcgcgcgcgcgc +hhhh hhhhhhhhhhhhhhhhhhh hahahahaha hchchchchchchchc +EXPLAIN SELECT * FROM federated.t1 WHERE col1 >= 'bbbb'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range PRIMARY PRIMARY 10 NULL 2 Using where +SELECT * FROM federated.t1 WHERE col1 >= 'bbbb'; +col1 col2 col3 col4 +bbbb bbbbbbbbbbbbbbbbbbb bababababa bcbcbcbcbcbcbcbc +cccc ccccccccccccccccccc cacacacaca cbcbcbcbcbcbcbcb +dddd ddddddddddddddddddd dadadadada dcdcdcdcdcdcdcdc +eeee eeeeeeeeeeeeeeeeeee eaeaeaeaea ecececececececec +ffff fffffffffffffffffff fafafafafa fcfcfcfcfcfcfcfc +gggg ggggggggggggggggggg gagagagaga gcgcgcgcgcgcgcgc +hhhh hhhhhhhhhhhhhhhhhhh hahahahaha hchchchchchchchc +EXPLAIN SELECT * FROM federated.t1 WHERE col1 < 'bbbb'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range PRIMARY PRIMARY 10 NULL 2 Using where +SELECT * FROM federated.t1 WHERE col1 < 'bbbb'; +col1 col2 col3 col4 +aaaa aaaaaaaaaaaaaaaaaaa ababababab acacacacacacacac +EXPLAIN SELECT * FROM federated.t1 WHERE col1 <= 'bbbb'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range PRIMARY PRIMARY 10 NULL 2 Using where +SELECT * FROM federated.t1 WHERE col1 <= 'bbbb'; +col1 col2 col3 col4 +aaaa aaaaaaaaaaaaaaaaaaa ababababab acacacacacacacac +bbbb bbbbbbbbbbbbbbbbbbb bababababa bcbcbcbcbcbcbcbc +EXPLAIN SELECT * FROM federated.t1 WHERE col1 <> 'bbbb'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range PRIMARY PRIMARY 10 NULL 4 Using where +SELECT * FROM federated.t1 WHERE col1 <> 'bbbb'; +col1 col2 col3 col4 +aaaa aaaaaaaaaaaaaaaaaaa ababababab acacacacacacacac +cccc ccccccccccccccccccc cacacacaca cbcbcbcbcbcbcbcb +dddd ddddddddddddddddddd dadadadada dcdcdcdcdcdcdcdc +eeee eeeeeeeeeeeeeeeeeee eaeaeaeaea ecececececececec +ffff fffffffffffffffffff fafafafafa fcfcfcfcfcfcfcfc +gggg ggggggggggggggggggg gagagagaga gcgcgcgcgcgcgcgc +hhhh hhhhhhhhhhhhhhhhhhh hahahahaha hchchchchchchchc +EXPLAIN SELECT * FROM federated.t1 WHERE col1 LIKE 'b%'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range PRIMARY PRIMARY 10 NULL 2 Using where +SELECT * FROM federated.t1 WHERE col1 LIKE 'b%'; +col1 col2 col3 col4 +bbbb bbbbbbbbbbbbbbbbbbb bababababa bcbcbcbcbcbcbcbc +EXPLAIN SELECT * FROM federated.t1 WHERE col4 LIKE '%b%'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 8 Using where +SELECT * FROM federated.t1 WHERE col4 LIKE '%b%'; +col1 col2 col3 col4 +bbbb bbbbbbbbbbbbbbbbbbb bababababa bcbcbcbcbcbcbcbc +cccc ccccccccccccccccccc cacacacaca cbcbcbcbcbcbcbcb +EXPLAIN SELECT * FROM federated.t1 WHERE col1 NOT LIKE 'c%'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 8 Using where +SELECT * FROM federated.t1 WHERE col1 NOT LIKE 'c%'; +col1 col2 col3 col4 +aaaa aaaaaaaaaaaaaaaaaaa ababababab acacacacacacacac +bbbb bbbbbbbbbbbbbbbbbbb bababababa bcbcbcbcbcbcbcbc +dddd ddddddddddddddddddd dadadadada dcdcdcdcdcdcdcdc +eeee eeeeeeeeeeeeeeeeeee eaeaeaeaea ecececececececec +ffff fffffffffffffffffff fafafafafa fcfcfcfcfcfcfcfc +gggg ggggggggggggggggggg gagagagaga gcgcgcgcgcgcgcgc +hhhh hhhhhhhhhhhhhhhhhhh hahahahaha hchchchchchchchc +EXPLAIN SELECT * FROM federated.t1 WHERE col4 NOT LIKE '%c%'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 8 Using where +SELECT * FROM federated.t1 WHERE col4 NOT LIKE '%c%'; +col1 col2 col3 col4 +DROP TABLE IF EXISTS federated.t1; +CREATE TABLE federated.t1 ( +`col1` varchar(8) NOT NULL DEFAULT '', +`col2` int(8) NOT NULL DEFAULT 0, +`col3` varchar(8) NOT NULL DEFAULT '', +primary key (`col1`, `col2`, `col3`)); +DROP TABLE IF EXISTS federated.t1; +CREATE TABLE federated.t1 ( +`col1` varchar(8) NOT NULL DEFAULT '', +`col2` varchar(8) NOT NULL DEFAULT '', +`col3` varchar(8) NOT NULL DEFAULT '', +primary key (`col1`, `col2`, `col3`)) +ENGINE="FEDERATED" + DEFAULT CHARSET=latin1 +COMMENT='mysql://root@127.0.0.1:SLAVE_PORT/federated/t1'; +INSERT INTO federated.t1 VALUES ('a00', '110', 'cc0'); +INSERT INTO federated.t1 VALUES ('aaa', '111', 'ccc'); +INSERT INTO federated.t1 VALUES ('bbb', '222', 'yyy'); +INSERT INTO federated.t1 VALUES ('ccc', '111', 'zzz'); +INSERT INTO federated.t1 VALUES ('ccd', '112', 'zzzz'); +EXPLAIN SELECT col3 FROM federated.t1 WHERE ( +(col1 = 'aaa' AND col2 >= '111') OR col1 > 'aaa') AND +(col1 < 'ccc' OR ( col1 = 'ccc' AND col2 <= '111')); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range PRIMARY PRIMARY 14 NULL 3 Using where; Using index +SELECT col3 FROM federated.t1 WHERE ( +(col1 = 'aaa' AND col2 >= '111') OR col1 > 'aaa') AND +(col1 < 'ccc' OR ( col1 = 'ccc' AND col2 <= '111')); +col3 +ccc +yyy +zzz +EXPLAIN SELECT col3 FROM federated.t1 WHERE ( +(col1 = 'aaa' AND col2 >= '111') OR col1 > 'aaa') AND +(col1 < 'ccc' OR ( col1 = 'ccc' AND col2 <= '111')); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range PRIMARY PRIMARY 20 NULL 6 Using where +SELECT col3 FROM federated.t1 WHERE ( +(col1 = 'aaa' AND col2 >= '111') OR col1 > 'aaa') AND +(col1 < 'ccc' OR ( col1 = 'ccc' AND col2 <= '111')); +col3 +ccc +yyy +zzz +DROP TABLE IF EXISTS federated.t1; +CREATE TABLE federated.t1 ( `id` int, `name` varchar(32), `floatval` float, @@ -453,6 +1182,13 @@ AND floatval IS NULL AND other IS NULL; count(*) 2 +EXPLAIN SELECT count(*) FROM federated.t1 +WHERE id IS NULL +AND name IS NULL +AND floatval IS NULL +AND other IS NULL; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 5 Using where DROP TABLE IF EXISTS federated.t1; CREATE TABLE federated.t1 ( `blurb_id` int NOT NULL DEFAULT 0, @@ -496,28 +1232,28 @@ COMMENT='mysql://root@127.0.0.1:SLAVE_PORT/federated/t1'; INSERT INTO federated.t1 VALUES (3,3,3),(1,1,1),(2,2,2),(4,4,4); EXPLAIN SELECT * FROM federated.t1 ORDER BY a; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 10000 Using filesort +1 SIMPLE t1 ALL NULL NULL NULL NULL 4 Using filesort EXPLAIN SELECT * FROM federated.t1 ORDER BY b; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 10000 Using filesort +1 SIMPLE t1 ALL NULL NULL NULL NULL 4 Using filesort EXPLAIN SELECT * FROM federated.t1 ORDER BY c; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 10000 Using filesort +1 SIMPLE t1 ALL NULL NULL NULL NULL 4 Using filesort EXPLAIN SELECT a FROM federated.t1 ORDER BY a; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 10000 Using filesort +1 SIMPLE t1 ALL NULL NULL NULL NULL 4 Using filesort EXPLAIN SELECT b FROM federated.t1 ORDER BY b; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 10000 Using filesort +1 SIMPLE t1 ALL NULL NULL NULL NULL 4 Using filesort EXPLAIN SELECT a,b FROM federated.t1 ORDER BY b; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 10000 Using filesort +1 SIMPLE t1 ALL NULL NULL NULL NULL 4 Using filesort EXPLAIN SELECT a,b FROM federated.t1; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 10000 +1 SIMPLE t1 ALL NULL NULL NULL NULL 4 EXPLAIN SELECT a,b,c FROM federated.t1; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 10000 +1 SIMPLE t1 ALL NULL NULL NULL NULL 4 DROP TABLE IF EXISTS federated.t1; CREATE TABLE federated.t1 (i1 int, i2 int, i3 int, i4 int, i5 int, i6 int, i7 int, i8 int, i9 int, i10 int, i11 int, i12 int, i13 int, i14 int, i15 int, i16 int, i17 @@ -817,6 +1553,9 @@ values (1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, "PatrickG"); UPDATE federated.t1 SET b=repeat('a',256); UPDATE federated.t1 SET i1=0, i2=0, i3=0, i4=0, i5=0, i6=0, i7=0, i8=0, i9=0, i10=0; +EXPLAIN SELECT * FROM federated.t1 WHERE i9=0 and i10=0; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 1 Using where SELECT * FROM federated.t1 WHERE i9=0 and i10=0; i1 i2 i3 i4 i5 i6 i7 i8 i9 i10 i11 i12 i13 i14 i15 i16 i17 i18 i19 i20 i21 i22 i23 i24 i25 i26 i27 i28 i29 i30 i31 i32 i33 i34 i35 i36 i37 i38 i39 i40 i41 i42 i43 i44 i45 i46 i47 i48 i49 i50 i51 i52 i53 i54 i55 i56 i57 i58 i59 i60 i61 i62 i63 i64 i65 i66 i67 i68 i69 i70 i71 i72 i73 i74 i75 i76 i77 i78 i79 i80 i81 i82 i83 i84 i85 i86 i87 i88 i89 i90 i91 i92 i93 i94 i95 i96 i97 i98 i99 i100 i101 i102 i103 i104 i105 i106 i107 i108 i109 i110 i111 i112 i113 i114 i115 i116 i117 i118 i119 i120 i121 i122 i123 i124 i125 i126 i127 i128 i129 i130 i131 i132 i133 i134 i135 i136 i137 i138 i139 i140 i141 i142 i143 i144 i145 i146 i147 i148 i149 i150 i151 i152 i153 i154 i155 i156 i157 i158 i159 i160 i161 i162 i163 i164 i165 i166 i167 i168 i169 i170 i171 i172 i173 i174 i175 i176 i177 i178 i179 i180 i181 i182 i183 i184 i185 i186 i187 i188 i189 i190 i191 i192 i193 i194 i195 i196 i197 i198 i199 i200 i201 i202 i203 i204 i205 i206 i207 i208 i209 i210 i211 i212 i213 i214 i215 i216 i217 i218 i219 i220 i221 i222 i223 i224 i225 i226 i227 i228 i229 i230 i231 i232 i233 i234 i235 i236 i237 i238 i239 i240 i241 i242 i243 i244 i245 i246 i247 i248 i249 i250 i251 i252 i253 i254 i255 i256 i257 i258 i259 i260 i261 i262 i263 i264 i265 i266 i267 i268 i269 i270 i271 i272 i273 i274 i275 i276 i277 i278 i279 i280 i281 i282 i283 i284 i285 i286 i287 i288 i289 i290 i291 i292 i293 i294 i295 i296 i297 i298 i299 i300 i301 i302 i303 i304 i305 i306 i307 i308 i309 i310 i311 i312 i313 i314 i315 i316 i317 i318 i319 i320 i321 i322 i323 i324 i325 i326 i327 i328 i329 i330 i331 i332 i333 i334 i335 i336 i337 i338 i339 i340 i341 i342 i343 i344 i345 i346 i347 i348 i349 i350 i351 i352 i353 i354 i355 i356 i357 i358 i359 i360 i361 i362 i363 i364 i365 i366 i367 i368 i369 i370 i371 i372 i373 i374 i375 i376 i377 i378 i379 i380 i381 i382 i383 i384 i385 i386 i387 i388 i389 i390 i391 i392 i393 i394 i395 i396 i397 i398 i399 i400 i401 i402 i403 i404 i405 i406 i407 i408 i409 i410 i411 i412 i413 i414 i415 i416 i417 i418 i419 i420 i421 i422 i423 i424 i425 i426 i427 i428 i429 i430 i431 i432 i433 i434 i435 i436 i437 i438 i439 i440 i441 i442 i443 i444 i445 i446 i447 i448 i449 i450 i451 i452 i453 i454 i455 i456 i457 i458 i459 i460 i461 i462 i463 i464 i465 i466 i467 i468 i469 i470 i471 i472 i473 i474 i475 i476 i477 i478 i479 i480 i481 i482 i483 i484 i485 i486 i487 i488 i489 i490 i491 i492 i493 i494 i495 i496 i497 i498 i499 i500 i501 i502 i503 i504 i505 i506 i507 i508 i509 i510 i511 i512 i513 i514 i515 i516 i517 i518 i519 i520 i521 i522 i523 i524 i525 i526 i527 i528 i529 i530 i531 i532 i533 i534 i535 i536 i537 i538 i539 i540 i541 i542 i543 i544 i545 i546 i547 i548 i549 i550 i551 i552 i553 i554 i555 i556 i557 i558 i559 i560 i561 i562 i563 i564 i565 i566 i567 i568 i569 i570 i571 i572 i573 i574 i575 i576 i577 i578 i579 i580 i581 i582 i583 i584 i585 i586 i587 i588 i589 i590 i591 i592 i593 i594 i595 i596 i597 i598 i599 i600 i601 i602 i603 i604 i605 i606 i607 i608 i609 i610 i611 i612 i613 i614 i615 i616 i617 i618 i619 i620 i621 i622 i623 i624 i625 i626 i627 i628 i629 i630 i631 i632 i633 i634 i635 i636 i637 i638 i639 i640 i641 i642 i643 i644 i645 i646 i647 i648 i649 i650 i651 i652 i653 i654 i655 i656 i657 i658 i659 i660 i661 i662 i663 i664 i665 i666 i667 i668 i669 i670 i671 i672 i673 i674 i675 i676 i677 i678 i679 i680 i681 i682 i683 i684 i685 i686 i687 i688 i689 i690 i691 i692 i693 i694 i695 i696 i697 i698 i699 i700 i701 i702 i703 i704 i705 i706 i707 i708 i709 i710 i711 i712 i713 i714 i715 i716 i717 i718 i719 i720 i721 i722 i723 i724 i725 i726 i727 i728 i729 i730 i731 i732 i733 i734 i735 i736 i737 i738 i739 i740 i741 i742 i743 i744 i745 i746 i747 i748 i749 i750 i751 i752 i753 i754 i755 i756 i757 i758 i759 i760 i761 i762 i763 i764 i765 i766 i767 i768 i769 i770 i771 i772 i773 i774 i775 i776 i777 i778 i779 i780 i781 i782 i783 i784 i785 i786 i787 i788 i789 i790 i791 i792 i793 i794 i795 i796 i797 i798 i799 i800 i801 i802 i803 i804 i805 i806 i807 i808 i809 i810 i811 i812 i813 i814 i815 i816 i817 i818 i819 i820 i821 i822 i823 i824 i825 i826 i827 i828 i829 i830 i831 i832 i833 i834 i835 i836 i837 i838 i839 i840 i841 i842 i843 i844 i845 i846 i847 i848 i849 i850 i851 i852 i853 i854 i855 i856 i857 i858 i859 i860 i861 i862 i863 i864 i865 i866 i867 i868 i869 i870 i871 i872 i873 i874 i875 i876 i877 i878 i879 i880 i881 i882 i883 i884 i885 i886 i887 i888 i889 i890 i891 i892 i893 i894 i895 i896 i897 i898 i899 i900 i901 i902 i903 i904 i905 i906 i907 i908 i909 i910 i911 i912 i913 i914 i915 i916 i917 i918 i919 i920 i921 i922 i923 i924 i925 i926 i927 i928 i929 i930 i931 i932 i933 i934 i935 i936 i937 i938 i939 i940 i941 i942 i943 i944 i945 i946 i947 i948 i949 i950 i951 i952 i953 i954 i955 i956 i957 i958 i959 i960 i961 i962 i963 i964 i965 i966 i967 i968 i969 i970 i971 i972 i973 i974 i975 i976 i977 i978 i979 i980 i981 i982 i983 i984 i985 i986 i987 i988 i989 i990 i991 i992 i993 i994 i995 i996 i997 i998 i999 i1000 b 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa @@ -912,7 +1651,7 @@ FROM federated.t1, federated.countries WHERE federated.t1.country_id = federated.countries.id; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE countries ALL PRIMARY NULL NULL NULL 5 -1 SIMPLE t1 ref country_id country_id 4 federated.countries.id 120 +1 SIMPLE t1 ref country_id country_id 4 federated.countries.id 2 SELECT federated.t1.name AS name, federated.t1.country_id AS country_id, federated.t1.other AS other, federated.countries.country AS country FROM federated.t1, federated.countries WHERE @@ -929,7 +1668,7 @@ FROM federated.t1 INNER JOIN federated.countries ON federated.t1.country_id = federated.countries.id; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE countries ALL PRIMARY NULL NULL NULL 5 -1 SIMPLE t1 ref country_id country_id 4 federated.countries.id 120 +1 SIMPLE t1 ref country_id country_id 4 federated.countries.id 2 SELECT federated.t1.name AS name, federated.t1.country_id AS country_id, federated.t1.other AS other, federated.countries.country AS country FROM federated.t1 INNER JOIN federated.countries ON @@ -947,7 +1686,7 @@ federated.t1.country_id = federated.countries.id WHERE federated.t1.name = 'Monty'; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE countries ALL PRIMARY NULL NULL NULL 5 -1 SIMPLE t1 ref country_id country_id 4 federated.countries.id 120 Using where +1 SIMPLE t1 ref country_id country_id 4 federated.countries.id 2 Using where SELECT federated.t1.name AS name, federated.t1.country_id AS country_id, federated.t1.other AS other, federated.countries.country AS country FROM federated.t1 INNER JOIN federated.countries ON @@ -960,7 +1699,7 @@ FROM federated.t1 LEFT JOIN federated.countries ON federated.t1.country_id = federated.countries.id ORDER BY federated.countries.id; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 10000 Using temporary; Using filesort +1 SIMPLE t1 ALL NULL NULL NULL NULL 5 Using temporary; Using filesort 1 SIMPLE countries eq_ref PRIMARY PRIMARY 4 federated.t1.country_id 1 SELECT federated.t1.*, federated.countries.country FROM federated.t1 LEFT JOIN federated.countries @@ -977,7 +1716,7 @@ FROM federated.t1 LEFT JOIN federated.countries ON federated.t1.country_id = federated.countries.id ORDER BY federated.countries.country; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 10000 Using temporary; Using filesort +1 SIMPLE t1 ALL NULL NULL NULL NULL 5 Using temporary; Using filesort 1 SIMPLE countries eq_ref PRIMARY PRIMARY 4 federated.t1.country_id 1 SELECT federated.t1.*, federated.countries.country FROM federated.t1 LEFT JOIN federated.countries @@ -995,7 +1734,7 @@ ON federated.t1.country_id = federated.countries.id ORDER BY federated.t1.country_id; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE countries ALL NULL NULL NULL NULL 5 Using temporary; Using filesort -1 SIMPLE t1 ref country_id country_id 4 federated.countries.id 120 +1 SIMPLE t1 ref country_id country_id 4 federated.countries.id 2 SELECT federated.t1.*, federated.countries.country FROM federated.t1 RIGHT JOIN federated.countries ON federated.t1.country_id = federated.countries.id diff --git a/mysql-test/t/federated.test b/mysql-test/t/federated.test index 1e33efe1c0e..e0cc4a9945d 100644 --- a/mysql-test/t/federated.test +++ b/mysql-test/t/federated.test @@ -1,19 +1,7 @@ ---source include/have_federated_db.inc - -source include/master-slave.inc; - -# remote table creation +source include/federated.inc; connection slave; ---replicate-ignore-db=federated -stop slave; - ---disable_warnings -# at this point, we are connected to master -DROP DATABASE IF EXISTS federated; ---enable_warnings -CREATE DATABASE federated; - +DROP TABLE IF EXISTS federated.t1; CREATE TABLE federated.t1 ( `id` int(20) NOT NULL, `name` varchar(32) NOT NULL default '' @@ -21,14 +9,10 @@ CREATE TABLE federated.t1 ( DEFAULT CHARSET=latin1; connection master; ---disable_warnings -DROP DATABASE IF EXISTS federated; ---enable_warnings -CREATE DATABASE federated; - +DROP TABLE IF EXISTS federated.t1; # test too many items (malformed) in the comment string url ---error 1005 -eval CREATE TABLE federated.t1 ( +--error 1432 +CREATE TABLE federated.t1 ( `id` int(20) NOT NULL, `name` varchar(32) NOT NULL default '' ) @@ -36,8 +20,8 @@ eval CREATE TABLE federated.t1 ( COMMENT='mysql://root@127.0.0.1:@/too/many/items/federated/t1'; # test not enough items (malformed) in the comment string url ---error 1005 -eval CREATE TABLE federated.t1 ( +--error 1432 +CREATE TABLE federated.t1 ( `id` int(20) NOT NULL, `name` varchar(32) NOT NULL default '' ) @@ -46,7 +30,7 @@ eval CREATE TABLE federated.t1 ( # test non-existant table --replace_result $SLAVE_MYPORT SLAVE_PORT ---error 1219 +--error 1434 eval CREATE TABLE federated.t1 ( `id` int(20) NOT NULL, `name` varchar(32) NOT NULL default '' @@ -56,7 +40,7 @@ eval CREATE TABLE federated.t1 ( # test bad user/password --replace_result $SLAVE_MYPORT SLAVE_PORT ---error 1218 +--error 1429 eval CREATE TABLE federated.t1 ( `id` int(20) NOT NULL, `name` varchar(32) NOT NULL default '' @@ -150,12 +134,9 @@ CREATE TABLE federated.t1 ( `name` varchar(32) NOT NULL default '', `other` int(20) NOT NULL default '0', `created` datetime default '2004-04-04 04:04:04', - PRIMARY KEY (`id`), - KEY `name` (`name`), - KEY `other_key` (`other`)) + PRIMARY KEY (`id`)) DEFAULT CHARSET=latin1; - connection master; --replace_result $SLAVE_MYPORT SLAVE_PORT eval CREATE TABLE federated.t1 ( @@ -163,9 +144,7 @@ eval CREATE TABLE federated.t1 ( `name` varchar(32) NOT NULL default '', `other` int(20) NOT NULL default '0', `created` datetime default '2004-04-04 04:04:04', - PRIMARY KEY (`id`), - KEY `name` (`name`), - KEY `other_key` (`other`)) + PRIMARY KEY (`id`)) ENGINE="FEDERATED" DEFAULT CHARSET=latin1 COMMENT='mysql://root@127.0.0.1:$SLAVE_MYPORT/federated/t1'; @@ -181,15 +160,109 @@ INSERT INTO federated.t1 (name, other) VALUES ('Ninth Name', 99999); INSERT INTO federated.t1 (name, other) VALUES ('Tenth Name', 101010); # basic select +EXPLAIN SELECT * FROM federated.t1; SELECT * FROM federated.t1; # with PRIMARY KEY index_read_idx +EXPLAIN SELECT * FROM federated.t1 WHERE id = 5; +SELECT * FROM federated.t1 WHERE id = 5; +EXPLAIN SELECT * FROM federated.t1 WHERE name = 'Sixth Name'; +SELECT * FROM federated.t1 WHERE name = 'Sixth Name'; +EXPLAIN SELECT * FROM federated.t1 WHERE id = 6 and name = 'Sixth Name'; +SELECT * FROM federated.t1 WHERE id = 6 and name = 'Sixth Name'; +EXPLAIN SELECT * FROM federated.t1 WHERE name = 'Sixth Name' AND other = 44444; +SELECT * FROM federated.t1 WHERE name = 'Sixth Name' AND other = 44444; +EXPLAIN SELECT * FROM federated.t1 WHERE name like '%th%'; +SELECT * FROM federated.t1 WHERE name like '%th%'; +UPDATE federated.t1 SET name = '3rd name' WHERE id = 3; +SELECT * FROM federated.t1 WHERE name = '3rd name'; +UPDATE federated.t1 SET name = 'Third name' WHERE name = '3rd name'; +SELECT * FROM federated.t1 WHERE name = 'Third name'; +# rnd_post, ::position +EXPLAIN SELECT * FROM federated.t1 ORDER BY id DESC; +SELECT * FROM federated.t1 ORDER BY id DESC; +EXPLAIN SELECT * FROM federated.t1 ORDER BY name; +SELECT * FROM federated.t1 ORDER BY name; +EXPLAIN SELECT * FROM federated.t1 ORDER BY name DESC; +SELECT * FROM federated.t1 ORDER BY name DESC; +EXPLAIN SELECT * FROM federated.t1 ORDER BY name ASC; +SELECT * FROM federated.t1 ORDER BY name ASC; +EXPLAIN SELECT * FROM federated.t1 GROUP BY other; +SELECT * FROM federated.t1 GROUP BY other; + +# ::delete_row +DELETE FROM federated.t1 WHERE id = 5; +SELECT * FROM federated.t1 WHERE id = 5; + +# ::delete_all_rows +DELETE FROM federated.t1; +SELECT * FROM federated.t1 WHERE id = 5; + +# previous test, but this time with indexes +connection slave; +DROP TABLE IF EXISTS federated.t1; +CREATE TABLE federated.t1 ( + `id` int(20) NOT NULL auto_increment, + `name` varchar(32) NOT NULL default '', + `other` int(20) NOT NULL default '0', + `created` datetime NOT NULL, + PRIMARY KEY (`id`), + key name(`name`), + key other(`other`), + key created(`created`)) + DEFAULT CHARSET=latin1; + +connection master; +DROP TABLE IF EXISTS federated.t1; +--replace_result $SLAVE_MYPORT SLAVE_PORT +eval CREATE TABLE federated.t1 ( + `id` int(20) NOT NULL auto_increment, + `name` varchar(32) NOT NULL default '', + `other` int(20) NOT NULL default '0', + `created` datetime NOT NULL, + PRIMARY KEY (`id`), + key name(`name`), + key other(`other`), + key created(`created`)) + ENGINE="FEDERATED" DEFAULT CHARSET=latin1 + COMMENT='mysql://root@127.0.0.1:$SLAVE_MYPORT/federated/t1'; + +INSERT INTO federated.t1 (name, other, created) + VALUES ('First Name', 11111, '2004-01-01 01:01:01'); +INSERT INTO federated.t1 (name, other, created) + VALUES ('Second Name', 22222, '2004-01-23 02:43:00'); +INSERT INTO federated.t1 (name, other, created) + VALUES ('Third Name', 33333, '2004-02-14 02:14:00'); +INSERT INTO federated.t1 (name, other, created) + VALUES ('Fourth Name', 44444, '2003-04-05 00:00:00'); +INSERT INTO federated.t1 (name, other, created) + VALUES ('Fifth Name', 55555, '2001-02-02 02:02:02'); +INSERT INTO federated.t1 (name, other, created) + VALUES ('Sixth Name', 66666, '2005-06-06 15:30:00'); +INSERT INTO federated.t1 (name, other, created) + VALUES ('Seventh Name', 77777, '2003-12-12 18:32:00'); +INSERT INTO federated.t1 (name, other, created) + VALUES ('Eigth Name', 88888, '2005-03-12 11:00:00'); +INSERT INTO federated.t1 (name, other, created) + VALUES ('Ninth Name', 99999, '2005-03-12 11:00:01'); +INSERT INTO federated.t1 (name, other, created) + VALUES ('Tenth Name', 101010, '2005-03-12 12:00:01'); + +# basic select +EXPLAIN SELECT * FROM federated.t1; +SELECT * FROM federated.t1; +# with PRIMARY KEY index_read_idx +EXPLAIN SELECT * FROM federated.t1 WHERE id = 5; SELECT * FROM federated.t1 WHERE id = 5; # with regular key index_read -> index_read_idx +EXPLAIN SELECT * FROM federated.t1 WHERE name = 'Sixth Name'; SELECT * FROM federated.t1 WHERE name = 'Sixth Name'; # regular and PRIMARY KEY index_read_idx +EXPLAIN SELECT * FROM federated.t1 WHERE id = 6 and name = 'Sixth Name'; SELECT * FROM federated.t1 WHERE id = 6 and name = 'Sixth Name'; # with regular key index_read -> index_read_idx +EXPLAIN SELECT * FROM federated.t1 WHERE other = 44444; SELECT * FROM federated.t1 WHERE other = 44444; +EXPLAIN SELECT * FROM federated.t1 WHERE name like '%th%'; SELECT * FROM federated.t1 WHERE name like '%th%'; # update - update_row, index_read_idx UPDATE federated.t1 SET name = '3rd name' WHERE id = 3; @@ -211,7 +284,6 @@ SELECT * FROM federated.t1 WHERE id = 5; # ::delete_all_rows DELETE FROM federated.t1; SELECT * FROM federated.t1 WHERE id = 5; - connection slave; DROP TABLE IF EXISTS federated.t1; CREATE TABLE federated.t1 ( @@ -253,8 +325,7 @@ SET name = 'Fourth Name', other = 'four four four' WHERE name IS NULL AND other IS NULL; UPDATE federated.t1 SET other = 'two two two two' WHERE name = 'Second Name'; -UPDATE federated.t1 SET other = 'seven seven' WHERE name like 'Sec%'; -UPDATE federated.t1 SET other = 'seven seven' WHERE name = 'Seventh Name'; +UPDATE federated.t1 SET other = 'seven seven' WHERE name like 'Sev%'; UPDATE federated.t1 SET name = 'Tenth Name' WHERE other like 'fee fie%'; SELECT * FROM federated.t1 WHERE name IS NULL OR other IS NULL ; SELECT * FROM federated.t1; @@ -338,6 +409,272 @@ SELECT * FROM federated.t1 WHERE name='third'; SELECT * FROM federated.t1 WHERE other=2222; SELECT * FROM federated.t1 WHERE name='third' and other=2222; +# more multi-column indexes, in the primary key +connection slave; +DROP TABLE IF EXISTS federated.t1; +CREATE TABLE federated.t1 ( + `id` int NOT NULL auto_increment, + `col1` int(10) NOT NULL DEFAULT 0, + `col2` varchar(64) NOT NULL DEFAULT '', + `col3` int(20) NOT NULL, + `col4` int(40) NOT NULL, + primary key (`id`, `col1`, `col2`, `col3`, `col4`), + key col1(col1), + key col2(col2), + key col3(col3), + key col4(col4)); + +connection master; +DROP TABLE IF EXISTS federated.t1; +--replace_result $SLAVE_MYPORT SLAVE_PORT +eval CREATE TABLE federated.t1 ( + `id` int NOT NULL auto_increment, + `col1` int(10) NOT NULL DEFAULT 0, + `col2` varchar(64) NOT NULL DEFAULT '', + `col3` int(20) NOT NULL, + `col4` int(40) NOT NULL, + primary key (`id`, `col1`, `col2`, `col3`, `col4`), + key col1(col1), + key col2(col2), + key col3(col3), + key col4(col4)) + ENGINE="FEDERATED" + COMMENT='mysql://root@127.0.0.1:$SLAVE_MYPORT/federated/t1'; + +INSERT INTO federated.t1 (col1, col2, col3, col4) + VALUES (1, 'one One', 11, 1111); +INSERT INTO federated.t1 (col1, col2, col3, col4) + VALUES (2, 'Two two', 22, 2222); +INSERT INTO federated.t1 (col1, col2, col3, col4) + VALUES (3, 'three Three', 33, 33333); +INSERT INTO federated.t1 (col1, col2, col3, col4) + VALUES (4, 'fourfourfour', 444, 4444444); +INSERT INTO federated.t1 (col1, col2, col3, col4) + VALUES (5, 'five 5 five five 5', 5, 55555); +INSERT INTO federated.t1 (col1, col2, col3, col4) + VALUES (6, 'six six Sixsix', 6666, 6); +INSERT INTO federated.t1 (col1, col2, col3, col4) + VALUES (7, 'seven Sevenseven', 77777, 7777); +INSERT INTO federated.t1 (col1, col2, col3, col4) + VALUES (8, 'eight eight eight', 88888, 88); +INSERT INTO federated.t1 (col1, col2, col3, col4) + VALUES (9, 'nine Nine', 999999, 999999); +INSERT INTO federated.t1 (col1, col2, col3, col4) + VALUES (10, 'Tenth ten TEN', 1010101, 1010); + +EXPLAIN SELECT * FROM federated.t1 WHERE col2 = 'two two'; +SELECT * FROM federated.t1 WHERE col2 = 'two two'; +EXPLAIN SELECT * FROM federated.t1 WHERE col2 = 'two Two'; +SELECT * FROM federated.t1 WHERE col2 = 'two Two'; +SELECT * FROM federated.t1 WHERE id = 3; +EXPLAIN SELECT * FROM federated.t1 WHERE id = 3; +SELECT * FROM federated.t1 WHERE id = 3 AND col1 = 3; +EXPLAIN SELECT * FROM federated.t1 WHERE id = 3 AND col1 = 3; +SELECT * FROM federated.t1 WHERE id = 4 AND col1 = 4 AND col2 = 'Two two'; +EXPLAIN SELECT * FROM federated.t1 WHERE id = 4 AND col1 = 4 AND col2 = 'Two two'; +SELECT * FROM federated.t1 WHERE id = 4 AND col1 = 4 AND col2 = 'fourfourfour'; +EXPLAIN SELECT * FROM federated.t1 WHERE id = 4 AND col1 = 4 AND col2 = 'fourfourfour'; +SELECT * FROM federated.t1 WHERE id = 5 AND col2 = 'five 5 five five 5' + AND col3 = 5; +EXPLAIN SELECT * FROM federated.t1 WHERE id = 5 AND col2 = 'five 5 five five 5' + AND col3 = 5; +EXPLAIN SELECT * FROM federated.t1 WHERE id = 5 AND col2 = 'five 5 five five 5' + AND col3 = 5 + AND col4 = 55555; +SELECT * FROM federated.t1 WHERE id = 5 AND col2 = 'five 5 five five 5' + AND col3 = 5 + AND col4 = 55555; +EXPLAIN SELECT * FROM federated.t1 WHERE id = 5 + AND col2 = 'Two two' AND col3 = 22 + AND col4 = 33; +SELECT * FROM federated.t1 WHERE id = 5 + AND col2 = 'Two two' AND col3 = 22 + AND col4 = 33; +EXPLAIN SELECT * FROM federated.t1 WHERE id = 5 + AND col2 = 'five 5 five five 5' AND col3 = 5 + AND col4 = 55555; +SELECT * FROM federated.t1 WHERE id = 5 + AND col2 = 'five 5 five five 5' AND col3 = 5 + AND col4 = 55555; +EXPLAIN SELECT * FROM federated.t1 WHERE (id = 5 AND col2 = 'five 5 five five 5') + OR (col2 = 'three Three' AND col3 = 33); +SELECT * FROM federated.t1 WHERE (id = 5 AND col2 = 'five 5 five five 5') + OR (col2 = 'three Three' AND col3 = 33); +EXPLAIN SELECT * FROM federated.t1 WHERE (id = 5 AND col2 = 'five 5 five five 5') + OR (col2 = 'three Three' AND col3 = 33) + OR col4 = 1010; +SELECT * FROM federated.t1 WHERE (id = 5 AND col2 = 'five 5 five five 5') + OR (col2 = 'three Three' AND col3 = 33) + OR col4 = 1010; +EXPLAIN SELECT * FROM federated.t1 WHERE (id = 5 AND col2 = 'Two two') + OR (col2 = 444 AND col3 = 4444444); +SELECT * FROM federated.t1 WHERE (id = 5 AND col2 = 'Two two') + OR (col2 = 444 AND col3 = 4444444); +EXPLAIN SELECT * FROM federated.t1 WHERE id = 1 + OR col1 = 10 + OR col2 = 'Two two' + OR col3 = 33 + OR col4 = 4444444; +SELECT * FROM federated.t1 WHERE id = 1 + OR col1 = 10 + OR col2 = 'Two two' + OR col3 = 33 + OR col4 = 4444444; +EXPLAIN SELECT * FROM federated.t1 WHERE id > 5; +SELECT * FROM federated.t1 WHERE id > 5; +EXPLAIN SELECT * FROM federated.t1 WHERE id >= 5; +SELECT * FROM federated.t1 WHERE id >= 5; +EXPLAIN SELECT * FROM federated.t1 WHERE id < 5; +SELECT * FROM federated.t1 WHERE id < 5; +EXPLAIN SELECT * FROM federated.t1 WHERE id <= 5; +SELECT * FROM federated.t1 WHERE id <= 5; +EXPLAIN SELECT * FROM federated.t1 WHERE id != 5; +SELECT * FROM federated.t1 WHERE id != 5; +EXPLAIN SELECT * FROM federated.t1 WHERE id > 3 AND id < 7; +SELECT * FROM federated.t1 WHERE id > 3 AND id < 7; +EXPLAIN SELECT * FROM federated.t1 WHERE id > 3 AND id <= 7; +SELECT * FROM federated.t1 WHERE id > 3 AND id <= 7; +EXPLAIN SELECT * FROM federated.t1 WHERE id >= 3 AND id <= 7; +SELECT * FROM federated.t1 WHERE id >= 3 AND id <= 7; +EXPLAIN SELECT * FROM federated.t1 WHERE id < 3 AND id <= 7; +SELECT * FROM federated.t1 WHERE id < 3 AND id <= 7; +EXPLAIN SELECT * FROM federated.t1 WHERE id < 3 AND id > 7; +SELECT * FROM federated.t1 WHERE id < 3 AND id > 7; +EXPLAIN SELECT * FROM federated.t1 WHERE id < 3 OR id > 7; +SELECT * FROM federated.t1 WHERE id < 3 OR id > 7; +EXPLAIN SELECT * FROM federated.t1 WHERE col2 = 'three Three'; +SELECT * FROM federated.t1 WHERE col2 = 'three Three'; +EXPLAIN SELECT * FROM federated.t1 WHERE col2 > 'one'; +SELECT * FROM federated.t1 WHERE col2 > 'one'; +EXPLAIN SELECT * FROM federated.t1 WHERE col2 LIKE 's%'; +SELECT * FROM federated.t1 WHERE col2 LIKE 's%'; +EXPLAIN SELECT * FROM federated.t1 WHERE col2 LIKE 'si%'; +SELECT * FROM federated.t1 WHERE col2 LIKE 'si%'; +EXPLAIN SELECT * FROM federated.t1 WHERE col2 LIKE 'se%'; +SELECT * FROM federated.t1 WHERE col2 LIKE 'se%'; +EXPLAIN SELECT * FROM federated.t1 WHERE col2 NOT LIKE 'e%'; +SELECT * FROM federated.t1 WHERE col2 NOT LIKE 'e%'; +EXPLAIN SELECT * FROM federated.t1 WHERE col2 <> 'one One'; +SELECT * FROM federated.t1 WHERE col2 <> 'one One'; + +# more multi-column indexes, in the primary key +connection slave; +DROP TABLE IF EXISTS federated.t1; +CREATE TABLE federated.t1 ( + `col1` varchar(8) NOT NULL DEFAULT '', + `col2` varchar(128) NOT NULL DEFAULT '', + `col3` varchar(20) NOT NULL DEFAULT '', + `col4` varchar(40) NOT NULL DEFAULT '', + primary key (`col1`, `col2`, `col3`, `col4`), + key 3key(`col2`,`col3`,`col4`), + key 2key (`col3`,`col4`), + key col4(col4)); + +connection master; +DROP TABLE IF EXISTS federated.t1; +--replace_result $SLAVE_MYPORT SLAVE_PORT +eval CREATE TABLE federated.t1 ( + `col1` varchar(8) NOT NULL DEFAULT '', + `col2` varchar(128) NOT NULL DEFAULT '', + `col3` varchar(20) NOT NULL DEFAULT '', + `col4` varchar(40) NOT NULL DEFAULT '', + primary key (`col1`, `col2`, `col3`, `col4`), + key 3key(`col2`,`col3`,`col4`), + key 2key (`col3`,`col4`), + key col4(col4)) + ENGINE="FEDERATED" + COMMENT='mysql://root@127.0.0.1:$SLAVE_MYPORT/federated/t1'; + +INSERT INTO federated.t1 (col1, col2, col3, col4) + VALUES ('aaaa', 'aaaaaaaaaaaaaaaaaaa', 'ababababab', 'acacacacacacacac'); +INSERT INTO federated.t1 (col1, col2, col3, col4) + VALUES ('bbbb', 'bbbbbbbbbbbbbbbbbbb', 'bababababa', 'bcbcbcbcbcbcbcbc'); +INSERT INTO federated.t1 (col1, col2, col3, col4) + VALUES ('cccc', 'ccccccccccccccccccc', 'cacacacaca', 'cbcbcbcbcbcbcbcb'); +INSERT INTO federated.t1 (col1, col2, col3, col4) + VALUES ('dddd', 'ddddddddddddddddddd', 'dadadadada', 'dcdcdcdcdcdcdcdc'); +INSERT INTO federated.t1 (col1, col2, col3, col4) + VALUES ('eeee', 'eeeeeeeeeeeeeeeeeee', 'eaeaeaeaea', 'ecececececececec'); +INSERT INTO federated.t1 (col1, col2, col3, col4) + VALUES ('ffff', 'fffffffffffffffffff', 'fafafafafa', 'fcfcfcfcfcfcfcfc'); +INSERT INTO federated.t1 (col1, col2, col3, col4) + VALUES ('gggg', 'ggggggggggggggggggg', 'gagagagaga', 'gcgcgcgcgcgcgcgc'); +INSERT INTO federated.t1 (col1, col2, col3, col4) + VALUES ('hhhh', 'hhhhhhhhhhhhhhhhhhh', 'hahahahaha', 'hchchchchchchchc'); + +EXPLAIN SELECT * FROM federated.t1 WHERE col1 = 'cccc'; +SELECT * FROM federated.t1 WHERE col1 = 'cccc'; +EXPLAIN SELECT * FROM federated.t1 WHERE col2 = 'eeeeeeeeeeeeeeeeeee'; +SELECT * FROM federated.t1 WHERE col2 = 'eeeeeeeeeeeeeeeeeee'; +EXPLAIN SELECT * FROM federated.t1 WHERE col3 = 'bababababa'; +SELECT * FROM federated.t1 WHERE col3 = 'bababababa'; +EXPLAIN SELECT * FROM federated.t1 WHERE col1 = 'gggg' AND col2 = 'ggggggggggggggggggg'; +SELECT * FROM federated.t1 WHERE col1 = 'gggg' AND col2 = 'ggggggggggggggggggg'; +EXPLAIN SELECT * FROM federated.t1 WHERE col1 = 'gggg' AND col3 = 'gagagagaga'; +SELECT * FROM federated.t1 WHERE col1 = 'gggg' AND col3 = 'gagagagaga'; +EXPLAIN SELECT * FROM federated.t1 WHERE col1 = 'ffff' AND col4 = 'fcfcfcfcfcfcfcfc'; +SELECT * FROM federated.t1 WHERE col1 = 'ffff' AND col4 = 'fcfcfcfcfcfcfcfc'; +EXPLAIN SELECT * FROM federated.t1 WHERE col1 > 'bbbb'; +SELECT * FROM federated.t1 WHERE col1 > 'bbbb'; +EXPLAIN SELECT * FROM federated.t1 WHERE col1 >= 'bbbb'; +SELECT * FROM federated.t1 WHERE col1 >= 'bbbb'; +EXPLAIN SELECT * FROM federated.t1 WHERE col1 < 'bbbb'; +SELECT * FROM federated.t1 WHERE col1 < 'bbbb'; +EXPLAIN SELECT * FROM federated.t1 WHERE col1 <= 'bbbb'; +SELECT * FROM federated.t1 WHERE col1 <= 'bbbb'; +EXPLAIN SELECT * FROM federated.t1 WHERE col1 <> 'bbbb'; +SELECT * FROM federated.t1 WHERE col1 <> 'bbbb'; +EXPLAIN SELECT * FROM federated.t1 WHERE col1 LIKE 'b%'; +SELECT * FROM federated.t1 WHERE col1 LIKE 'b%'; +EXPLAIN SELECT * FROM federated.t1 WHERE col4 LIKE '%b%'; +SELECT * FROM federated.t1 WHERE col4 LIKE '%b%'; +EXPLAIN SELECT * FROM federated.t1 WHERE col1 NOT LIKE 'c%'; +SELECT * FROM federated.t1 WHERE col1 NOT LIKE 'c%'; +EXPLAIN SELECT * FROM federated.t1 WHERE col4 NOT LIKE '%c%'; +SELECT * FROM federated.t1 WHERE col4 NOT LIKE '%c%'; +connection slave; +DROP TABLE IF EXISTS federated.t1; +CREATE TABLE federated.t1 ( + `col1` varchar(8) NOT NULL DEFAULT '', + `col2` int(8) NOT NULL DEFAULT 0, + `col3` varchar(8) NOT NULL DEFAULT '', + primary key (`col1`, `col2`, `col3`)); + +connection master; +DROP TABLE IF EXISTS federated.t1; +--replace_result $SLAVE_MYPORT SLAVE_PORT +eval CREATE TABLE federated.t1 ( + `col1` varchar(8) NOT NULL DEFAULT '', + `col2` varchar(8) NOT NULL DEFAULT '', + `col3` varchar(8) NOT NULL DEFAULT '', + primary key (`col1`, `col2`, `col3`)) + ENGINE="FEDERATED" + DEFAULT CHARSET=latin1 + COMMENT='mysql://root@127.0.0.1:$SLAVE_MYPORT/federated/t1'; + +INSERT INTO federated.t1 VALUES ('a00', '110', 'cc0'); +INSERT INTO federated.t1 VALUES ('aaa', '111', 'ccc'); +INSERT INTO federated.t1 VALUES ('bbb', '222', 'yyy'); +INSERT INTO federated.t1 VALUES ('ccc', '111', 'zzz'); +INSERT INTO federated.t1 VALUES ('ccd', '112', 'zzzz'); + +# let's see what the foreign database says +connection slave; +EXPLAIN SELECT col3 FROM federated.t1 WHERE ( +(col1 = 'aaa' AND col2 >= '111') OR col1 > 'aaa') AND +(col1 < 'ccc' OR ( col1 = 'ccc' AND col2 <= '111')); +SELECT col3 FROM federated.t1 WHERE ( +(col1 = 'aaa' AND col2 >= '111') OR col1 > 'aaa') AND +(col1 < 'ccc' OR ( col1 = 'ccc' AND col2 <= '111')); + +connection master; +EXPLAIN SELECT col3 FROM federated.t1 WHERE ( +(col1 = 'aaa' AND col2 >= '111') OR col1 > 'aaa') AND +(col1 < 'ccc' OR ( col1 = 'ccc' AND col2 <= '111')); +SELECT col3 FROM federated.t1 WHERE ( +(col1 = 'aaa' AND col2 >= '111') OR col1 > 'aaa') AND +(col1 < 'ccc' OR ( col1 = 'ccc' AND col2 <= '111')); + # test NULLs connection slave; DROP TABLE IF EXISTS federated.t1; @@ -374,6 +711,11 @@ WHERE id IS NULL AND name IS NULL AND floatval IS NULL AND other IS NULL; +EXPLAIN SELECT count(*) FROM federated.t1 +WHERE id IS NULL +AND name IS NULL +AND floatval IS NULL +AND other IS NULL; connection slave; DROP TABLE IF EXISTS federated.t1; @@ -735,6 +1077,7 @@ values (1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, "PatrickG"); UPDATE federated.t1 SET b=repeat('a',256); UPDATE federated.t1 SET i1=0, i2=0, i3=0, i4=0, i5=0, i6=0, i7=0, i8=0, i9=0, i10=0; +EXPLAIN SELECT * FROM federated.t1 WHERE i9=0 and i10=0; SELECT * FROM federated.t1 WHERE i9=0 and i10=0; UPDATE federated.t1 SET i50=20; SELECT * FROM federated.t1; @@ -928,14 +1271,4 @@ ORDER BY federated.t1.country_id; DROP TABLE federated.countries; -connection master; ---disable_warnings -DROP TABLE IF EXISTS federated.t1; -DROP DATABASE IF EXISTS federated; ---enable_warnings - -connection slave; ---disable_warnings -DROP TABLE IF EXISTS federated.t1; -DROP DATABASE IF EXISTS federated; ---enable_warnings +source include/federated_cleanup.inc; diff --git a/sql/ha_federated.cc b/sql/ha_federated.cc index 383652e4a3a..0ad0bd6cb9d 100644 --- a/sql/ha_federated.cc +++ b/sql/ha_federated.cc @@ -54,6 +54,7 @@ ***IMPORTANT*** + This is a first release, conceptual release Only 'mysql://' is supported at this release. @@ -345,6 +346,7 @@ */ +#define FEDERATED_DEBUG 1 #include "mysql_priv.h" #ifdef USE_PRAGMA_IMPLEMENTATION #pragma implementation // gcc: Class implementation @@ -352,7 +354,8 @@ #ifdef HAVE_FEDERATED_DB #include "ha_federated.h" -#define MAX_REMOTE_SIZE IO_SIZE + +#include "m_string.h" /* Variables for federated share methods */ static HASH federated_open_tables; // Hash used to track open // tables @@ -413,13 +416,14 @@ bool federated_db_end() return FALSE; } - /* Check (in create) whether the tables exists, and that it can be connected to SYNOPSIS check_foreign_data_source() share pointer to FEDERATED share + table_create_flag tells us that ::create is the caller, + therefore, return CANT_CREATE_FEDERATED_TABLE DESCRIPTION This method first checks that the connection information that parse url @@ -427,23 +431,24 @@ bool federated_db_end() table, and if so, does the foreign table exist. */ -static int check_foreign_data_source(FEDERATED_SHARE *share) +static int check_foreign_data_source( + FEDERATED_SHARE *share, + bool table_create_flag) { - char escaped_table_base_name[IO_SIZE]; - MYSQL *mysql; - MYSQL_RES *result=0; + char escaped_table_name[STRING_BUFFER_USUAL_SIZE]; + char query_buffer[FEDERATED_QUERY_BUFFER_SIZE]; + char error_buffer[FEDERATED_QUERY_BUFFER_SIZE]; uint error_code; - char query_buffer[IO_SIZE]; - char error_buffer[IO_SIZE]; String query(query_buffer, sizeof(query_buffer), &my_charset_bin); + MYSQL_RES *result= 0; + MYSQL *mysql; DBUG_ENTER("ha_federated::check_foreign_data_source"); + /* Zero the length, otherwise the string will have misc chars */ query.length(0); /* error out if we can't alloc memory for mysql_init(NULL) (per Georg) */ if (! (mysql= mysql_init(NULL))) - { DBUG_RETURN(HA_ERR_OUT_OF_MEM); - } /* check if we can connect */ if (!mysql_real_connect(mysql, share->hostname, @@ -453,11 +458,17 @@ static int check_foreign_data_source(FEDERATED_SHARE *share) share->port, share->socket, 0)) { + /* + we want the correct error message, but it to return + ER_CANT_CREATE_FEDERATED_TABLE if called by ::create + */ + error_code= table_create_flag? + ER_CANT_CREATE_FEDERATED_TABLE : ER_CONNECT_TO_FOREIGN_DATA_SRC; + my_sprintf(error_buffer, - (error_buffer, - "unable to connect to database '%s' on host '%s as user '%s' !", - share->database, share->hostname, share->username)); - error_code= ER_CONNECT_TO_MASTER; + (error_buffer, " database %s username %s hostname %s", + share->database, share->username, share->hostname)); + my_error(ER_CONNECT_TO_FOREIGN_DATA_SRC, MYF(0), error_buffer); goto error; } else @@ -468,48 +479,41 @@ static int check_foreign_data_source(FEDERATED_SHARE *share) with transactions */ mysql->reconnect= 1; - /* + /* Note: I am not using INORMATION_SCHEMA because this needs to work with < 5.0 if we can connect, then make sure the table exists */ - query.append("SHOW TABLES LIKE '"); - escape_string_for_mysql(&my_charset_bin, (char *)escaped_table_base_name, - sizeof(escaped_table_base_name), - share->table_base_name, - share->table_base_name_length); - query.append(escaped_table_base_name); - query.append("'"); + query.append("SELECT * FROM `", 15); + escape_string_for_mysql(&my_charset_bin, (char *)escaped_table_name, + sizeof(escaped_table_name), + share->table_name, + share->table_name_length); + query.append(escaped_table_name); + query.append("` WHERE 1=0", 11); - error_code= ER_QUERY_ON_MASTER; + /* + an || here on the error code - if it's a syntax error, or missing + table, give us that, or if the connection has problems, give us that + */ if (mysql_real_query(mysql, query.ptr(), query.length())) - goto error; - - result= mysql_store_result(mysql); - if (! result) - goto error; - - /* if ! mysql_num_rows, the table doesn't exist, send error */ - if (! mysql_num_rows(result)) { - my_sprintf(error_buffer, - (error_buffer, "foreign table '%s' does not exist!", - share->table_base_name)); + error_code= table_create_flag ? + ER_CANT_CREATE_FEDERATED_TABLE : ER_FOREIGN_DATA_SRC_DOESNT_EXIST; + my_sprintf(error_buffer, (error_buffer, ": %d : %s", + mysql_errno(mysql), mysql_error(mysql))); + + my_error(error_code, MYF(0), error_buffer); goto error; } - mysql_free_result(result); - result= 0; + mysql_close(mysql); } DBUG_RETURN(0); error: - if (result) - mysql_free_result(result); mysql_close(mysql); - my_error(error_code, MYF(0), error_buffer); DBUG_RETURN(error_code); - } @@ -545,22 +549,27 @@ error: 'password' and 'port' are both optional. RETURN VALUE - 0 success - 1 failure, wrong string format + 0 success + error_num particular error code */ static int parse_url(FEDERATED_SHARE *share, TABLE *table, uint table_create_flag) { - uint error_num= (table_create_flag ? ER_CANT_CREATE_TABLE : - ER_CONNECT_TO_MASTER); + uint error_num= (table_create_flag ? + ER_FOREIGN_DATA_STRING_INVALID_CANT_CREATE : + ER_FOREIGN_DATA_STRING_INVALID); DBUG_ENTER("ha_federated::parse_url"); share->port= 0; - share->socket= 0; share->scheme= my_strdup(table->s->comment, MYF(0)); + DBUG_PRINT("info",("parse_url alloced share->scheme %lx", share->scheme)); + /* + remove addition of null terminator and store length + for each string in share + */ if ((share->username= strstr(share->scheme, "://"))) { share->scheme[share->username - share->scheme]= '\0'; @@ -613,20 +622,20 @@ static int parse_url(FEDERATED_SHARE *share, TABLE *table, share->port= atoi(share->sport); } - if ((share->table_base_name= strchr(share->database, '/'))) + if ((share->table_name= strchr(share->database, '/'))) { - share->database[share->table_base_name - share->database]= '\0'; - share->table_base_name++; + share->database[share->table_name - share->database]= '\0'; + share->table_name++; } else goto error; - share->table_base_name_length= strlen(share->table_base_name); + share->table_name_length= strlen(share->table_name); } else goto error; /* make sure there's not an extra / */ - if ((strchr(share->table_base_name, '/'))) + if ((strchr(share->table_name, '/'))) goto error; if (share->hostname[0] == '\0') @@ -645,7 +654,7 @@ static int parse_url(FEDERATED_SHARE *share, TABLE *table, hostname %s port %d database %s tablename %s\n", share->scheme, share->username, share->password, share->hostname, share->port, share->database, - share->table_base_name)); + share->table_name)); } else goto error; @@ -656,9 +665,16 @@ static int parse_url(FEDERATED_SHARE *share, TABLE *table, DBUG_RETURN(0); error: - my_error(error_num, MYF(0), - "connection string is not in the correct format",0); - DBUG_RETURN(1); + if (share->scheme) + { + DBUG_PRINT("info", + ("error: parse_url. Returning error code %d \ + freeing share->scheme %lx", error_num, share->scheme)); + my_free((gptr) share->scheme, MYF(0)); + share->scheme= 0; + } + my_error(error_num, MYF(0), table->s->comment); + DBUG_RETURN(error_num); } @@ -684,9 +700,9 @@ error: uint ha_federated::convert_row_to_internal_format(byte *record, MYSQL_ROW row) { - ulong *lengths; uint num_fields; - uint x= 0; + ulong *lengths; + Field **field; DBUG_ENTER("ha_federated::convert_row_to_internal_format"); @@ -695,14 +711,105 @@ uint ha_federated::convert_row_to_internal_format(byte *record, MYSQL_ROW row) memset(record, 0, table->s->null_bytes); - for (Field **field= table->field; *field; field++, x++) + for (field= table->field; *field; field++) { + /* + index variable to move us through the row at the + same iterative step as the field + */ + int x= field - table->field; + my_ptrdiff_t old_ptr; + old_ptr= (my_ptrdiff_t) (record - table->record[0]); + (*field)->move_field(old_ptr); if (!row[x]) (*field)->set_null(); else + { + (*field)->set_notnull(); (*field)->store(row[x], lengths[x], &my_charset_bin); + } + (*field)->move_field(-old_ptr); } + DBUG_DUMP("record", record, table->s->reclength); + DBUG_RETURN(0); +} + +static bool emit_key_part_name(String *to, KEY_PART_INFO *part) +{ + DBUG_ENTER("emit_key_part_name"); + if (to->append(" `", 2) || + to->append(part->field->field_name) || + to->append("`", 1)) + DBUG_RETURN(1); // Out of memory + DBUG_RETURN(0); +} + +static bool emit_key_part_element(String *to, KEY_PART_INFO *part, + bool needs_quotes, bool is_like, + const byte *ptr, uint len) +{ + Field *field= part->field; + DBUG_ENTER("emit_key_part_element"); + + if (needs_quotes && to->append("'", 1)) + DBUG_RETURN(1); + + if (part->type == HA_KEYTYPE_BIT) + { + char buff[STRING_BUFFER_USUAL_SIZE], *buf= buff; + + *buf++= '0'; + *buf++= 'x'; + for (; len; ptr++,len--) + { + uint tmp= (uint)(uchar) *ptr; + *buf++= _dig_vec_upper[tmp >> 4]; + *buf++= _dig_vec_upper[tmp & 15]; + } + if (to->append(buff, (uint)(buf - buff))) + DBUG_RETURN(1); + } + else if (part->key_part_flag & HA_BLOB_PART) + { + String blob; + uint blob_length= uint2korr(ptr); + blob.set_quick((char*) ptr+HA_KEY_BLOB_LENGTH, + blob_length, &my_charset_bin); + if (append_escaped(to, &blob)) + DBUG_RETURN(1); + } + else if (part->key_part_flag & HA_VAR_LENGTH_PART) + { + String varchar; + uint var_length= uint2korr(ptr); + varchar.set_quick((char*) ptr+HA_KEY_BLOB_LENGTH, + var_length, &my_charset_bin); + if (append_escaped(to, &varchar)) + DBUG_RETURN(1); + } + else + { + char strbuff[MAX_FIELD_WIDTH]; + String str(strbuff, sizeof(strbuff), part->field->charset()), *res; + + res= field->val_str(&str, (char *)ptr); + + if (field->result_type() == STRING_RESULT) + { + if (append_escaped(to, res)) + DBUG_RETURN(1); + } + else if (to->append(res->ptr(), res->length())) + DBUG_RETURN(1); + } + + if (is_like && to->append("%", 1)) + DBUG_RETURN(1); + + if (needs_quotes && to->append("'",1)) + DBUG_RETURN(1); + DBUG_RETURN(0); } @@ -716,6 +823,8 @@ uint ha_federated::convert_row_to_internal_format(byte *record, MYSQL_ROW row) key_info KEY struct pointer key byte pointer containing key key_length length of key + range_type 0 - no range, 1 - min range, 2 - max range + (see enum range_operation) DESCRIPTION Using iteration through all the keys via a KEY_PART_INFO pointer, @@ -726,112 +835,391 @@ uint ha_federated::convert_row_to_internal_format(byte *record, MYSQL_ROW row) 0 After all keys have been accounted for to create the WHERE clause 1 No keys found - */ + Range flags Table per Timour: -bool ha_federated::create_where_from_key(String *to, KEY *key_info, - const byte *key, uint key_length) + ----------------- + - start_key: + * ">" -> HA_READ_AFTER_KEY + * ">=" -> HA_READ_KEY_OR_NEXT + * "=" -> HA_READ_KEY_EXACT + + - end_key: + * "<" -> HA_READ_BEFORE_KEY + * "<=" -> HA_READ_AFTER_KEY + + records_in_range: + ----------------- + - start_key: + * ">" -> HA_READ_AFTER_KEY + * ">=" -> HA_READ_KEY_EXACT + * "=" -> HA_READ_KEY_EXACT + + - end_key: + * "<" -> HA_READ_BEFORE_KEY + * "<=" -> HA_READ_AFTER_KEY + * "=" -> HA_READ_AFTER_KEY + +0 HA_READ_KEY_EXACT, Find first record else error +1 HA_READ_KEY_OR_NEXT, Record or next record +2 HA_READ_KEY_OR_PREV, Record or previous +3 HA_READ_AFTER_KEY, Find next rec. after key-record +4 HA_READ_BEFORE_KEY, Find next rec. before key-record +5 HA_READ_PREFIX, Key which as same prefix +6 HA_READ_PREFIX_LAST, Last key with the same prefix +7 HA_READ_PREFIX_LAST_OR_PREV, Last or prev key with the same prefix + +Flags that I've found: + +id, primary key, varchar + +id = 'ccccc' +records_in_range: start_key 0 end_key 3 +read_range_first: start_key 0 end_key NULL + +id > 'ccccc' +records_in_range: start_key 3 end_key NULL +read_range_first: start_key 3 end_key NULL + +id < 'ccccc' +records_in_range: start_key NULL end_key 4 +read_range_first: start_key NULL end_key 4 + +id <= 'ccccc' +records_in_range: start_key NULL end_key 3 +read_range_first: start_key NULL end_key 3 + +id >= 'ccccc' +records_in_range: start_key 0 end_key NULL +read_range_first: start_key 1 end_key NULL + +id like 'cc%cc' +records_in_range: start_key 0 end_key 3 +read_range_first: start_key 1 end_key 3 + +id > 'aaaaa' and id < 'ccccc' +records_in_range: start_key 3 end_key 4 +read_range_first: start_key 3 end_key 4 + +id >= 'aaaaa' and id < 'ccccc'; +records_in_range: start_key 0 end_key 4 +read_range_first: start_key 1 end_key 4 + +id >= 'aaaaa' and id <= 'ccccc'; +records_in_range: start_key 0 end_key 3 +read_range_first: start_key 1 end_key 3 + +id > 'aaaaa' and id <= 'ccccc'; +records_in_range: start_key 3 end_key 3 +read_range_first: start_key 3 end_key 3 + +numeric keys: + +id = 4 +index_read_idx: start_key 0 end_key NULL + +id > 4 +records_in_range: start_key 3 end_key NULL +read_range_first: start_key 3 end_key NULL + +id >= 4 +records_in_range: start_key 0 end_key NULL +read_range_first: start_key 1 end_key NULL + +id < 4 +records_in_range: start_key NULL end_key 4 +read_range_first: start_key NULL end_key 4 + +id <= 4 +records_in_range: start_key NULL end_key 3 +read_range_first: start_key NULL end_key 3 + +id like 4 +full table scan, select * from + +id > 2 and id < 8 +records_in_range: start_key 3 end_key 4 +read_range_first: start_key 3 end_key 4 + +id >= 2 and id < 8 +records_in_range: start_key 0 end_key 4 +read_range_first: start_key 1 end_key 4 + +id >= 2 and id <= 8 +records_in_range: start_key 0 end_key 3 +read_range_first: start_key 1 end_key 3 + +id > 2 and id <= 8 +records_in_range: start_key 3 end_key 3 +read_range_first: start_key 3 end_key 3 + +multi keys (id int, name varchar, other varchar) + +id = 1; +records_in_range: start_key 0 end_key 3 +read_range_first: start_key 0 end_key NULL + +id > 4; +id > 2 and name = '333'; remote: id > 2 +id > 2 and name > '333'; remote: id > 2 +id > 2 and name > '333' and other < 'ddd'; remote: id > 2 no results +id > 2 and name >= '333' and other < 'ddd'; remote: id > 2 1 result +id >= 4 and name = 'eric was here' and other > 'eeee'; +records_in_range: start_key 3 end_key NULL +read_range_first: start_key 3 end_key NULL + +id >= 4; +id >= 2 and name = '333' and other < 'ddd'; +remote: `id` >= 2 AND `name` >= '333'; +records_in_range: start_key 0 end_key NULL +read_range_first: start_key 1 end_key NULL + +id < 4; +id < 3 and name = '222' and other <= 'ccc'; remote: id < 3 +records_in_range: start_key NULL end_key 4 +read_range_first: start_key NULL end_key 4 + +id <= 4; +records_in_range: start_key NULL end_key 3 +read_range_first: start_key NULL end_key 3 + +id like 4; +full table scan + +id > 2 and id < 4; +records_in_range: start_key 3 end_key 4 +read_range_first: start_key 3 end_key 4 + +id >= 2 and id < 4; +records_in_range: start_key 0 end_key 4 +read_range_first: start_key 1 end_key 4 + +id >= 2 and id <= 4; +records_in_range: start_key 0 end_key 3 +read_range_first: start_key 1 end_key 3 + +id > 2 and id <= 4; +id = 6 and name = 'eric was here' and other > 'eeee'; +remote: (`id` > 6 AND `name` > 'eric was here' AND `other` > 'eeee') +AND (`id` <= 6) AND ( AND `name` <= 'eric was here') +no results +records_in_range: start_key 3 end_key 3 +read_range_first: start_key 3 end_key 3 + +Summary: + +* If the start key flag is 0 the max key flag shouldn't even be set, + and if it is, the query produced would be invalid. +* Multipart keys, even if containing some or all numeric columns, + are treated the same as non-numeric keys + + If the query is " = " (quotes or not): + - records in range start key flag HA_READ_KEY_EXACT, + end key flag HA_READ_AFTER_KEY (incorrect) + - any other: start key flag HA_READ_KEY_OR_NEXT, + end key flag HA_READ_AFTER_KEY (correct) + +* 'like' queries (of key) + - Numeric, full table scan + - Non-numeric + records_in_range: start_key 0 end_key 3 + other : start_key 1 end_key 3 + +* If the key flag is HA_READ_AFTER_KEY: + if start_key, append > + if end_key, append <= + +* If create_where_key was called by records_in_range: + + - if the key is numeric: + start key flag is 0 when end key is NULL, end key flag is 3 or 4 + - if create_where_key was called by any other function: + start key flag is 1 when end key is NULL, end key flag is 3 or 4 + - if the key is non-numeric, or multipart + When the query is an exact match, the start key flag is 0, + end key flag is 3 for what should be a no-range condition where + you should have 0 and max key NULL, which it is if called by + read_range_first + +Conclusion: + +1. Need logic to determin if a key is min or max when the flag is +HA_READ_AFTER_KEY, and handle appending correct operator accordingly + +2. Need a boolean flag to pass to create_where_from_key, used in the +switch statement. Add 1 to the flag if: + - start key flag is HA_READ_KEY_EXACT and the end key is NULL + +*/ + +bool ha_federated::create_where_from_key(String *to, + KEY *key_info, + const key_range *start_key, + const key_range *end_key, + bool records_in_range) { - uint second_loop= 0; - KEY_PART_INFO *key_part; - bool needs_quotes; - String tmp; + bool both_not_null= (start_key != NULL && end_key != NULL) ? TRUE : FALSE; + const byte *ptr; + uint remainder, length; + char tmpbuff[FEDERATED_QUERY_BUFFER_SIZE]; + String tmp(tmpbuff, sizeof(tmpbuff), system_charset_info); + const key_range *ranges[2]= { start_key, end_key }; DBUG_ENTER("ha_federated::create_where_from_key"); - for (key_part= key_info->key_part; (int) key_length > 0; key_part++) + tmp.length(0); + if (start_key == NULL && end_key == NULL) + DBUG_RETURN(1); + + for (int i= 0; i <= 1; i++) { - Field *field= key_part->field; - needs_quotes= field->needs_quotes(); - uint length= key_part->length; - - if (second_loop++ && to->append(" AND ", 5)) - DBUG_RETURN(1); - if (to->append('`') || to->append(field->field_name) || to->append("` ", 2)) - DBUG_RETURN(1); // Out of memory - - if (key_part->null_bit) - { - if (*key++) - { - if (to->append("IS NULL", 7)) - DBUG_RETURN(1); - - DBUG_PRINT("info", - ("NULL type %s", to->c_ptr_quick())); - key_length-= key_part->store_length; - key+= key_part->store_length - 1; - continue; - } - key_length--; - } - if (to->append("= ")) - DBUG_RETURN(1); - if (needs_quotes && to->append("'")) - DBUG_RETURN(1); - if (key_part->type == HA_KEYTYPE_BIT) - { - /* This is can be treated as a hex string */ - Field_bit *field= (Field_bit *) (key_part->field); - char buff[64 + 2], *ptr; - byte *end= (byte*)(key)+length; - - buff[0]= '0'; - buff[1]= 'x'; - for (ptr= buff + 2; key < end; key++) - { - uint tmp= (uint)(uchar) *key; - *ptr++= _dig_vec_upper[tmp >> 4]; - *ptr++= _dig_vec_upper[tmp & 15]; - } - if (to->append(buff, (uint)(ptr - buff))) - DBUG_RETURN(1); - - key_length-= length; + bool needs_quotes; + uint loop_counter= 0; + KEY_PART_INFO *key_part; + if (ranges[i] == NULL) continue; - } - if (key_part->key_part_flag & HA_BLOB_PART) - { - uint blob_length= uint2korr(key); - key+= HA_KEY_BLOB_LENGTH; - key_length-= HA_KEY_BLOB_LENGTH; + const byte *key= ranges[i]->key; + uint key_length= ranges[i]->length; - tmp.set_quick((char*) key, blob_length, &my_charset_bin); - if (append_escaped(to, &tmp)) - DBUG_RETURN(1); - - length= key_part->length; - } - else if (key_part->key_part_flag & HA_VAR_LENGTH_PART) + if (both_not_null) { - length= uint2korr(key); - key+= HA_KEY_BLOB_LENGTH; - tmp.set_quick((char*) key, length, &my_charset_bin); - if (append_escaped(to, &tmp)) - DBUG_RETURN(1); + if (i > 0) + tmp.append(") AND (", 7); + else + tmp.append("(", 1); } - else - { - char buff[MAX_FIELD_WIDTH]; - String str(buff, sizeof(buff), field->charset()), *res; - res= field->val_str(&str, (char*) (key)); - if (field->result_type() == STRING_RESULT) + for (key_part= key_info->key_part, + remainder= key_info->key_parts, + length= ranges[i]->length, + ptr= ranges[i]->key; ; + remainder--, + key_part++) + { + Field *field= key_part->field; + uint store_length= key_part->store_length; + uint part_length= min(store_length, length); + needs_quotes= field->needs_quotes(); + DBUG_DUMP("key, start of loop", (char *) ptr, length); + + if (key_part->null_bit) { - if (append_escaped(to, res)) - DBUG_RETURN(1); - res= field->val_str(&str, (char*) (key)); + if (*ptr++) + { + if (emit_key_part_name(&tmp, key_part) || + tmp.append(" IS NULL", 8)) + DBUG_RETURN(1); + continue; + } } - else if (to->append(res->ptr(), res->length())) + + if (tmp.append("(", 1)) DBUG_RETURN(1); + + switch(ranges[i]->flag) { + case(HA_READ_KEY_EXACT): + if (store_length >= length || + !needs_quotes || + key_part->type == HA_KEYTYPE_BIT || + field->result_type() != STRING_RESULT) + { + if (emit_key_part_name(&tmp, key_part)) + DBUG_RETURN(1); + + if (records_in_range) + { + if (tmp.append(" >= ", 4)) + DBUG_RETURN(1); + } + else + { + if (tmp.append(" = ", 3)) + DBUG_RETURN(1); + } + if (emit_key_part_element(&tmp, key_part, needs_quotes, 0, ptr, + part_length)) + DBUG_RETURN(1); + } + else + /* LIKE */ + { + if (emit_key_part_name(&tmp, key_part) || + tmp.append(" LIKE ", 6) || + emit_key_part_element(&tmp, key_part, needs_quotes, 1, ptr, + part_length)) + DBUG_RETURN(1); + } + break; + case(HA_READ_AFTER_KEY): + if (store_length >= length) /* end key */ + { + if (emit_key_part_name(&tmp, key_part)) + DBUG_RETURN(1); + + if (i > 0) /* end key */ + tmp.append(" <= ", 4); + else /* start key */ + tmp.append(" > ", 3); + + if (emit_key_part_element(&tmp, key_part, needs_quotes, 0, ptr, + part_length)) + DBUG_RETURN(1); + break; + } + case(HA_READ_KEY_OR_NEXT): + if ( emit_key_part_name(&tmp, key_part) || + tmp.append(" >= ", 4) || + emit_key_part_element(&tmp, key_part, needs_quotes, 0, ptr, + part_length)) + DBUG_RETURN(1); + break; + case(HA_READ_BEFORE_KEY): + if (store_length >= length) + { + if (emit_key_part_name(&tmp, key_part) || + tmp.append(" < ", 3) || + emit_key_part_element(&tmp, key_part, needs_quotes, 0, ptr, + part_length)) + DBUG_RETURN(1); + break; + } + case(HA_READ_KEY_OR_PREV): + if ( emit_key_part_name(&tmp, key_part) || + tmp.append(" <= ", 4) || + emit_key_part_element(&tmp, key_part, needs_quotes, 0, ptr, + part_length)) + DBUG_RETURN(1); + break; + default: + DBUG_PRINT("info",("cannot handle flag %d", ranges[i]->flag)); + DBUG_RETURN(1); + } + if (tmp.append(")", 1)) + DBUG_RETURN(1); + +next_loop: + if (store_length >= length) + break; + DBUG_PRINT("info", ("remainder %d", remainder)); + DBUG_ASSERT(remainder > 1); + length-= store_length; + ptr+= store_length; + if (tmp.append(" AND ", 5)) + DBUG_RETURN(1); + + DBUG_PRINT("info", + ("create_where_from_key WHERE clause: %s", + tmp.c_ptr_quick())); } - if (needs_quotes && to->append("'")) - DBUG_RETURN(1); - DBUG_PRINT("info", - ("final value for 'to' %s", to->c_ptr_quick())); - key+= length; - key_length-= length; - DBUG_RETURN(0); } - DBUG_RETURN(1); + if (both_not_null) + tmp.append(")", 1); + + if (to->append(" WHERE ", 7)) + DBUG_RETURN(1); + + if (to->append(tmp)) + DBUG_RETURN(1); + + DBUG_RETURN(0); } /* @@ -842,40 +1230,45 @@ bool ha_federated::create_where_from_key(String *to, KEY *key_info, static FEDERATED_SHARE *get_share(const char *table_name, TABLE *table) { - FEDERATED_SHARE *share; - char query_buffer[IO_SIZE]; + char *select_query, *tmp_table_name; + char query_buffer[FEDERATED_QUERY_BUFFER_SIZE]; + uint tmp_table_name_length; + Field **field; String query(query_buffer, sizeof(query_buffer), &my_charset_bin); + FEDERATED_SHARE *share; + /* + In order to use this string, we must first zero it's length, + or it will contain garbage + */ query.length(0); - uint table_name_length, table_base_name_length; - char *tmp_table_name, *table_base_name, *select_query; - - /* share->table_name has the file location - we want the table's name! */ - table_base_name= (char*) table->s->table_name; - DBUG_PRINT("info", ("table_name %s", table_base_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. - */ pthread_mutex_lock(&federated_mutex); - table_name_length= (uint) strlen(table_name); - table_base_name_length= (uint) strlen(table_base_name); + tmp_table_name= (char *)table->s->table_name; + tmp_table_name_length= (uint) strlen(tmp_table_name); if (!(share= (FEDERATED_SHARE *) hash_search(&federated_open_tables, (byte*) table_name, - table_name_length))) + strlen(table_name)))) { query.set_charset(system_charset_info); - query.append("SELECT * FROM `"); + query.append("SELECT ", 7); + for (field= table->field; *field; field++) + { + query.append("`", 1); + query.append((*field)->field_name); + query.append("`,", 2); + } + query.length(query.length()-1); + query.append(" FROM `", 7); + if (!(share= (FEDERATED_SHARE *) - my_multi_malloc(MYF(MY_WME | MY_ZEROFILL), + my_multi_malloc(MYF(MY_WME), &share, sizeof(*share), - &tmp_table_name, table_name_length + 1, - &select_query, query.length() + - strlen(table->s->comment) + 1, NullS))) + &tmp_table_name, tmp_table_name_length+ 1, + &select_query, + query.length()+strlen(table->s->comment)+1, + NullS))) { pthread_mutex_unlock(&federated_mutex); return NULL; @@ -884,16 +1277,15 @@ static FEDERATED_SHARE *get_share(const char *table_name, TABLE *table) if (parse_url(share, table, 0)) goto error; - query.append(share->table_base_name); - query.append("`"); - share->use_count= 0; - share->table_name_length= table_name_length; - share->table_name= tmp_table_name; + query.append(share->table_name, share->table_name_length); + query.append("`", 1); share->select_query= select_query; - strmov(share->table_name, table_name); strmov(share->select_query, query.ptr()); + share->use_count= 0; + share->table_name_length= strlen(share->table_name); DBUG_PRINT("info", ("share->select_query %s", share->select_query)); + if (my_hash_insert(&federated_open_tables, (byte*) share)) goto error; thr_lock_init(&share->lock); @@ -907,9 +1299,13 @@ static FEDERATED_SHARE *get_share(const char *table_name, TABLE *table) error: pthread_mutex_unlock(&federated_mutex); if (share->scheme) + { + DBUG_PRINT("info", + ("error: get_share. Freeing share->scheme %lx", + share->scheme)); my_free((gptr) share->scheme, MYF(0)); - my_free((gptr) share, MYF(0)); - + share->scheme= 0; + } return NULL; } @@ -922,12 +1318,19 @@ error: static int free_share(FEDERATED_SHARE *share) { + DBUG_ENTER("free_share"); pthread_mutex_lock(&federated_mutex); if (!--share->use_count) { if (share->scheme) + { + DBUG_PRINT("info", + ("free_share: Freeing share->scheme %lx", + share->scheme)); my_free((gptr) share->scheme, MYF(0)); + share->scheme= 0; + } hash_delete(&federated_open_tables, (byte*) share); thr_lock_delete(&share->lock); @@ -936,23 +1339,109 @@ static int free_share(FEDERATED_SHARE *share) } pthread_mutex_unlock(&federated_mutex); - return 0; + DBUG_RETURN(0); } +ha_rows ha_federated::records_in_range(uint inx, key_range *start_key, + key_range *end_key) +{ + /* + const byte *key; + uint length; + enum ha_rkey_function flag; + + Note: + + records_in_range should sent to the remote an explain + 'select * from table where ...' start_key and end_key + + The job of this is to give a reasonable estimate of how many rows + a query will produce, not precise, just general to help the optimiser + choose an optimal query plan - this function MUST be QUICK! + + USE Explain to get row estimate. Do not use count. + + Should this method even be here? We really want indexes to be used as often + as possible, therefore it seems we just need to hard-code the return value + to a very low number + +*/ +#ifdef CALC_RECORDS_IN_RANGE + char sql_query_buf[FEDERATED_QUERY_BUFFER_SIZE]; + int error= 0; + ha_rows return_rows= 20; + String sql_query(sql_query_buf, sizeof(sql_query_buf), &my_charset_bin); + MYSQL_RES *result= 0; + MYSQL_ROW row; +#endif + DBUG_ENTER("ha_federated::records_in_range"); +#ifdef CALC_RECORDS_IN_RANGE + if (start_key == NULL && end_key == NULL) + DBUG_RETURN(0); + + sql_query.length(0); + sql_query.append("EXPLAIN ", 8); + sql_query.append(share->select_query); + + create_where_from_key(&sql_query, + &table->key_info[inx], + start_key, + end_key, + 1); + + DBUG_PRINT("info",("inx %d sql_query %s", inx, sql_query.c_ptr_quick())); + if (mysql_real_query(mysql, sql_query.ptr(), sql_query.length())) + goto error; + sql_query.length(0); + + result= mysql_store_result(mysql); + if (! result) + goto error; + + if (! mysql_num_rows(result)) + goto error; + if (!(row= mysql_fetch_row(result))) + goto error; + + if (! row[8]) + return_rows=0; + else + return_rows= (ha_rows) my_strtoll10(row[8], (char**) 0, &error); + + if (result) + { + mysql_free_result(result); + result= 0; + } + +error: + if (result) + { + mysql_free_result(result); + result= 0; + } + + DBUG_PRINT("info", ("return_rows (records) %d", return_rows)); + DBUG_RETURN(return_rows); +#else + DBUG_RETURN(FEDERATED_RECORDS_IN_RANGE); +#endif +} /* 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. */ -static const char *ha_federated_exts[] = { - NullS -}; const char **ha_federated::bas_ext() const { - return ha_federated_exts; + static const char *ext[]= + { + NullS + }; + return ext; } @@ -969,6 +1458,7 @@ const char **ha_federated::bas_ext() const int ha_federated::open(const char *name, int mode, uint test_if_locked) { + int rc; DBUG_ENTER("ha_federated::open"); if (!(share= get_share(name, table))) @@ -990,8 +1480,13 @@ int ha_federated::open(const char *name, int mode, uint test_if_locked) share->port, share->socket, 0)) { - my_error(ER_CONNECT_TO_MASTER, MYF(0), mysql_error(mysql)); - DBUG_RETURN(ER_CONNECT_TO_MASTER); + int error_code; + char error_buffer[FEDERATED_QUERY_BUFFER_SIZE]; + error_code= ER_CONNECT_TO_FOREIGN_DATA_SRC; + my_sprintf(error_buffer, (error_buffer, ": %d : %s", + mysql_errno(mysql), mysql_error(mysql))); + my_error(error_code, MYF(0), error_buffer); + DBUG_RETURN(error_code); } /* Since we do not support transactions at this version, we can let the client @@ -1016,6 +1511,7 @@ int ha_federated::open(const char *name, int mode, uint test_if_locked) int ha_federated::close(void) { + int retval; DBUG_ENTER("ha_federated::close"); /* free the result set */ @@ -1028,7 +1524,8 @@ int ha_federated::close(void) } /* Disconnect from mysql */ mysql_close(mysql); - DBUG_RETURN(free_share(share)); + retval= free_share(share); + DBUG_RETURN(retval); } @@ -1085,30 +1582,34 @@ inline uint field_in_record_is_null(TABLE *table, int ha_federated::write_row(byte *buf) { - uint x= 0, num_fields= 0; - Field **field; - query_id_t current_query_id= 1; - ulong tmp_query_id= 1; + bool num_fields; + int error_code; uint all_fields_have_same_query_id= 1; - - char insert_buffer[IO_SIZE]; - char values_buffer[IO_SIZE], insert_field_value_buffer[IO_SIZE]; + ulong current_query_id= 1; + ulong tmp_query_id= 1; + char error_buffer[FEDERATED_QUERY_BUFFER_SIZE]; + char insert_buffer[FEDERATED_QUERY_BUFFER_SIZE]; + char values_buffer[FEDERATED_QUERY_BUFFER_SIZE]; + char insert_field_value_buffer[STRING_BUFFER_USUAL_SIZE]; + Field **field; /* The main insert query string */ String insert_string(insert_buffer, sizeof(insert_buffer), &my_charset_bin); - insert_string.length(0); /* The string containing the values to be added to the insert */ String values_string(values_buffer, sizeof(values_buffer), &my_charset_bin); - values_string.length(0); /* The actual value of the field, to be added to the values_string */ String insert_field_value_string(insert_field_value_buffer, sizeof(insert_field_value_buffer), &my_charset_bin); + values_string.length(0); + insert_string.length(0); insert_field_value_string.length(0); DBUG_ENTER("ha_federated::write_row"); - DBUG_PRINT("info", ("table charset name %s csname %s", - table->s->table_charset->name, table->s->table_charset->csname)); + DBUG_PRINT("info", + ("table charset name %s csname %s", + table->s->table_charset->name, + table->s->table_charset->csname)); statistic_increment(table->in_use->status_var.ha_write_count, &LOCK_status); if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT) @@ -1123,21 +1624,22 @@ int ha_federated::write_row(byte *buf) DBUG_PRINT("info", ("current query id %d", current_query_id)); /* start off our string */ - insert_string.append("INSERT INTO `"); - insert_string.append(share->table_base_name); - insert_string.append("`"); + insert_string.append("INSERT INTO `", 13); + insert_string.append(share->table_name, share->table_name_length); + insert_string.append("`",1); /* start both our field and field values strings */ - insert_string.append(" ("); - values_string.append(" VALUES ("); + insert_string.append(" (", 2); + values_string.append(" VALUES (", 9); /* Even if one field is different, all_fields_same_query_id can't remain 0 if it remains 0, then that means no fields were specified in the query such as in the case of INSERT INTO table VALUES (val1, val2, valN) + */ - for (field= table->field; *field; field++, x++) + for (field= table->field; *field; field++) { - if (x > 0 && tmp_query_id != (*field)->query_id) + if (field > table->field && tmp_query_id != (*field)->query_id) all_fields_have_same_query_id= 0; tmp_query_id= (*field)->query_id; @@ -1145,31 +1647,30 @@ int ha_federated::write_row(byte *buf) /* loop through the field pointer array, add any fields to both the values list and the fields list that match the current query id + + You might ask "Why an index variable (num_fields) ?" My answer is that + we need to count how many fields we actually need */ - x=0; - for (field= table->field; *field; field++, x++) + for (field= table->field; *field; field++) { /* if there is a query id and if it's equal to the current query id */ if (((*field)->query_id && (*field)->query_id == current_query_id) || all_fields_have_same_query_id) { - num_fields++; + /* + There are some fields. This will be used later to determine + whether to chop off commas and parens. + */ + num_fields= TRUE; if ((*field)->is_null()) - { - DBUG_PRINT("info", - ("column %d current query id %d field is_null query id %d", - x, current_query_id, (*field)->query_id)); - insert_field_value_string.append("NULL"); - } + insert_field_value_string.append("NULL", 4); else { - DBUG_PRINT("info", - ("column %d current query id %d field is not null query ID %d", - x, current_query_id, (*field)->query_id)); (*field)->val_str(&insert_field_value_string); /* quote these fields if they require it */ - (*field)->quote_data(&insert_field_value_string); } + (*field)->quote_data(&insert_field_value_string); + } /* append the field name */ insert_string.append((*field)->field_name); @@ -1178,8 +1679,15 @@ int ha_federated::write_row(byte *buf) insert_field_value_string.length(0); /* append commas between both fields and fieldnames */ - insert_string.append(','); - values_string.append(','); + /* + unfortunately, we can't use the logic + if *(fields + 1) to make the following + appends conditional because we may not append + if the next field doesn't match the condition: + (((*field)->query_id && (*field)->query_id == current_query_id) + */ + insert_string.append(",",1); + values_string.append(",",1); } } @@ -1197,15 +1705,14 @@ int ha_federated::write_row(byte *buf) AND, we don't want to chop off the last char '(' insert will be "INSERT INTO t1 VALUES ();" */ - DBUG_PRINT("info", ("x %d num fields %d", x, num_fields)); - if (num_fields > 0) + if (num_fields) { /* chops off leading commas */ values_string.chop(); - insert_string.append(')'); + insert_string.append(")", 1); } /* we always want to append this, even if there aren't any fields */ - values_string.append(')'); + values_string.append(")", 1); /* add the values */ insert_string.append(values_string); @@ -1214,8 +1721,11 @@ int ha_federated::write_row(byte *buf) if (mysql_real_query(mysql, insert_string.ptr(), insert_string.length())) { - my_error(ER_QUERY_ON_MASTER, MYF(0), mysql_error(mysql)); - DBUG_RETURN(ER_QUERY_ON_MASTER); + error_code= ER_QUERY_ON_FOREIGN_DATA_SRC; + my_sprintf(error_buffer, (error_buffer, ": %d : %s", + mysql_errno(mysql), mysql_error(mysql))); + my_error(error_code, MYF(0), error_buffer); + DBUG_RETURN(error_code); } DBUG_RETURN(0); @@ -1241,39 +1751,56 @@ int ha_federated::write_row(byte *buf) int ha_federated::update_row(const byte *old_data, byte *new_data) { - uint x= 0; - uint has_a_primary_key= 0; - uint primary_key_field_num; - char old_field_value_buffer[IO_SIZE], new_field_value_buffer[IO_SIZE]; - char update_buffer[IO_SIZE], where_buffer[IO_SIZE]; + /* + This used to control how the query was built. If there was a primary key, + the query would be built such that there was a where clause with only + that column as the condition. This is flawed, because if we have a multi-part + primary key, it would only use the first part! We don't need to do this anyway, + because read_range_first will retrieve the correct record, which is what is used + to build the WHERE clause. We can however use this to append a LIMIT to the end + if there is NOT a primary key. Why do this? Because we only are updating one + record, and LIMIT enforces this. + */ + bool has_a_primary_key= (table->s->primary_key == 0 ? TRUE : FALSE); + /* + buffers for following strings + */ + char error_buffer[FEDERATED_QUERY_BUFFER_SIZE]; + char old_field_value_buffer[STRING_BUFFER_USUAL_SIZE]; + char new_field_value_buffer[STRING_BUFFER_USUAL_SIZE]; + char update_buffer[FEDERATED_QUERY_BUFFER_SIZE]; + char where_buffer[FEDERATED_QUERY_BUFFER_SIZE]; /* stores the value to be replaced of the field were are updating */ - String old_field_value(old_field_value_buffer, sizeof(old_field_value_buffer), + String old_field_value(old_field_value_buffer, + sizeof(old_field_value_buffer), &my_charset_bin); /* stores the new value of the field */ - String new_field_value(new_field_value_buffer, sizeof(new_field_value_buffer), + String new_field_value(new_field_value_buffer, + sizeof(new_field_value_buffer), &my_charset_bin); /* stores the update query */ - String update_string(update_buffer, sizeof(update_buffer), &my_charset_bin); + String update_string(update_buffer, + sizeof(update_buffer), + &my_charset_bin); /* stores the WHERE clause */ - String where_string(where_buffer, sizeof(where_buffer), &my_charset_bin); + String where_string(where_buffer, + sizeof(where_buffer), + &my_charset_bin); DBUG_ENTER("ha_federated::update_row"); + /* + set string lengths to 0 to avoid misc chars in string + */ old_field_value.length(0); new_field_value.length(0); update_string.length(0); where_string.length(0); - has_a_primary_key= (table->s->primary_key == 0 ? 1 : 0); - primary_key_field_num= has_a_primary_key ? - table->key_info[table->s->primary_key].key_part->fieldnr - 1 : -1; - if (has_a_primary_key) - DBUG_PRINT("info", ("has a primary key")); - - update_string.append("UPDATE `"); - update_string.append(share->table_base_name); - update_string.append("`"); - update_string.append(" SET "); + update_string.append("UPDATE `", 8); + update_string.append(share->table_name); + update_string.append("`", 1); + update_string.append(" SET ", 5); /* In this loop, we want to match column names to values being inserted @@ -1285,104 +1812,73 @@ int ha_federated::update_row(const byte *old_data, byte *new_data) field=oldvalue */ - for (Field **field= table->field; *field; field++, x++) + for (Field **field= table->field; *field; field++) { - /* - In all of these tests for 'has_a_primary_key', what I'm trying to - accomplish is to only use the primary key in the WHERE clause if the - table has a primary key, as opposed to a table without a primary key - in which case we have to use all the fields to create a WHERE clause - using the old/current values, as well as adding a LIMIT statement - */ - if (has_a_primary_key) - { - if (x == primary_key_field_num) - where_string.append((*field)->field_name); - } - else - where_string.append((*field)->field_name); - + where_string.append((*field)->field_name); update_string.append((*field)->field_name); - update_string.append('='); + update_string.append(" = ", 3); if ((*field)->is_null()) - { - DBUG_PRINT("info", ("column %d is NULL", x )); - new_field_value.append("NULL"); - } + new_field_value.append("NULL", 4); else { /* otherwise = */ (*field)->val_str(&new_field_value); (*field)->quote_data(&new_field_value); - if (has_a_primary_key) - { - if (x == primary_key_field_num) - where_string.append("="); - } - else if (!field_in_record_is_null(table, *field, (char*) old_data)) - where_string.append("="); + if (!field_in_record_is_null(table, *field, (char*) old_data)) + where_string.append(" = ", 3); } - if (has_a_primary_key) - { - if (x == primary_key_field_num) - { - (*field)->val_str(&old_field_value, - (char*) (old_data + (*field)->offset())); - (*field)->quote_data(&old_field_value); - where_string.append(old_field_value); - } - } + if (field_in_record_is_null(table, *field, (char*) old_data)) + where_string.append(" IS NULL ", 9); else { - if (field_in_record_is_null(table, *field, (char*) old_data)) - where_string.append(" IS NULL "); - else - { - uint o_len; - (*field)->val_str(&old_field_value, - (char*) (old_data + (*field)->offset())); - o_len= (*field)->pack_length(); - DBUG_PRINT("info", ("o_len %lu", o_len)); - (*field)->quote_data(&old_field_value); - where_string.append(old_field_value); - } + uint o_len; + (*field)->val_str(&old_field_value, + (char*) (old_data + (*field)->offset())); + o_len= (*field)->pack_length(); + (*field)->quote_data(&old_field_value); + where_string.append(old_field_value); } - DBUG_PRINT("info", - ("column %d new value %s old value %s", - x, new_field_value.c_ptr_quick(), old_field_value.c_ptr_quick() )); + update_string.append(new_field_value); new_field_value.length(0); - if (x + 1 < table->s->fields) + /* + Only append conjunctions if we have another field in which + to iterate + */ + if (*(field + 1)) { - update_string.append(", "); - if (!has_a_primary_key) - where_string.append(" AND "); + update_string.append(", ", 2); + where_string.append(" AND ", 5); } old_field_value.length(0); } - update_string.append(" WHERE "); + update_string.append(" WHERE ", 7); update_string.append(where_string); + /* + If this table has not a primary key, then we could possibly + update multiple rows. We want to make sure to only update one! + */ if (! has_a_primary_key) - update_string.append(" LIMIT 1"); + update_string.append(" LIMIT 1", 8); - DBUG_PRINT("info", ("Final update query: %s", - update_string.c_ptr_quick())); if (mysql_real_query(mysql, update_string.ptr(), update_string.length())) { - my_error(ER_QUERY_ON_MASTER, MYF(0), mysql_error(mysql)); - DBUG_RETURN(ER_QUERY_ON_MASTER); + int error_code= ER_QUERY_ON_FOREIGN_DATA_SRC; + char error_buffer[FEDERATED_QUERY_BUFFER_SIZE]; + my_sprintf(error_buffer, (error_buffer, ": %d : %s", + mysql_errno(mysql), mysql_error(mysql))); + my_error(error_code, MYF(0), error_buffer); + DBUG_RETURN(error_code); } - - DBUG_RETURN(0); } /* - This will delete a row. 'buf' will contain a copy of the row to be deleted. + 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 @@ -1398,49 +1894,59 @@ int ha_federated::update_row(const byte *old_data, byte *new_data) int ha_federated::delete_row(const byte *buf) { - char delete_buffer[IO_SIZE]; - char data_buffer[IO_SIZE]; + char error_buffer[FEDERATED_QUERY_BUFFER_SIZE]; + char delete_buffer[FEDERATED_QUERY_BUFFER_SIZE]; + char data_buffer[FEDERATED_QUERY_BUFFER_SIZE]; + String delete_string(delete_buffer, sizeof(delete_buffer), &my_charset_bin); String data_string(data_buffer, sizeof(data_buffer), &my_charset_bin); + delete_string.length(0); + data_string.length(0); + DBUG_ENTER("ha_federated::delete_row"); - delete_string.length(0); - delete_string.append("DELETE FROM `"); - delete_string.append(share->table_base_name); - delete_string.append("`"); - delete_string.append(" WHERE "); + delete_string.append("DELETE FROM `", 13); + delete_string.append(share->table_name); + delete_string.append("`", 1); + delete_string.append(" WHERE ", 7); for (Field **field= table->field; *field; field++) { - Field *cur_field= *field; - data_string.length(0); - delete_string.append(cur_field->field_name); + delete_string.append((*field)->field_name); - if (cur_field->is_null_in_record((const uchar*) buf)) + if ((*field)->is_null()) { - delete_string.append(" IS "); - data_string.append("NULL"); + delete_string.append(" IS ", 4); + data_string.append("NULL", 4); } else { - delete_string.append("="); - cur_field->val_str(&data_string, (char*) buf+ cur_field->offset()); - cur_field->quote_data(&data_string); + delete_string.append(" = ", 3); + (*field)->val_str(&data_string); + (*field)->quote_data(&data_string); } delete_string.append(data_string); - delete_string.append(" AND "); + data_string.length(0); + + if (*(field + 1)) + delete_string.append(" AND ", 5); } - delete_string.length(delete_string.length()-5); // Remove AND - delete_string.append(" LIMIT 1"); + delete_string.append(" LIMIT 1", 8); DBUG_PRINT("info", ("Delete sql: %s", delete_string.c_ptr_quick())); if (mysql_real_query(mysql, delete_string.ptr(), delete_string.length())) { - my_error(ER_QUERY_ON_MASTER, MYF(0), mysql_error(mysql)); - DBUG_RETURN(ER_QUERY_ON_MASTER); + int error_code= ER_QUERY_ON_FOREIGN_DATA_SRC; + char error_buffer[FEDERATED_QUERY_BUFFER_SIZE]; + my_error(error_code, MYF(0), error_buffer); + DBUG_RETURN(error_code); } + deleted+= mysql->affected_rows; + DBUG_PRINT("info", + ("rows deleted %d rows deleted for all time %d", + int(mysql->affected_rows), deleted)); DBUG_RETURN(0); } @@ -1454,12 +1960,12 @@ int ha_federated::delete_row(const byte *buf) */ int ha_federated::index_read(byte *buf, const byte *key, - uint key_len __attribute__ ((unused)), - enum ha_rkey_function find_flag - __attribute__ ((unused))) + uint key_len, enum ha_rkey_function find_flag) { + int retval; DBUG_ENTER("ha_federated::index_read"); - DBUG_RETURN(index_read_idx(buf, active_index, key, key_len, find_flag)); + retval= index_read_idx(buf, active_index, key, key_len, find_flag); + DBUG_RETURN(retval); } @@ -1473,17 +1979,23 @@ int ha_federated::index_read(byte *buf, const byte *key, */ int ha_federated::index_read_idx(byte *buf, uint index, const byte *key, - uint key_len __attribute__ ((unused)), - enum ha_rkey_function find_flag - __attribute__ ((unused))) + uint key_len, enum ha_rkey_function find_flag) { - char index_value[IO_SIZE]; - String index_string(index_value, sizeof(index_value), &my_charset_bin); - index_string.length(0); - uint keylen; + int retval; + char error_buffer[FEDERATED_QUERY_BUFFER_SIZE]; + char index_value[STRING_BUFFER_USUAL_SIZE]; + char key_value[STRING_BUFFER_USUAL_SIZE]; + char test_value[STRING_BUFFER_USUAL_SIZE]; + char sql_query_buffer[FEDERATED_QUERY_BUFFER_SIZE]; + String index_string(index_value, + sizeof(index_value), + &my_charset_bin); + String sql_query(sql_query_buffer, + sizeof(sql_query_buffer), + &my_charset_bin); + key_range range; - char sql_query_buffer[IO_SIZE]; - String sql_query(sql_query_buffer, sizeof(sql_query_buffer), &my_charset_bin); + index_string.length(0); sql_query.length(0); DBUG_ENTER("ha_federated::index_read_idx"); @@ -1492,10 +2004,14 @@ int ha_federated::index_read_idx(byte *buf, uint index, const byte *key, &LOCK_status); sql_query.append(share->select_query); - sql_query.append(" WHERE "); - keylen= strlen((char*) (key)); - create_where_from_key(&index_string, &table->key_info[index], key, keylen); + range.key= key; + range.length= key_len; + range.flag= find_flag; + create_where_from_key(&index_string, + &table->key_info[index], + &range, + NULL, NULL); sql_query.append(index_string); DBUG_PRINT("info", @@ -1514,35 +2030,43 @@ int ha_federated::index_read_idx(byte *buf, uint index, const byte *key, } if (mysql_real_query(mysql, sql_query.ptr(), sql_query.length())) { - my_error(ER_QUERY_ON_MASTER, MYF(0), mysql_error(mysql)); - DBUG_RETURN(ER_QUERY_ON_MASTER); + my_sprintf(error_buffer, (error_buffer, ": %d : %s", + mysql_errno(mysql), mysql_error(mysql))); + retval= ER_QUERY_ON_FOREIGN_DATA_SRC; + goto error; } result= mysql_store_result(mysql); if (!result) { - table->status= STATUS_NOT_FOUND; - DBUG_RETURN(HA_ERR_END_OF_FILE); + retval= HA_ERR_END_OF_FILE; + goto error; } - - if (mysql_errno(mysql)) - { - table->status= STATUS_NOT_FOUND; - DBUG_RETURN(mysql_errno(mysql)); - } - /* - This basically says that the record in table->record[0] is legal, and that it is - ok to use this record, for whatever reason, such as with a join (without it, joins - will not work) - */ + /* + This basically says that the record in table->record[0] is legal, + and that it is ok to use this record, for whatever reason, such + as with a join (without it, joins will not work) + */ table->status=0; - DBUG_RETURN(rnd_next(buf)); + retval= rnd_next(buf); + DBUG_RETURN(retval); + +error: + if (result) + { + mysql_free_result(result); + result= 0; + } + table->status= STATUS_NOT_FOUND; + my_error(retval, MYF(0), error_buffer); + DBUG_RETURN(retval); } /* Initialized at each key walk (called multiple times unlike rnd_init()) */ int ha_federated::index_init(uint keynr) { + int error; DBUG_ENTER("ha_federated::index_init"); DBUG_PRINT("info", ("table: '%s' key: %d", table->s->table_name, keynr)); @@ -1550,14 +2074,90 @@ int ha_federated::index_init(uint keynr) DBUG_RETURN(0); } -/* Used to read forward through the index. */ -int ha_federated::index_next(byte *buf) +/* + + int read_range_first(const key_range *start_key, + const key_range *end_key, + bool eq_range, bool sorted); +*/ +int ha_federated::read_range_first(const key_range *start_key, + const key_range *end_key, + bool eq_range, bool sorted) { - DBUG_ENTER("ha_federated::index_next"); - DBUG_RETURN(rnd_next(buf)); + char sql_query_buffer[FEDERATED_QUERY_BUFFER_SIZE]; + int retval; + String sql_query(sql_query_buffer, + sizeof(sql_query_buffer), + &my_charset_bin); + + DBUG_ENTER("ha_federated::read_range_first"); + if (start_key == NULL && end_key == NULL) + DBUG_RETURN(0); + + sql_query.length(0); + sql_query.append(share->select_query); + create_where_from_key(&sql_query, + &table->key_info[active_index], + start_key, end_key, NULL); + + if (mysql_real_query(mysql, sql_query.ptr(), sql_query.length())) + { + retval= ER_QUERY_ON_FOREIGN_DATA_SRC; + goto error; + } + sql_query.length(0); + + if (result) + { + DBUG_PRINT("info", + ("mysql_free_result address %lx", result)); + mysql_free_result(result); + result= 0; + } + result= mysql_store_result(mysql); + + if (!result) + { + retval= HA_ERR_END_OF_FILE; + goto error; + } + + /* This was successful, please let it be known! */ + table->status=0; + + retval= rnd_next(table->record[0]); + DBUG_RETURN(retval); + +error: + table->status= STATUS_NOT_FOUND; + if (result) + { + DBUG_PRINT("info", ("mysql_free_result address %lx", result)); + mysql_free_result(result); + result= 0; + } + DBUG_RETURN(retval); +} + +int ha_federated::read_range_next() +{ + int retval; + DBUG_ENTER("ha_federated::read_range_next"); + retval= rnd_next(table->record[0]); + DBUG_RETURN(retval); } +/* Used to read forward through the index. */ +int ha_federated::index_next(byte *buf) +{ + int retval; + DBUG_ENTER("ha_federated::index_next"); + statistic_increment(table->in_use->status_var.ha_read_next_count, + &LOCK_status); + retval= rnd_next(buf); + DBUG_RETURN(retval); +} /* rnd_init() is called when the system wants the storage engine to do a table scan. @@ -1573,12 +2173,15 @@ int ha_federated::index_next(byte *buf) int ha_federated::rnd_init(bool scan) { - DBUG_ENTER("ha_federated::rnd_init"); + int num_fields, rows; + int retval; + char error_buffer[FEDERATED_QUERY_BUFFER_SIZE]; + DBUG_ENTER("ha_federated::rnd_init"); /* - This 'scan' flag is incredibly important for this handler to work - properly, especially with updates containing WHERE clauses using - indexed columns. + The use of the 'scan' flag is incredibly important for this handler + to work properly, especially with updates containing WHERE clauses + using indexed columns. When the initial query contains a WHERE clause of the query using an indexed column, it's index_read_idx that selects the exact record from @@ -1621,32 +2224,40 @@ int ha_federated::rnd_init(bool scan) result= 0; } - if (mysql_real_query - (mysql, share->select_query, strlen(share->select_query))) - { - my_error(ER_QUERY_ON_MASTER, MYF(0), mysql_error(mysql)); - DBUG_RETURN(ER_QUERY_ON_MASTER); - } - result= mysql_store_result(mysql); + if (mysql_real_query(mysql, + share->select_query, + strlen(share->select_query))) + goto error; - if (mysql_errno(mysql)) - DBUG_RETURN(mysql_errno(mysql)); + result= mysql_store_result(mysql); + if (!result) + goto error; } DBUG_RETURN(0); + +error: + retval= ER_QUERY_ON_FOREIGN_DATA_SRC; + my_sprintf(error_buffer, (error_buffer, ": %d : %s", + mysql_errno(mysql), mysql_error(mysql))); + my_error(retval, MYF(0), error_buffer); + DBUG_PRINT("info", + ("return error code %d", retval)); + DBUG_RETURN(retval); } int ha_federated::rnd_end() { + int retval; DBUG_ENTER("ha_federated::rnd_end"); + if (result) { DBUG_PRINT("info", ("mysql_free_result address %lx", result)); mysql_free_result(result); result= 0; } - - mysql_free_result(result); - DBUG_RETURN(index_end()); + retval= index_end(); + DBUG_RETURN(retval); } int ha_federated::index_end(void) @@ -1668,6 +2279,7 @@ int ha_federated::index_end(void) int ha_federated::rnd_next(byte *buf) { + int retval; MYSQL_ROW row; DBUG_ENTER("ha_federated::rnd_next"); @@ -1687,7 +2299,8 @@ int ha_federated::rnd_next(byte *buf) if (!(row= mysql_fetch_row(result))) DBUG_RETURN(HA_ERR_END_OF_FILE); - DBUG_RETURN(convert_row_to_internal_format(buf, row)); + retval= convert_row_to_internal_format(buf, row); + DBUG_RETURN(retval); } @@ -1734,13 +2347,15 @@ int ha_federated::rnd_pos(byte *buf, byte *pos) */ if (scan_flag) { + int retval; statistic_increment(table->in_use->status_var.ha_read_rnd_count, &LOCK_status); memcpy_fixed(¤t_position, pos, sizeof(MYSQL_ROW_OFFSET)); // pos /* is not aligned */ result->current_row= 0; result->data_cursor= current_position; - DBUG_RETURN(rnd_next(buf)); + retval= rnd_next(buf); + DBUG_RETURN(retval); } DBUG_RETURN(0); } @@ -1789,12 +2404,97 @@ int ha_federated::rnd_pos(byte *buf, byte *pos) sql_update.cc */ -/* FIX: later version provide better information to the optimizer */ void ha_federated::info(uint flag) { + char error_buffer[FEDERATED_QUERY_BUFFER_SIZE]; + char status_buf[FEDERATED_QUERY_BUFFER_SIZE]; + char escaped_table_name[FEDERATED_QUERY_BUFFER_SIZE]; + int error; + uint error_code; + MYSQL_RES *result=0; + MYSQL_ROW row; + String status_query_string(status_buf, sizeof(status_buf), &my_charset_bin); + DBUG_ENTER("ha_federated::info"); - records= 10000; // fix later + + error_code= ER_QUERY_ON_FOREIGN_DATA_SRC; + /* we want not to show table status if not needed to do so */ + if (flag & (HA_STATUS_VARIABLE | HA_STATUS_CONST)) + { + status_query_string.length(0); + status_query_string.append("SHOW TABLE STATUS LIKE '", 24); + + escape_string_for_mysql(&my_charset_bin, (char *)escaped_table_name, + sizeof(escaped_table_name), + share->table_name, + share->table_name_length); + status_query_string.append(escaped_table_name); + status_query_string.append("'"); + + if (mysql_real_query(mysql, status_query_string.ptr(), + status_query_string.length())) + goto error; + + status_query_string.length(0); + + result= mysql_store_result(mysql); + if (! result) + goto error; + + if (! mysql_num_rows(result)) + goto error; + + if (!(row= mysql_fetch_row(result))) + goto error; + + if (flag & HA_STATUS_VARIABLE | HA_STATUS_CONST) + { + /* + deleted is set in ha_federated::info + */ + /* + need to figure out what this means as far as federated is concerned, + since we don't have a "file" + + data_file_length = ? + index_file_length = ? + delete_length = ? + */ + if (row[4] != NULL) + records= (ha_rows) my_strtoll10(row[4], (char**) 0, &error); + if (row[5] != NULL) + mean_rec_length= (ha_rows) my_strtoll10(row[5], (char**) 0, &error); + if (row[12] != NULL) + update_time= (ha_rows) my_strtoll10(row[12], (char**) 0, &error); + if (row[13] != NULL) + check_time= (ha_rows) my_strtoll10(row[13], (char**) 0, &error); + } + if (flag & HA_STATUS_CONST) + { + TABLE_SHARE *share= table->s; + block_size= 4096; + } + } + + if (result) + { + mysql_free_result(result); + result= 0; + } + DBUG_VOID_RETURN; + +error: + if (result) + { + mysql_free_result(result); + result= 0; + } + my_sprintf(error_buffer, + (error_buffer, + ": %d : %s", + mysql_errno(mysql), mysql_error(mysql))); + my_error(error_code, MYF(0), error_buffer); DBUG_VOID_RETURN; } @@ -1815,22 +2515,31 @@ int ha_federated::delete_all_rows() { DBUG_ENTER("ha_federated::delete_all_rows"); - char query_buffer[IO_SIZE]; + char query_buffer[FEDERATED_QUERY_BUFFER_SIZE]; String query(query_buffer, sizeof(query_buffer), &my_charset_bin); query.length(0); query.set_charset(system_charset_info); - query.append("TRUNCATE `"); - query.append(share->table_base_name); - query.append("`"); + query.append("TRUNCATE `", 10); + query.append(share->table_name); + query.append("`", 1); + /* + TRUNCATE won't return anything in mysql_affected_rows + */ + deleted+= records; if (mysql_real_query(mysql, query.ptr(), query.length())) { - my_error(ER_QUERY_ON_MASTER, MYF(0), mysql_error(mysql)); - DBUG_RETURN(ER_QUERY_ON_MASTER); + int error_code= ER_QUERY_ON_FOREIGN_DATA_SRC; + char error_buffer[FEDERATED_QUERY_BUFFER_SIZE]; + my_sprintf(error_buffer, + (error_buffer, + ": %d : %s", + mysql_errno(mysql), mysql_error(mysql))); + my_error(error_code, MYF(0), error_buffer); + DBUG_RETURN(error_code); } - - DBUG_RETURN(HA_ERR_WRONG_COMMAND); + DBUG_RETURN(0); } @@ -1878,7 +2587,7 @@ THR_LOCK_DATA **ha_federated::store_lock(THD *thd, */ if ((lock_type >= TL_WRITE_CONCURRENT_INSERT && - lock_type <= TL_WRITE) && !thd->in_lock_tables && !thd->tablespace_op) + lock_type <= TL_WRITE) && !thd->in_lock_tables) lock_type= TL_WRITE_ALLOW_WRITE; /* @@ -1908,28 +2617,41 @@ THR_LOCK_DATA **ha_federated::store_lock(THD *thd, int ha_federated::create(const char *name, TABLE *table_arg, HA_CREATE_INFO *create_info) { - int connection_error=0; - FEDERATED_SHARE tmp; + int retval= 0; + /* + only a temporary share, to test the url + */ + FEDERATED_SHARE tmp_share; DBUG_ENTER("ha_federated::create"); - if (parse_url(&tmp, table_arg, 1)) - { - my_error(ER_CANT_CREATE_TABLE, MYF(0), name, 1); + if ((retval= parse_url(&tmp_share, table_arg, 1))) goto error; - } - if ((connection_error= check_foreign_data_source(&tmp))) - { - my_error(connection_error, MYF(0), name, 1); + + if ((retval= check_foreign_data_source(&tmp_share, 1))) goto error; + + if (tmp_share.scheme) + { + DBUG_PRINT("info", + ("ha_federated::create. Freeing tmp_share.scheme %lx", + tmp_share.scheme)); + my_free((gptr) tmp_share.scheme, MYF(0)); + tmp_share.scheme= 0; } - - my_free((gptr) tmp.scheme, MYF(0)); - DBUG_RETURN(0); - + DBUG_PRINT("info", ("no errors, returning %d", retval)); + DBUG_RETURN(retval); + error: - DBUG_PRINT("info", ("errors, returning %d", ER_CANT_CREATE_TABLE)); - my_free((gptr) tmp.scheme, MYF(0)); - DBUG_RETURN(ER_CANT_CREATE_TABLE); + if (tmp_share.scheme) + { + DBUG_PRINT("info", + ("error in ha_federated::create. Freeing tmp_share.scheme %lx", + tmp_share.scheme)); + my_free((gptr) tmp_share.scheme, MYF(0)); + tmp_share.scheme= 0; + } + DBUG_PRINT("info", ("errors, returning %d", retval)); + DBUG_RETURN(retval); } #endif /* HAVE_FEDERATED_DB */ diff --git a/sql/ha_federated.h b/sql/ha_federated.h index f084976718c..e8bebe81768 100644 --- a/sql/ha_federated.h +++ b/sql/ha_federated.h @@ -26,16 +26,16 @@ #endif #include -//#include + +#define FEDERATED_QUERY_BUFFER_SIZE STRING_BUFFER_USUAL_SIZE * 5 +#define FEDERATED_RECORDS_IN_RANGE 2 /* FEDERATED_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_federated_share { - char *table_name; - char *table_base_name; - /* + /* the primary select query to be used in rnd_init */ char *select_query; @@ -47,11 +47,12 @@ typedef struct st_federated_share { char *username; char *password; char *database; + char *table_name; char *table; char *socket; char *sport; - int port; - uint table_name_length,table_base_name_length,use_count; + ushort port; + uint table_name_length,use_count; pthread_mutex_t mutex; THR_LOCK lock; } FEDERATED_SHARE; @@ -63,7 +64,7 @@ class ha_federated: public handler { THR_LOCK_DATA lock; /* MySQL lock */ FEDERATED_SHARE *share; /* Shared lock info */ - MYSQL *mysql; + MYSQL *mysql; /* MySQL connection */ MYSQL_RES *result; bool scan_flag; uint ref_length; @@ -77,11 +78,13 @@ private: */ uint convert_row_to_internal_format(byte *buf, MYSQL_ROW row); bool create_where_from_key(String *to, KEY *key_info, - const byte *key, uint key_length); + const key_range *start_key, + const key_range *end_key, + bool records_in_range); public: ha_federated(TABLE *table): handler(table), - mysql(0), result(0), scan_flag(0), + mysql(0), result(0), scan_flag(0), ref_length(sizeof(MYSQL_ROW_OFFSET)), current_position(0) { } @@ -94,20 +97,20 @@ public: The name of the index type that will be used for display don't implement this method unless you really have indexes */ + // perhaps get index type const char *index_type(uint inx) { return "REMOTE"; } 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 handler.h - Serg: Double check these (Brian) - // FIX add blob support */ ulong table_flags() const { - return (HA_TABLE_SCAN_ON_INDEX | HA_NOT_EXACT_COUNT | - HA_PRIMARY_KEY_IN_READ_INDEX | HA_FILE_BASED | - HA_AUTO_PART_KEY | HA_CAN_INDEX_BLOBS); + /* fix server to be able to get remote server table flags */ + return (HA_NOT_EXACT_COUNT | + HA_PRIMARY_KEY_IN_READ_INDEX | HA_FILE_BASED | HA_REC_NOT_IN_SEQ | + HA_AUTO_PART_KEY | HA_CAN_INDEX_BLOBS| HA_NO_PREFIX_CHAR_KEYS); } /* This is a bitmap of flags that says how the storage engine @@ -119,29 +122,40 @@ public: If all_parts it's set, MySQL want to know the flags for the combined index up to and including 'part'. */ + /* fix server to be able to get remote server index flags */ ulong index_flags(uint inx, uint part, bool all_parts) const { - return (HA_READ_NEXT); - // return (HA_READ_NEXT | HA_ONLY_WHOLE_INDEX); + return (HA_READ_NEXT | HA_READ_RANGE | HA_READ_AFTER_KEY); } uint max_supported_record_length() const { return HA_MAX_REC_LENGTH; } uint max_supported_keys() const { return MAX_KEY; } - uint max_supported_key_parts() const { return 1024; } - uint max_supported_key_length() const { return 1024; } + uint max_supported_key_parts() const { return MAX_REF_PARTS; } + uint max_supported_key_length() const { return MAX_KEY_LENGTH; } /* Called in test_quick_select to determine if indexes should be used. + Normally, we need to know number of blocks . For federated we need to + know number of blocks on remote side, and number of packets and blocks + on the network side (?) + Talk to Kostja about this - how to get the + number of rows * ... + disk scan time on other side (block size, size of the row) + network time ... + The reason for "records * 1000" is that such a large number forces + this to use indexes " */ - virtual double scan_time() + double scan_time() { - DBUG_PRINT("ha_federated::scan_time", - ("rows %d", records)); return (double)(records*2); + DBUG_PRINT("info", + ("records %d", records)); return (double)(records*1000); } /* The next method will never be called if you do not implement indexes. */ - virtual double read_time(uint index, uint ranges, ha_rows rows) - { return (double) rows / 20.0+1; } + double read_time(uint index, uint ranges, ha_rows rows) + { + return (double) rows / 20.0+1; + } + const key_map *keys_to_use_for_scanning() { return &key_map_full; } /* Everything below are methods that we implment in ha_federated.cc. @@ -151,16 +165,20 @@ public: int open(const char *name, int mode, uint test_if_locked); // required int close(void); // required - int write_row(byte * buf); - int update_row(const byte * old_data, byte * new_data); - int delete_row(const byte * buf); + int write_row(byte *buf); + int update_row(const byte *old_data, byte *new_data); + int delete_row(const byte *buf); int index_init(uint keynr); - int index_read(byte * buf, const byte * key, + int index_read(byte *buf, const byte *key, uint key_len, enum ha_rkey_function find_flag); - int index_read_idx(byte * buf, uint idx, const byte * key, + int index_read_idx(byte *buf, uint idx, const byte *key, uint key_len, enum ha_rkey_function find_flag); - int index_next(byte * buf); + int index_next(byte *buf); int index_end(); + int read_range_first(const key_range *start_key, + const key_range *end_key, + bool eq_range, bool sorted); + int read_range_next(); /* unlike index_init(), rnd_init() can be called two times without rnd_end() in between (it only makes sense if scan=1). @@ -172,13 +190,15 @@ public: int rnd_init(bool scan); //required int rnd_end(); int rnd_next(byte *buf); //required - int rnd_pos(byte * buf, byte *pos); //required + int rnd_pos(byte *buf, byte *pos); //required void position(const byte *record); //required void info(uint); //required int delete_all_rows(void); int create(const char *name, TABLE *form, HA_CREATE_INFO *create_info); //required + ha_rows records_in_range(uint inx, key_range *start_key, + key_range *end_key); THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to, enum thr_lock_type lock_type); //required diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index f999f17aedf..011a9b1cf91 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5370,3 +5370,15 @@ ER_SCALE_BIGGER_THAN_PRECISION 42000 S1009 eng "Scale may not be larger than the precision (column '%-.64s')." ER_WRONG_LOCK_OF_SYSTEM_TABLE eng "You can't combine write-locking of system '%-.64s.%-.64s' table with other tables" +ER_CONNECT_TO_FOREIGN_DATA_SRC + eng "Unable to connect to foreign data source - database '%s'!" +ER_QUERY_ON_FOREIGN_DATA_SRC + eng "There was a problem processing the query on the foreign data source. Data source error: '%-.64s'" +ER_FOREIGN_DATA_SRC_DOESNT_EXIST + eng "The foreign data source you are trying to reference does not exist. Data source error : '%-.64s'" +ER_FOREIGN_DATA_STRING_INVALID_CANT_CREATE + eng "Can't create federated table. The data source connection string '%-.64s' is not in the correct format" +ER_FOREIGN_DATA_STRING_INVALID + eng "The data source connection string '%-.64s' is not in the correct format" +ER_CANT_CREATE_FEDERATED_TABLE + eng "Can't create federated table. Foreign data src error : '%-.64s'" From f39e07d6de1594694557ceadde7dbfcca8c21b20 Mon Sep 17 00:00:00 2001 From: "brian@zim.(none)" <> Date: Fri, 15 Jul 2005 17:36:20 -0700 Subject: [PATCH 17/48] Added federated to max base test. --- BUILD/SETUP.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BUILD/SETUP.sh b/BUILD/SETUP.sh index 32dd915e60e..1603cfadbed 100755 --- a/BUILD/SETUP.sh +++ b/BUILD/SETUP.sh @@ -48,7 +48,7 @@ global_warnings="-Wimplicit -Wreturn-type -Wswitch -Wtrigraphs -Wcomment -W -Wch c_warnings="$global_warnings -Wunused" cxx_warnings="$global_warnings -Woverloaded-virtual -Wsign-promo -Wreorder -Wctor-dtor-privacy -Wnon-virtual-dtor" -base_max_configs="--with-innodb --with-berkeley-db --with-ndbcluster --with-archive-storage-engine --with-openssl --with-big-tables --with-blackhole-storage-engine" +base_max_configs="--with-innodb --with-berkeley-db --with-ndbcluster --with-archive-storage-engine --with-openssl --with-big-tables --with-blackhole-storage-engine --with-federated-storage-engine" max_leave_isam_configs="--with-innodb --with-berkeley-db --with-ndbcluster --with-archive-storage-engine --with-federated-storage-engine --with-blackhole-storage-engine --with-openssl --with-embedded-server --with-big-tables" max_configs="$base_max_configs --with-embedded-server" From 81d93df1ae8a1aff17345b18188ce0ddecdb9e19 Mon Sep 17 00:00:00 2001 From: "evgen@moonbone.local" <> Date: Sat, 16 Jul 2005 05:31:16 +0400 Subject: [PATCH 18/48] Fix bug#11868 NOT NULL ref optimization in subquery used in update must be disabled if ref is built with a key from the updated table Problem was in add_not_null_conds() optimization function. It contains following code: JOIN_TAB *referred_tab= not_null_item->field->table->reginfo.join_tab; ... add_cond_and_fix(&referred_tab->select_cond, notnull); For UPDATE described in bug report referred_tab is 0 and dereferencing it crashes the server. --- mysql-test/r/update.result | 11 +++++++++++ mysql-test/t/update.test | 13 +++++++++++++ sql/sql_select.cc | 7 ++++++- 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/update.result b/mysql-test/r/update.result index d83952e118b..e35d4e29fe4 100644 --- a/mysql-test/r/update.result +++ b/mysql-test/r/update.result @@ -240,3 +240,14 @@ update t1, t2 set t1.a = t2.a where t2.b = t1.b; show warnings; Level Code Message drop table t1, t2; +create table t1(f1 int, f2 int); +create table t2(f3 int, f4 int); +create index idx on t2(f3); +insert into t1 values(1,0),(2,0); +insert into t2 values(1,1),(2,2); +UPDATE t1 SET t1.f2=(SELECT MAX(t2.f4) FROM t2 WHERE t2.f3=t1.f1); +select * from t1; +f1 f2 +1 1 +2 2 +drop table t1,t2; diff --git a/mysql-test/t/update.test b/mysql-test/t/update.test index 6a90fb95760..41f7d37e6d0 100644 --- a/mysql-test/t/update.test +++ b/mysql-test/t/update.test @@ -201,3 +201,16 @@ analyze table t1,t2; update t1, t2 set t1.a = t2.a where t2.b = t1.b; show warnings; drop table t1, t2; + +# +# Bug #11868 Update with subquery with ref built with a key from the updated +# table crashes server +# +create table t1(f1 int, f2 int); +create table t2(f3 int, f4 int); +create index idx on t2(f3); +insert into t1 values(1,0),(2,0); +insert into t2 values(1,1),(2,2); +UPDATE t1 SET t1.f2=(SELECT MAX(t2.f4) FROM t2 WHERE t2.f3=t1.f1); +select * from t1; +drop table t1,t2; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 43cf649685e..e56828c3244 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -3555,7 +3555,12 @@ static void add_not_null_conds(JOIN *join) DBUG_ASSERT(item->type() == Item::FIELD_ITEM); Item_field *not_null_item= (Item_field*)item; JOIN_TAB *referred_tab= not_null_item->field->table->reginfo.join_tab; - if (referred_tab->join != join) + /* + For UPDATE queries such as: + UPDATE t1 SET t1.f2=(SELECT MAX(t2.f4) FROM t2 WHERE t2.f3=t1.f1); + not_null_item is the t1.f1, but it's referred_tab is 0. + */ + if (!referred_tab || referred_tab->join != join) continue; Item *notnull; if (!(notnull= new Item_func_isnotnull(not_null_item))) From 53c630a524e73122581f677790241daf81e46ff9 Mon Sep 17 00:00:00 2001 From: "georg@lmy002.wdf.sap.corp" <> Date: Sat, 16 Jul 2005 07:13:40 +0200 Subject: [PATCH 19/48] Restore previous used client charset in mysql_reconnect Moved mysql_set_character_set function to client.c Changed function prototype for mysql_set_character_set (as suggested by Konstantin) --- include/mysql.h | 2 +- libmysql/libmysql.c | 33 --------------------------------- sql-common/client.c | 44 +++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 44 insertions(+), 35 deletions(-) diff --git a/include/mysql.h b/include/mysql.h index daa29f70e16..2ba41e6d367 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -396,7 +396,7 @@ unsigned int STDCALL mysql_warning_count(MYSQL *mysql); const char * STDCALL mysql_info(MYSQL *mysql); unsigned long STDCALL mysql_thread_id(MYSQL *mysql); const char * STDCALL mysql_character_set_name(MYSQL *mysql); -int STDCALL mysql_set_character_set(MYSQL *mysql, char *csname); +int STDCALL mysql_set_character_set(MYSQL *mysql, const char *csname); MYSQL * STDCALL mysql_init(MYSQL *mysql); my_bool STDCALL mysql_ssl_set(MYSQL *mysql, const char *key, diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index 097983cbbd3..f9ddf7fa665 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -1511,39 +1511,6 @@ void STDCALL mysql_get_character_set_info(MYSQL *mysql, MY_CHARSET_INFO *csinfo) csinfo->dir = charsets_dir; } -int STDCALL mysql_set_character_set(MYSQL *mysql, char *cs_name) -{ - struct charset_info_st *cs; - const char *save_csdir= charsets_dir; - - if (mysql->options.charset_dir) - charsets_dir= mysql->options.charset_dir; - - if ((cs= get_charset_by_csname(cs_name, MY_CS_PRIMARY, MYF(0)))) - { - char buff[MY_CS_NAME_SIZE + 10]; - charsets_dir= save_csdir; - sprintf(buff, "SET NAMES %s", cs_name); - if (!mysql_query(mysql, buff)) - { - mysql->charset= cs; - } - } - else - { - char cs_dir_name[FN_REFLEN]; - get_charsets_dir(cs_dir_name); - mysql->net.last_errno= CR_CANT_READ_CHARSET; - strmov(mysql->net.sqlstate, unknown_sqlstate); - my_snprintf(mysql->net.last_error, sizeof(mysql->net.last_error) - 1, - ER(mysql->net.last_errno), cs_name, cs_dir_name); - - } - charsets_dir= save_csdir; - return mysql->net.last_errno; -} - - uint STDCALL mysql_thread_safe(void) { #ifdef THREAD diff --git a/sql-common/client.c b/sql-common/client.c index c736acae857..4ecc8d26fc7 100644 --- a/sql-common/client.c +++ b/sql-common/client.c @@ -2206,7 +2206,8 @@ my_bool mysql_reconnect(MYSQL *mysql) tmp_mysql.rpl_pivot = mysql->rpl_pivot; if (!mysql_real_connect(&tmp_mysql,mysql->host,mysql->user,mysql->passwd, mysql->db, mysql->port, mysql->unix_socket, - mysql->client_flag | CLIENT_REMEMBER_OPTIONS)) + mysql->client_flag | CLIENT_REMEMBER_OPTIONS) || + mysql_set_character_set(&tmp_mysql, mysql->charset->csname)) { mysql->net.last_errno= tmp_mysql.net.last_errno; strmov(mysql->net.last_error, tmp_mysql.net.last_error); @@ -2778,8 +2779,49 @@ uint STDCALL mysql_errno(MYSQL *mysql) return mysql->net.last_errno; } + const char * STDCALL mysql_error(MYSQL *mysql) { return mysql->net.last_error; } +/* + mysql_set_character_set function sends SET NAMES cs_name to + the server (which changes character_set_client, character_set_result + and character_set_connection) and updates mysql->charset so other + functions like mysql_real_escape will work correctly. +*/ +int STDCALL mysql_set_character_set(MYSQL *mysql, const char *cs_name) +{ + struct charset_info_st *cs; + const char *save_csdir= charsets_dir; + + if (mysql->options.charset_dir) + charsets_dir= mysql->options.charset_dir; + + if (strlen(cs_name) < MY_CS_NAME_SIZE && + (cs= get_charset_by_csname(cs_name, MY_CS_PRIMARY, MYF(0)))) + { + char buff[MY_CS_NAME_SIZE + 10]; + charsets_dir= save_csdir; + sprintf(buff, "SET NAMES %s", cs_name); + if (!mysql_real_query(mysql, buff, strlen(buff))) + { + mysql->charset= cs; + } + } + else + { + char cs_dir_name[FN_REFLEN]; + get_charsets_dir(cs_dir_name); + mysql->net.last_errno= CR_CANT_READ_CHARSET; + strmov(mysql->net.sqlstate, unknown_sqlstate); + my_snprintf(mysql->net.last_error, sizeof(mysql->net.last_error) - 1, + ER(mysql->net.last_errno), cs_name, cs_dir_name); + + } + charsets_dir= save_csdir; + return mysql->net.last_errno; +} + + From 912fef706a7d54be91fd9e3b5584af17b8943100 Mon Sep 17 00:00:00 2001 From: "georg@lmy002.wdf.sap.corp" <> Date: Sat, 16 Jul 2005 09:19:23 +0200 Subject: [PATCH 20/48] cs fixes --- tests/mysql_client_test.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 77c8d9959e6..557794ab1f6 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -13617,8 +13617,8 @@ static void test_bug10214() static void test_client_character_set() { MY_CHARSET_INFO cs; - char *csname = (char*) "utf8"; - char *csdefault = (char*)mysql_character_set_name(mysql); + char *csname= (char*) "utf8"; + char *csdefault= (char*)mysql_character_set_name(mysql); int rc; myheader("test_client_character_set"); @@ -13732,16 +13732,16 @@ static void test_bug11037() fprintf(stdout, "Got error, as expected:\n [%d] %s\n", mysql_stmt_errno(stmt), mysql_stmt_error(stmt)); - rc = mysql_stmt_execute(stmt); + rc= mysql_stmt_execute(stmt); check_execute(stmt, rc); - rc = mysql_stmt_fetch(stmt); + rc= mysql_stmt_fetch(stmt); DIE_UNLESS(rc==0); - rc = mysql_stmt_fetch(stmt); + rc= mysql_stmt_fetch(stmt); DIE_UNLESS(rc==MYSQL_NO_DATA); - rc = mysql_stmt_fetch(stmt); + rc= mysql_stmt_fetch(stmt); DIE_UNLESS(rc==MYSQL_NO_DATA); mysql_stmt_close(stmt); From ff0c7f22cfab033022d4090c7cb324d4b4ace619 Mon Sep 17 00:00:00 2001 From: "timour@mysql.com" <> Date: Sat, 16 Jul 2005 10:30:25 +0300 Subject: [PATCH 21/48] Added test for Bug #11521 "Negative integer keys incorrectly substituted for 0 during range analysis." The problem is that the range optimizer incorrectly replaces any negative constant with '0' for all types except BIGINT because the method save_in_field() casts negative integers to non-negative. This causes incorrect query results where (0 = any_negative_number). The problem caused by this bug is fixed by the patch for BUG#11185. That patch constitutes an optimization due to which the problem code is never called with negative constants. This patch adds a test so we are sure that the problem does not reappear. --- mysql-test/r/select.result | 12 ++++++++++++ mysql-test/t/select.test | 11 +++++++++++ 2 files changed, 23 insertions(+) diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result index 5c0616f9e54..6c9ec9d8297 100644 --- a/mysql-test/r/select.result +++ b/mysql-test/r/select.result @@ -2570,3 +2570,15 @@ f2 1 NULL drop table t1,t2; +create table t2 (a tinyint unsigned); +create index t2i on t2(a); +insert into t2 values (0), (254), (255); +explain select * from t2 where a > -1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 index t2i t2i 2 NULL 3 Using where; Using index +select * from t2 where a > -1; +a +0 +254 +255 +drop table t2; diff --git a/mysql-test/t/select.test b/mysql-test/t/select.test index 2e261d0611f..caa9e332e57 100644 --- a/mysql-test/t/select.test +++ b/mysql-test/t/select.test @@ -2127,3 +2127,14 @@ insert into t2 values (1,2,3),(2,4,6); select A.f2 from t1 left join t2 A on A.f2 = f1 where A.f3=(select min(f3) from t2 C where A.f4 = C.f4) or A.f3 IS NULL; drop table t1,t2; + +# +# Bug #11521 Negative integer keys incorrectly substituted for 0 during +# range analysis. + +create table t2 (a tinyint unsigned); +create index t2i on t2(a); +insert into t2 values (0), (254), (255); +explain select * from t2 where a > -1; +select * from t2 where a > -1; +drop table t2; From 8b0adacbc64f4db3accc72504336d1413dedc28b Mon Sep 17 00:00:00 2001 From: "konstantin@mysql.com" <> Date: Sat, 16 Jul 2005 16:10:42 +0400 Subject: [PATCH 22/48] After-merge fixes (4.1 -> 5.0). --- mysql-test/r/sp.result | 26 ++++++++++++++++---------- mysql-test/r/view.result | 2 +- mysql-test/t/sp.test | 19 ++++++++++--------- sql/item_func.cc | 2 +- sql/item_func.h | 6 ++++-- sql/mysql_priv.h | 2 +- sql/mysqld.cc | 8 +------- 7 files changed, 34 insertions(+), 31 deletions(-) diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index 158dd9bb949..fd63204e32f 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -2657,16 +2657,22 @@ end| call avg ()| drop procedure avg| drop procedure if exists bug6129| -set @@sql_mode = 'traditional'| -create procedure bug6129(mode text) -select @@sql_mode = mode| -call bug6129(@@sql_mode)| -@@sql_mode = mode -1 -set @@sql_mode = ''| -call bug6129(@@sql_mode)| -@@sql_mode = mode -0 +set @old_mode= @@sql_mode; +set @@sql_mode= ""; +create procedure bug6129() +select @@sql_mode| +call bug6129()| +@@sql_mode + +set @@sql_mode= "NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO"| +call bug6129()| +@@sql_mode +NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO +set @@sql_mode= "NO_ZERO_IN_DATE"| +call bug6129()| +@@sql_mode +NO_ZERO_IN_DATE +set @@sql_mode=@old_mode; drop procedure bug6129| drop procedure if exists bug9856| create procedure bug9856() diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index e7bc3a1bb1f..a132279c6bc 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -1836,7 +1836,7 @@ create table t2 (b timestamp default now()); create view v1 as select a,b,t1.a < now() from t1,t2 where t1.a < now(); SHOW CREATE VIEW v1; View Create View -v1 CREATE ALGORITHM=UNDEFINED VIEW `test`.`v1` AS select sql_no_cache `test`.`t1`.`a` AS `a`,`test`.`t2`.`b` AS `b`,(`test`.`t1`.`a` < now()) AS `t1.a < now()` from `test`.`t1` join `test`.`t2` where (`test`.`t1`.`a` < now()) +v1 CREATE ALGORITHM=UNDEFINED VIEW `test`.`v1` AS select sql_no_cache `test`.`t1`.`a` AS `a`,`test`.`t2`.`b` AS `b`,(`test`.`t1`.`a` < now()) AS `t1.a < now()` from (`test`.`t1` join `test`.`t2`) where (`test`.`t1`.`a` < now()) drop view v1; drop table t1, t2; CREATE TABLE t1 ( a varchar(50) ); diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index 209386fcba4..d9e6163cbc7 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -3377,15 +3377,16 @@ drop procedure avg| --disable_warnings drop procedure if exists bug6129| --enable_warnings -set @@sql_mode = 'traditional'| -create procedure bug6129(mode text) - select @@sql_mode = mode| - -# 1 -call bug6129(@@sql_mode)| -set @@sql_mode = ''| -# 0 -call bug6129(@@sql_mode)| +set @old_mode= @@sql_mode; +set @@sql_mode= ""; +create procedure bug6129() + select @@sql_mode| +call bug6129()| +set @@sql_mode= "NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO"| +call bug6129()| +set @@sql_mode= "NO_ZERO_IN_DATE"| +call bug6129()| +set @@sql_mode=@old_mode; drop procedure bug6129| diff --git a/sql/item_func.cc b/sql/item_func.cc index faf73536eb6..c3bdb11418f 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -4229,7 +4229,7 @@ Item_func_get_system_var(sys_var *var_arg, enum_var_type var_type_arg, bool -Item_func_get_system_var::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) +Item_func_get_system_var::fix_fields(THD *thd, Item **ref) { Item *item= var->item(thd, var_type, &component); DBUG_ENTER("Item_func_get_system_var::fix_fields"); diff --git a/sql/item_func.h b/sql/item_func.h index b8638161787..43a85e6aa0b 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -1210,15 +1210,17 @@ public: Item_func_get_system_var(sys_var *var_arg, enum_var_type var_type_arg, LEX_STRING *component_arg, const char *name_arg, size_t name_len_arg); - bool fix_fields(THD *thd, TABLE_LIST *tables, Item **ref); + bool fix_fields(THD *thd, Item **ref); /* Stubs for pure virtual methods. Should never be called: this item is always substituted with a constant in fix_fields(). */ - double val() { DBUG_ASSERT(0); return 0.0; } + double val_real() { DBUG_ASSERT(0); return 0.0; } longlong val_int() { DBUG_ASSERT(0); return 0; } String* val_str(String*) { DBUG_ASSERT(0); return 0; } void fix_length_and_dec() { DBUG_ASSERT(0); } + /* TODO: fix to support views */ + const char *func_name() const { return "get_system_var"; } }; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index b7d5098e230..49eb50be580 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -477,7 +477,7 @@ typedef my_bool (*qc_engine_callback)(THD *thd, char *table_key, class user_var_entry; enum enum_var_type { - OPT_DEFAULT, OPT_SESSION, OPT_GLOBAL + OPT_DEFAULT= 0, OPT_SESSION, OPT_GLOBAL }; class sys_var; #include "item.h" diff --git a/sql/mysqld.cc b/sql/mysqld.cc index a9a81b47cda..78d9af387da 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -685,13 +685,7 @@ static void close_connections(void) { DBUG_PRINT("quit",("Informing thread %ld that it's time to die", tmp->thread_id)); - /* - Re: bug 7403 - close_connection will be called mulitple times - bug a wholesale clean up of our network code is a very large - project. This will wake up the socket on Windows and prevent the - printing of the error message that we are force closing a connection. - */ - close_connection(tmp, 0, 1); + tmp->killed= THD::KILL_CONNECTION; if (tmp->mysys_var) { tmp->mysys_var->abort=1; From 6ea960c8d6c8fc00a7a3a1a34e6f095117be02f4 Mon Sep 17 00:00:00 2001 From: "igor@rurik.mysql.com" <> Date: Sat, 16 Jul 2005 08:19:20 -0700 Subject: [PATCH 23/48] opt_range.cc: Fixed bug #11853. Corrected the code of the range optimization for NOT IN and NOT BETWEEN. range.test, range.result: Fixed bug #11853. --- mysql-test/r/range.result | 24 ++++++++++++++++++++++++ mysql-test/t/range.test | 23 +++++++++++++++++++++++ sql/opt_range.cc | 3 ++- 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/range.result b/mysql-test/r/range.result index 6f9b4e41b22..e8e30b3653a 100644 --- a/mysql-test/r/range.result +++ b/mysql-test/r/range.result @@ -744,3 +744,27 @@ a b 1 3 DROP VIEW v1; DROP TABLE t1; +CREATE TABLE t1 (name varchar(15) NOT NULL, KEY idx(name)); +INSERT INTO t1 VALUES ('Betty'), ('Anna'); +SELECT * FROM t1; +name +Anna +Betty +DELETE FROM t1 WHERE name NOT LIKE 'A%a'; +SELECT * FROM t1; +name +Anna +DROP TABLE t1; +CREATE TABLE t1 (a int, KEY idx(a)); +INSERT INTO t1 VALUES (NULL), (1), (2), (3); +SELECT * FROM t1; +a +NULL +1 +2 +3 +DELETE FROM t1 WHERE NOT(a <=> 2); +SELECT * FROM t1; +a +2 +DROP TABLE t1; diff --git a/mysql-test/t/range.test b/mysql-test/t/range.test index 3d2285b1633..065cef659b8 100644 --- a/mysql-test/t/range.test +++ b/mysql-test/t/range.test @@ -553,3 +553,26 @@ SELECT a,b FROM v1 WHERE a < 2 and b=3; DROP VIEW v1; DROP TABLE t1; + +# +# Bug #11853: DELETE statement with a NOT (LIKE/<=>) where condition +# for an indexed attribute +# + +CREATE TABLE t1 (name varchar(15) NOT NULL, KEY idx(name)); +INSERT INTO t1 VALUES ('Betty'), ('Anna'); + +SELECT * FROM t1; +DELETE FROM t1 WHERE name NOT LIKE 'A%a'; +SELECT * FROM t1; + +DROP TABLE t1; + +CREATE TABLE t1 (a int, KEY idx(a)); +INSERT INTO t1 VALUES (NULL), (1), (2), (3); + +SELECT * FROM t1; +DELETE FROM t1 WHERE NOT(a <=> 2); +SELECT * FROM t1; + +DROP TABLE t1; diff --git a/sql/opt_range.cc b/sql/opt_range.cc index c2760b08b6e..676f8d8a33d 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -3531,7 +3531,8 @@ static SEL_TREE *get_mm_tree(PARAM *param,COND *cond) if (arg->type() != Item::FUNC_ITEM) DBUG_RETURN(0); cond_func= (Item_func*) arg; - if (cond_func->select_optimize() == Item_func::OPTIMIZE_NONE) + if (cond_func->functype() != Item_func::BETWEEN && + cond_func->functype() != Item_func::IN_FUNC) DBUG_RETURN(0); inv= TRUE; } From bff3507b1d6ba1c8d0f46f6d19528a0d52e31610 Mon Sep 17 00:00:00 2001 From: "igor@rurik.mysql.com" <> Date: Sat, 16 Jul 2005 18:06:34 -0700 Subject: [PATCH 24/48] func_in.result, func_in.test: Fixed bug #11885. sql_select.cc: Fixed bug #11885. Predicates of the forms 'a IN (v)' 'a NOT IN (v)' now is replaced by 'a=v' and 'a<>v' at the parsing stage. sql_yacc.yy: Fixed bug #11885. Predicates of the forms 'a IN (v)' 'a NOT IN (v)' now is replaced by 'a=v' and 'a<>v' at the parsing stage. --- mysql-test/r/func_in.result | 25 ++++++++++++++++++++++++- mysql-test/t/func_in.test | 18 ++++++++++++++++++ sql/sql_select.cc | 6 +++--- sql/sql_yacc.yy | 20 ++++++++++++++++++-- 4 files changed, 63 insertions(+), 6 deletions(-) diff --git a/mysql-test/r/func_in.result b/mysql-test/r/func_in.result index f75fe0d1627..b0c0178328e 100644 --- a/mysql-test/r/func_in.result +++ b/mysql-test/r/func_in.result @@ -119,7 +119,7 @@ c char(1) character set latin1 collate latin1_danish_ci insert into t1 values ('A','B','C'); insert into t1 values ('a','c','c'); select * from t1 where a in (b); -ERROR HY000: Illegal mix of collations (latin1_general_ci,IMPLICIT) and (latin1_swedish_ci,IMPLICIT) for operation ' IN ' +ERROR HY000: Illegal mix of collations (latin1_general_ci,IMPLICIT) and (latin1_swedish_ci,IMPLICIT) for operation '=' select * from t1 where a in (b,c); ERROR HY000: Illegal mix of collations (latin1_general_ci,IMPLICIT), (latin1_swedish_ci,IMPLICIT), (latin1_danish_ci,IMPLICIT) for operation ' IN ' select * from t1 where 'a' in (a,b,c); @@ -193,3 +193,26 @@ select * from t1 where a in (NULL, 'aa'); a aa drop table t1; +CREATE TABLE t1 (a int PRIMARY KEY); +INSERT INTO t1 VALUES (44), (45), (46); +SELECT * FROM t1 WHERE a IN (45); +a +45 +SELECT * FROM t1 WHERE a NOT IN (0, 45); +a +44 +46 +SELECT * FROM t1 WHERE a NOT IN (45); +a +44 +46 +CREATE VIEW v1 AS SELECT * FROM t1 WHERE a NOT IN (45); +SHOW CREATE VIEW v1; +View Create View +v1 CREATE ALGORITHM=UNDEFINED VIEW `test`.`v1` AS select `test`.`t1`.`a` AS `a` from `test`.`t1` where (`test`.`t1`.`a` <> 45) +SELECT * FROM v1; +a +44 +46 +DROP VIEW v1; +DROP TABLE t1; diff --git a/mysql-test/t/func_in.test b/mysql-test/t/func_in.test index 6e0883b821f..3a6b6653098 100644 --- a/mysql-test/t/func_in.test +++ b/mysql-test/t/func_in.test @@ -101,3 +101,21 @@ create table t1 (a char(20) character set binary); insert into t1 values ('aa'), ('bb'); select * from t1 where a in (NULL, 'aa'); drop table t1; + +# +# Bug #11885: WHERE condition with NOT IN (one element) +# + +CREATE TABLE t1 (a int PRIMARY KEY); +INSERT INTO t1 VALUES (44), (45), (46); + +SELECT * FROM t1 WHERE a IN (45); +SELECT * FROM t1 WHERE a NOT IN (0, 45); +SELECT * FROM t1 WHERE a NOT IN (45); + +CREATE VIEW v1 AS SELECT * FROM t1 WHERE a NOT IN (45); +SHOW CREATE VIEW v1; +SELECT * FROM v1; + +DROP VIEW v1; +DROP TABLE t1; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 87ce26b24d5..e60688fe574 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -2834,11 +2834,11 @@ add_key_fields(KEY_FIELD **key_fields,uint *and_level, cond_func->arguments()[1]->real_item()->type() == Item::FIELD_ITEM && !(cond_func->arguments()[0]->used_tables() & OUTER_REF_TABLE_BIT)) values--; + DBUG_ASSERT(cond_func->functype() != Item_func::IN_FUNC || + cond_func->argument_count() != 2); add_key_equal_fields(key_fields, *and_level, cond_func, (Item_field*) (cond_func->key_item()->real_item()), - cond_func->argument_count() == 2 && - cond_func->functype() == Item_func::IN_FUNC, - values, + 0, values, cond_func->argument_count()-1, usable_tables); } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 2397540deae..756a8a6ee3d 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -4227,9 +4227,25 @@ bool_pri: predicate: bit_expr IN_SYM '(' expr_list ')' - { $4->push_front($1); $$= new Item_func_in(*$4); } + { + if ($4->elements == 1) + $$= new Item_func_eq($1, $4->head()); + else + { + $4->push_front($1); + $$= new Item_func_in(*$4); + } + } | bit_expr not IN_SYM '(' expr_list ')' - { $5->push_front($1); $$= negate_expression(YYTHD, new Item_func_in(*$5)); } + { + if ($5->elements == 1) + $$= new Item_func_ne($1, $5->head()); + else + { + $5->push_front($1); + $$= negate_expression(YYTHD, new Item_func_in(*$5)); + } + } | bit_expr IN_SYM in_subselect { $$= new Item_in_subselect($1, $3); } | bit_expr not IN_SYM in_subselect From dd129ed32954bf49baa852f3e56dd2358db211ef Mon Sep 17 00:00:00 2001 From: "patg@radha.local" <> Date: Sun, 17 Jul 2005 04:10:19 +0200 Subject: [PATCH 25/48] Federated Storage Engine, bug 9925 "Federated query processor (step 2 or step 3) not using logic in WHERE statement" --- sql/ha_federated.cc | 454 ++++++++++++++++++------------------------- sql/ha_federated.h | 49 ++++- sql/share/errmsg.txt | 6 +- 3 files changed, 233 insertions(+), 276 deletions(-) diff --git a/sql/ha_federated.cc b/sql/ha_federated.cc index 0ad0bd6cb9d..c953ff1d7ed 100644 --- a/sql/ha_federated.cc +++ b/sql/ha_federated.cc @@ -346,7 +346,6 @@ */ -#define FEDERATED_DEBUG 1 #include "mysql_priv.h" #ifdef USE_PRAGMA_IMPLEMENTATION #pragma implementation // gcc: Class implementation @@ -435,19 +434,18 @@ static int check_foreign_data_source( FEDERATED_SHARE *share, bool table_create_flag) { - char escaped_table_name[STRING_BUFFER_USUAL_SIZE]; + char escaped_table_name[NAME_LEN*2]; char query_buffer[FEDERATED_QUERY_BUFFER_SIZE]; char error_buffer[FEDERATED_QUERY_BUFFER_SIZE]; uint error_code; String query(query_buffer, sizeof(query_buffer), &my_charset_bin); - MYSQL_RES *result= 0; MYSQL *mysql; DBUG_ENTER("ha_federated::check_foreign_data_source"); /* Zero the length, otherwise the string will have misc chars */ query.length(0); /* error out if we can't alloc memory for mysql_init(NULL) (per Georg) */ - if (! (mysql= mysql_init(NULL))) + if (!(mysql= mysql_init(NULL))) DBUG_RETURN(HA_ERR_OUT_OF_MEM); /* check if we can connect */ if (!mysql_real_connect(mysql, @@ -463,12 +461,13 @@ static int check_foreign_data_source( ER_CANT_CREATE_FEDERATED_TABLE if called by ::create */ error_code= table_create_flag? - ER_CANT_CREATE_FEDERATED_TABLE : ER_CONNECT_TO_FOREIGN_DATA_SRC; + ER_CANT_CREATE_FEDERATED_TABLE : ER_CONNECT_TO_FOREIGN_DATA_SOURCE; my_sprintf(error_buffer, (error_buffer, " database %s username %s hostname %s", - share->database, share->username, share->hostname)); - my_error(ER_CONNECT_TO_FOREIGN_DATA_SRC, MYF(0), error_buffer); + share->database, share->username, share->hostname)); + + my_error(ER_CONNECT_TO_FOREIGN_DATA_SOURCE, MYF(0), error_buffer); goto error; } else @@ -482,34 +481,35 @@ static int check_foreign_data_source( /* Note: I am not using INORMATION_SCHEMA because this needs to work with < 5.0 if we can connect, then make sure the table exists + + the query will be: SELECT * FROM `tablename` WHERE 1=0 */ - query.append("SELECT * FROM `", 15); + query.append(FEDERATED_SELECT); + query.append(FEDERATED_STAR); + query.append(FEDERATED_FROM); + query.append(FEDERATED_BTICK); escape_string_for_mysql(&my_charset_bin, (char *)escaped_table_name, sizeof(escaped_table_name), share->table_name, share->table_name_length); query.append(escaped_table_name); - query.append("` WHERE 1=0", 11); + query.append(FEDERATED_BTICK); + query.append(FEDERATED_WHERE); + query.append(FEDERATED_1EQ0); - /* - an || here on the error code - if it's a syntax error, or missing - table, give us that, or if the connection has problems, give us that - */ + DBUG_PRINT("info", ("check_foreign_data_source query %s", query.c_ptr_quick())); if (mysql_real_query(mysql, query.ptr(), query.length())) { error_code= table_create_flag ? - ER_CANT_CREATE_FEDERATED_TABLE : ER_FOREIGN_DATA_SRC_DOESNT_EXIST; + ER_CANT_CREATE_FEDERATED_TABLE : ER_FOREIGN_DATA_SOURCE_DOESNT_EXIST; my_sprintf(error_buffer, (error_buffer, ": %d : %s", mysql_errno(mysql), mysql_error(mysql))); my_error(error_code, MYF(0), error_buffer); goto error; } - - mysql_close(mysql); - } - DBUG_RETURN(0); + error_code=0; error: mysql_close(mysql); @@ -706,8 +706,8 @@ uint ha_federated::convert_row_to_internal_format(byte *record, MYSQL_ROW row) DBUG_ENTER("ha_federated::convert_row_to_internal_format"); - num_fields= mysql_num_fields(result); - lengths= mysql_fetch_lengths(result); + num_fields= mysql_num_fields(stored_result); + lengths= mysql_fetch_lengths(stored_result); memset(record, 0, table->s->null_bytes); @@ -738,9 +738,9 @@ uint ha_federated::convert_row_to_internal_format(byte *record, MYSQL_ROW row) static bool emit_key_part_name(String *to, KEY_PART_INFO *part) { DBUG_ENTER("emit_key_part_name"); - if (to->append(" `", 2) || + if (to->append(FEDERATED_BTICK) || to->append(part->field->field_name) || - to->append("`", 1)) + to->append(FEDERATED_BTICK)) DBUG_RETURN(1); // Out of memory DBUG_RETURN(0); } @@ -752,7 +752,7 @@ static bool emit_key_part_element(String *to, KEY_PART_INFO *part, Field *field= part->field; DBUG_ENTER("emit_key_part_element"); - if (needs_quotes && to->append("'", 1)) + if (needs_quotes && to->append(FEDERATED_SQUOTE)) DBUG_RETURN(1); if (part->type == HA_KEYTYPE_BIT) @@ -804,10 +804,10 @@ static bool emit_key_part_element(String *to, KEY_PART_INFO *part, DBUG_RETURN(1); } - if (is_like && to->append("%", 1)) + if (is_like && to->append(FEDERATED_PERCENT)) DBUG_RETURN(1); - if (needs_quotes && to->append("'",1)) + if (needs_quotes && to->append(FEDERATED_SQUOTE)) DBUG_RETURN(1); DBUG_RETURN(0); @@ -1057,7 +1057,8 @@ bool ha_federated::create_where_from_key(String *to, const key_range *end_key, bool records_in_range) { - bool both_not_null= (start_key != NULL && end_key != NULL) ? TRUE : FALSE; + bool both_not_null= + (start_key != NULL && end_key != NULL) ? TRUE : FALSE; const byte *ptr; uint remainder, length; char tmpbuff[FEDERATED_QUERY_BUFFER_SIZE]; @@ -1082,9 +1083,9 @@ bool ha_federated::create_where_from_key(String *to, if (both_not_null) { if (i > 0) - tmp.append(") AND (", 7); + tmp.append(FEDERATED_CONJUNCTION); else - tmp.append("(", 1); + tmp.append(FEDERATED_OPENPAREN); } for (key_part= key_info->key_part, @@ -1105,13 +1106,13 @@ bool ha_federated::create_where_from_key(String *to, if (*ptr++) { if (emit_key_part_name(&tmp, key_part) || - tmp.append(" IS NULL", 8)) + tmp.append(FEDERATED_ISNULL)) DBUG_RETURN(1); continue; } } - if (tmp.append("(", 1)) + if (tmp.append(FEDERATED_OPENPAREN)) DBUG_RETURN(1); switch(ranges[i]->flag) { @@ -1126,14 +1127,15 @@ bool ha_federated::create_where_from_key(String *to, if (records_in_range) { - if (tmp.append(" >= ", 4)) + if (tmp.append(FEDERATED_GE)) DBUG_RETURN(1); } else { - if (tmp.append(" = ", 3)) + if (tmp.append(FEDERATED_EQ)) DBUG_RETURN(1); } + if (emit_key_part_element(&tmp, key_part, needs_quotes, 0, ptr, part_length)) DBUG_RETURN(1); @@ -1142,9 +1144,9 @@ bool ha_federated::create_where_from_key(String *to, /* LIKE */ { if (emit_key_part_name(&tmp, key_part) || - tmp.append(" LIKE ", 6) || - emit_key_part_element(&tmp, key_part, needs_quotes, 1, ptr, - part_length)) + tmp.append(FEDERATED_LIKE) || + emit_key_part_element(&tmp, key_part, needs_quotes, 1, ptr, + part_length)) DBUG_RETURN(1); } break; @@ -1155,35 +1157,43 @@ bool ha_federated::create_where_from_key(String *to, DBUG_RETURN(1); if (i > 0) /* end key */ - tmp.append(" <= ", 4); + { + if (tmp.append(FEDERATED_LE)) + DBUG_RETURN(1); + } else /* start key */ - tmp.append(" > ", 3); + { + if (tmp.append(FEDERATED_GT)) + DBUG_RETURN(1); + } if (emit_key_part_element(&tmp, key_part, needs_quotes, 0, ptr, - part_length)) - DBUG_RETURN(1); + part_length)) + { + DBUG_RETURN(1); + } break; } case(HA_READ_KEY_OR_NEXT): - if ( emit_key_part_name(&tmp, key_part) || - tmp.append(" >= ", 4) || + if (emit_key_part_name(&tmp, key_part) || + tmp.append(FEDERATED_GE) || emit_key_part_element(&tmp, key_part, needs_quotes, 0, ptr, - part_length)) + part_length)) DBUG_RETURN(1); break; case(HA_READ_BEFORE_KEY): if (store_length >= length) { if (emit_key_part_name(&tmp, key_part) || - tmp.append(" < ", 3) || - emit_key_part_element(&tmp, key_part, needs_quotes, 0, ptr, - part_length)) + tmp.append(FEDERATED_LT) || + emit_key_part_element(&tmp, key_part, needs_quotes, 0, ptr, + part_length)) DBUG_RETURN(1); break; } case(HA_READ_KEY_OR_PREV): - if ( emit_key_part_name(&tmp, key_part) || - tmp.append(" <= ", 4) || + if (emit_key_part_name(&tmp, key_part) || + tmp.append(FEDERATED_LE) || emit_key_part_element(&tmp, key_part, needs_quotes, 0, ptr, part_length)) DBUG_RETURN(1); @@ -1192,7 +1202,7 @@ bool ha_federated::create_where_from_key(String *to, DBUG_PRINT("info",("cannot handle flag %d", ranges[i]->flag)); DBUG_RETURN(1); } - if (tmp.append(")", 1)) + if (tmp.append(FEDERATED_CLOSEPAREN)) DBUG_RETURN(1); next_loop: @@ -1202,7 +1212,7 @@ next_loop: DBUG_ASSERT(remainder > 1); length-= store_length; ptr+= store_length; - if (tmp.append(" AND ", 5)) + if (tmp.append(FEDERATED_AND)) DBUG_RETURN(1); DBUG_PRINT("info", @@ -1211,9 +1221,10 @@ next_loop: } } if (both_not_null) - tmp.append(")", 1); + if (tmp.append(FEDERATED_CLOSEPAREN)) + DBUG_RETURN(1); - if (to->append(" WHERE ", 7)) + if (to->append(FEDERATED_WHERE)) DBUG_RETURN(1); if (to->append(tmp)) @@ -1251,15 +1262,17 @@ static FEDERATED_SHARE *get_share(const char *table_name, TABLE *table) strlen(table_name)))) { query.set_charset(system_charset_info); - query.append("SELECT ", 7); + query.append(FEDERATED_SELECT); for (field= table->field; *field; field++) { - query.append("`", 1); + query.append(FEDERATED_BTICK); query.append((*field)->field_name); - query.append("`,", 2); + query.append(FEDERATED_BTICK); + query.append(FEDERATED_COMMA); } - query.length(query.length()-1); - query.append(" FROM `", 7); + query.length(query.length()- strlen(FEDERATED_COMMA)); + query.append(FEDERATED_FROM); + query.append(FEDERATED_BTICK); if (!(share= (FEDERATED_SHARE *) @@ -1278,7 +1291,7 @@ static FEDERATED_SHARE *get_share(const char *table_name, TABLE *table) goto error; query.append(share->table_name, share->table_name_length); - query.append("`", 1); + query.append(FEDERATED_BTICK); share->select_query= select_query; strmov(share->select_query, query.ptr()); share->use_count= 0; @@ -1300,9 +1313,6 @@ error: pthread_mutex_unlock(&federated_mutex); if (share->scheme) { - DBUG_PRINT("info", - ("error: get_share. Freeing share->scheme %lx", - share->scheme)); my_free((gptr) share->scheme, MYF(0)); share->scheme= 0; } @@ -1325,9 +1335,6 @@ static int free_share(FEDERATED_SHARE *share) { if (share->scheme) { - DBUG_PRINT("info", - ("free_share: Freeing share->scheme %lx", - share->scheme)); my_free((gptr) share->scheme, MYF(0)); share->scheme= 0; } @@ -1347,86 +1354,14 @@ ha_rows ha_federated::records_in_range(uint inx, key_range *start_key, key_range *end_key) { /* - const byte *key; - uint length; - enum ha_rkey_function flag; - Note: - - records_in_range should sent to the remote an explain - 'select * from table where ...' start_key and end_key - - The job of this is to give a reasonable estimate of how many rows - a query will produce, not precise, just general to help the optimiser - choose an optimal query plan - this function MUST be QUICK! - - USE Explain to get row estimate. Do not use count. - - Should this method even be here? We really want indexes to be used as often - as possible, therefore it seems we just need to hard-code the return value - to a very low number + We really want indexes to be used as often as possible, therefore + we just need to hard-code the return value to a very low number to + force the issue */ -#ifdef CALC_RECORDS_IN_RANGE - char sql_query_buf[FEDERATED_QUERY_BUFFER_SIZE]; - int error= 0; - ha_rows return_rows= 20; - String sql_query(sql_query_buf, sizeof(sql_query_buf), &my_charset_bin); - MYSQL_RES *result= 0; - MYSQL_ROW row; -#endif DBUG_ENTER("ha_federated::records_in_range"); -#ifdef CALC_RECORDS_IN_RANGE - if (start_key == NULL && end_key == NULL) - DBUG_RETURN(0); - - sql_query.length(0); - sql_query.append("EXPLAIN ", 8); - sql_query.append(share->select_query); - - create_where_from_key(&sql_query, - &table->key_info[inx], - start_key, - end_key, - 1); - - DBUG_PRINT("info",("inx %d sql_query %s", inx, sql_query.c_ptr_quick())); - if (mysql_real_query(mysql, sql_query.ptr(), sql_query.length())) - goto error; - sql_query.length(0); - - result= mysql_store_result(mysql); - if (! result) - goto error; - - if (! mysql_num_rows(result)) - goto error; - if (!(row= mysql_fetch_row(result))) - goto error; - - if (! row[8]) - return_rows=0; - else - return_rows= (ha_rows) my_strtoll10(row[8], (char**) 0, &error); - - if (result) - { - mysql_free_result(result); - result= 0; - } - -error: - if (result) - { - mysql_free_result(result); - result= 0; - } - - DBUG_PRINT("info", ("return_rows (records) %d", return_rows)); - DBUG_RETURN(return_rows); -#else DBUG_RETURN(FEDERATED_RECORDS_IN_RANGE); -#endif } /* If frm_error() is called then we will use this to to find out @@ -1467,11 +1402,6 @@ int ha_federated::open(const char *name, int mode, uint test_if_locked) /* Connect to foreign database mysql_real_connect() */ mysql= mysql_init(0); - DBUG_PRINT("info", ("hostname %s", share->hostname)); - DBUG_PRINT("info", ("username %s", share->username)); - DBUG_PRINT("info", ("password %s", share->password)); - DBUG_PRINT("info", ("database %s", share->database)); - DBUG_PRINT("info", ("port %d", share->port)); if (!mysql_real_connect(mysql, share->hostname, share->username, @@ -1482,7 +1412,7 @@ int ha_federated::open(const char *name, int mode, uint test_if_locked) { int error_code; char error_buffer[FEDERATED_QUERY_BUFFER_SIZE]; - error_code= ER_CONNECT_TO_FOREIGN_DATA_SRC; + error_code= ER_CONNECT_TO_FOREIGN_DATA_SOURCE; my_sprintf(error_buffer, (error_buffer, ": %d : %s", mysql_errno(mysql), mysql_error(mysql))); my_error(error_code, MYF(0), error_buffer); @@ -1515,12 +1445,12 @@ int ha_federated::close(void) DBUG_ENTER("ha_federated::close"); /* free the result set */ - if (result) + if (stored_result) { DBUG_PRINT("info", - ("mysql_free_result result at address %lx", result)); - mysql_free_result(result); - result= 0; + ("mysql_free_result result at address %lx", stored_result)); + mysql_free_result(stored_result); + stored_result= 0; } /* Disconnect from mysql */ mysql_close(mysql); @@ -1582,12 +1512,10 @@ inline uint field_in_record_is_null(TABLE *table, int ha_federated::write_row(byte *buf) { - bool num_fields; - int error_code; + bool has_fields= FALSE; uint all_fields_have_same_query_id= 1; ulong current_query_id= 1; ulong tmp_query_id= 1; - char error_buffer[FEDERATED_QUERY_BUFFER_SIZE]; char insert_buffer[FEDERATED_QUERY_BUFFER_SIZE]; char values_buffer[FEDERATED_QUERY_BUFFER_SIZE]; char insert_field_value_buffer[STRING_BUFFER_USUAL_SIZE]; @@ -1623,13 +1551,17 @@ int ha_federated::write_row(byte *buf) current_query_id= table->in_use->query_id; DBUG_PRINT("info", ("current query id %d", current_query_id)); - /* start off our string */ - insert_string.append("INSERT INTO `", 13); + /* + start both our field and field values strings + */ + insert_string.append(FEDERATED_INSERT); + insert_string.append(FEDERATED_BTICK); insert_string.append(share->table_name, share->table_name_length); - insert_string.append("`",1); - /* start both our field and field values strings */ - insert_string.append(" (", 2); - values_string.append(" VALUES (", 9); + insert_string.append(FEDERATED_BTICK); + insert_string.append(FEDERATED_OPENPAREN); + + values_string.append(FEDERATED_VALUES); + values_string.append(FEDERATED_OPENPAREN); /* Even if one field is different, all_fields_same_query_id can't remain @@ -1648,7 +1580,7 @@ int ha_federated::write_row(byte *buf) loop through the field pointer array, add any fields to both the values list and the fields list that match the current query id - You might ask "Why an index variable (num_fields) ?" My answer is that + You might ask "Why an index variable (has_fields) ?" My answer is that we need to count how many fields we actually need */ for (field= table->field; *field; field++) @@ -1661,10 +1593,10 @@ int ha_federated::write_row(byte *buf) There are some fields. This will be used later to determine whether to chop off commas and parens. */ - num_fields= TRUE; + has_fields= TRUE; if ((*field)->is_null()) - insert_field_value_string.append("NULL", 4); + insert_field_value_string.append(FEDERATED_NULL); else { (*field)->val_str(&insert_field_value_string); @@ -1686,33 +1618,28 @@ int ha_federated::write_row(byte *buf) if the next field doesn't match the condition: (((*field)->query_id && (*field)->query_id == current_query_id) */ - insert_string.append(",",1); - values_string.append(",",1); - + insert_string.append(FEDERATED_COMMA); + values_string.append(FEDERATED_COMMA); } } /* - chop of the trailing comma, or if there were no fields, a '(' - So, "INSERT INTO foo (" becomes "INSERT INTO foo " - or, with fields, "INSERT INTO foo (field1, field2," becomes - "INSERT INTO foo (field1, field2" + remove trailing comma */ - insert_string.chop(); - + insert_string.length(insert_string.length() - strlen(FEDERATED_COMMA)); /* if there were no fields, we don't want to add a closing paren AND, we don't want to chop off the last char '(' insert will be "INSERT INTO t1 VALUES ();" */ - if (num_fields) + if (has_fields) { /* chops off leading commas */ - values_string.chop(); - insert_string.append(")", 1); + values_string.length(values_string.length() - strlen(FEDERATED_COMMA)); + insert_string.append(FEDERATED_CLOSEPAREN); } /* we always want to append this, even if there aren't any fields */ - values_string.append(")", 1); + values_string.append(FEDERATED_CLOSEPAREN); /* add the values */ insert_string.append(values_string); @@ -1721,7 +1648,9 @@ int ha_federated::write_row(byte *buf) if (mysql_real_query(mysql, insert_string.ptr(), insert_string.length())) { - error_code= ER_QUERY_ON_FOREIGN_DATA_SRC; + int error_code; + char error_buffer[FEDERATED_QUERY_BUFFER_SIZE]; + error_code= ER_QUERY_ON_FOREIGN_DATA_SOURCE; my_sprintf(error_buffer, (error_buffer, ": %d : %s", mysql_errno(mysql), mysql_error(mysql))); my_error(error_code, MYF(0), error_buffer); @@ -1797,10 +1726,11 @@ int ha_federated::update_row(const byte *old_data, byte *new_data) update_string.length(0); where_string.length(0); - update_string.append("UPDATE `", 8); + update_string.append(FEDERATED_UPDATE); + update_string.append(FEDERATED_BTICK); update_string.append(share->table_name); - update_string.append("`", 1); - update_string.append(" SET ", 5); + update_string.append(FEDERATED_BTICK); + update_string.append(FEDERATED_SET); /* In this loop, we want to match column names to values being inserted @@ -1816,10 +1746,10 @@ int ha_federated::update_row(const byte *old_data, byte *new_data) { where_string.append((*field)->field_name); update_string.append((*field)->field_name); - update_string.append(" = ", 3); + update_string.append(FEDERATED_EQ); if ((*field)->is_null()) - new_field_value.append("NULL", 4); + new_field_value.append(FEDERATED_NULL); else { /* otherwise = */ @@ -1827,11 +1757,11 @@ int ha_federated::update_row(const byte *old_data, byte *new_data) (*field)->quote_data(&new_field_value); if (!field_in_record_is_null(table, *field, (char*) old_data)) - where_string.append(" = ", 3); + where_string.append(FEDERATED_EQ); } if (field_in_record_is_null(table, *field, (char*) old_data)) - where_string.append(" IS NULL ", 9); + where_string.append(FEDERATED_ISNULL); else { uint o_len; @@ -1851,23 +1781,23 @@ int ha_federated::update_row(const byte *old_data, byte *new_data) */ if (*(field + 1)) { - update_string.append(", ", 2); - where_string.append(" AND ", 5); + update_string.append(FEDERATED_COMMA); + where_string.append(FEDERATED_AND); } old_field_value.length(0); } - update_string.append(" WHERE ", 7); + update_string.append(FEDERATED_WHERE); update_string.append(where_string); /* If this table has not a primary key, then we could possibly update multiple rows. We want to make sure to only update one! */ - if (! has_a_primary_key) - update_string.append(" LIMIT 1", 8); + if (!has_a_primary_key) + update_string.append(FEDERATED_LIMIT1); if (mysql_real_query(mysql, update_string.ptr(), update_string.length())) { - int error_code= ER_QUERY_ON_FOREIGN_DATA_SRC; + int error_code= ER_QUERY_ON_FOREIGN_DATA_SOURCE; char error_buffer[FEDERATED_QUERY_BUFFER_SIZE]; my_sprintf(error_buffer, (error_buffer, ": %d : %s", mysql_errno(mysql), mysql_error(mysql))); @@ -1894,7 +1824,6 @@ int ha_federated::update_row(const byte *old_data, byte *new_data) int ha_federated::delete_row(const byte *buf) { - char error_buffer[FEDERATED_QUERY_BUFFER_SIZE]; char delete_buffer[FEDERATED_QUERY_BUFFER_SIZE]; char data_buffer[FEDERATED_QUERY_BUFFER_SIZE]; @@ -1905,10 +1834,12 @@ int ha_federated::delete_row(const byte *buf) DBUG_ENTER("ha_federated::delete_row"); - delete_string.append("DELETE FROM `", 13); + delete_string.append(FEDERATED_DELETE); + delete_string.append(FEDERATED_FROM); + delete_string.append(FEDERATED_BTICK); delete_string.append(share->table_name); - delete_string.append("`", 1); - delete_string.append(" WHERE ", 7); + delete_string.append(FEDERATED_BTICK); + delete_string.append(FEDERATED_WHERE); for (Field **field= table->field; *field; field++) { @@ -1916,12 +1847,12 @@ int ha_federated::delete_row(const byte *buf) if ((*field)->is_null()) { - delete_string.append(" IS ", 4); - data_string.append("NULL", 4); + delete_string.append(FEDERATED_IS); + data_string.append(FEDERATED_NULL); } else { - delete_string.append(" = ", 3); + delete_string.append(FEDERATED_EQ); (*field)->val_str(&data_string); (*field)->quote_data(&data_string); } @@ -1930,15 +1861,15 @@ int ha_federated::delete_row(const byte *buf) data_string.length(0); if (*(field + 1)) - delete_string.append(" AND ", 5); + delete_string.append(FEDERATED_AND); } - delete_string.append(" LIMIT 1", 8); + delete_string.append(FEDERATED_LIMIT1); DBUG_PRINT("info", ("Delete sql: %s", delete_string.c_ptr_quick())); if (mysql_real_query(mysql, delete_string.ptr(), delete_string.length())) { - int error_code= ER_QUERY_ON_FOREIGN_DATA_SRC; + int error_code= ER_QUERY_ON_FOREIGN_DATA_SOURCE; char error_buffer[FEDERATED_QUERY_BUFFER_SIZE]; my_error(error_code, MYF(0), error_buffer); DBUG_RETURN(error_code); @@ -2011,7 +1942,7 @@ int ha_federated::index_read_idx(byte *buf, uint index, const byte *key, create_where_from_key(&index_string, &table->key_info[index], &range, - NULL, NULL); + NULL, 0); sql_query.append(index_string); DBUG_PRINT("info", @@ -2023,21 +1954,21 @@ int ha_federated::index_read_idx(byte *buf, uint index, const byte *key, ("current position %d sql_query %s", current_position, sql_query.c_ptr_quick())); - if (result) + if (stored_result) { - mysql_free_result(result); - result= 0; + mysql_free_result(stored_result); + stored_result= 0; } if (mysql_real_query(mysql, sql_query.ptr(), sql_query.length())) { my_sprintf(error_buffer, (error_buffer, ": %d : %s", - mysql_errno(mysql), mysql_error(mysql))); - retval= ER_QUERY_ON_FOREIGN_DATA_SRC; + mysql_errno(mysql), mysql_error(mysql))); + retval= ER_QUERY_ON_FOREIGN_DATA_SOURCE; goto error; } - result= mysql_store_result(mysql); + stored_result= mysql_store_result(mysql); - if (!result) + if (!stored_result) { retval= HA_ERR_END_OF_FILE; goto error; @@ -2047,16 +1978,16 @@ int ha_federated::index_read_idx(byte *buf, uint index, const byte *key, and that it is ok to use this record, for whatever reason, such as with a join (without it, joins will not work) */ - table->status=0; + table->status= 0; retval= rnd_next(buf); DBUG_RETURN(retval); error: - if (result) + if (stored_result) { - mysql_free_result(result); - result= 0; + mysql_free_result(stored_result); + stored_result= 0; } table->status= STATUS_NOT_FOUND; my_error(retval, MYF(0), error_buffer); @@ -2098,43 +2029,43 @@ int ha_federated::read_range_first(const key_range *start_key, sql_query.append(share->select_query); create_where_from_key(&sql_query, &table->key_info[active_index], - start_key, end_key, NULL); + start_key, end_key, 0); if (mysql_real_query(mysql, sql_query.ptr(), sql_query.length())) { - retval= ER_QUERY_ON_FOREIGN_DATA_SRC; + retval= ER_QUERY_ON_FOREIGN_DATA_SOURCE; goto error; } sql_query.length(0); - if (result) + if (stored_result) { DBUG_PRINT("info", - ("mysql_free_result address %lx", result)); - mysql_free_result(result); - result= 0; + ("mysql_free_result address %lx", stored_result)); + mysql_free_result(stored_result); + stored_result= 0; } - result= mysql_store_result(mysql); + stored_result= mysql_store_result(mysql); - if (!result) + if (!stored_result) { retval= HA_ERR_END_OF_FILE; goto error; } /* This was successful, please let it be known! */ - table->status=0; + table->status= 0; retval= rnd_next(table->record[0]); DBUG_RETURN(retval); error: table->status= STATUS_NOT_FOUND; - if (result) + if (stored_result) { - DBUG_PRINT("info", ("mysql_free_result address %lx", result)); - mysql_free_result(result); - result= 0; + DBUG_PRINT("info", ("mysql_free_result address %lx", stored_result)); + mysql_free_result(stored_result); + stored_result= 0; } DBUG_RETURN(retval); } @@ -2216,12 +2147,12 @@ int ha_federated::rnd_init(bool scan) if (scan) { DBUG_PRINT("info", ("share->select_query %s", share->select_query)); - if (result) + if (stored_result) { DBUG_PRINT("info", - ("mysql_free_result address %lx", result)); - mysql_free_result(result); - result= 0; + ("mysql_free_result address %lx", stored_result)); + mysql_free_result(stored_result); + stored_result= 0; } if (mysql_real_query(mysql, @@ -2229,14 +2160,14 @@ int ha_federated::rnd_init(bool scan) strlen(share->select_query))) goto error; - result= mysql_store_result(mysql); - if (!result) + stored_result= mysql_store_result(mysql); + if (!stored_result) goto error; } DBUG_RETURN(0); error: - retval= ER_QUERY_ON_FOREIGN_DATA_SRC; + retval= ER_QUERY_ON_FOREIGN_DATA_SOURCE; my_sprintf(error_buffer, (error_buffer, ": %d : %s", mysql_errno(mysql), mysql_error(mysql))); my_error(retval, MYF(0), error_buffer); @@ -2250,11 +2181,11 @@ int ha_federated::rnd_end() int retval; DBUG_ENTER("ha_federated::rnd_end"); - if (result) + if (stored_result) { - DBUG_PRINT("info", ("mysql_free_result address %lx", result)); - mysql_free_result(result); - result= 0; + DBUG_PRINT("info", ("mysql_free_result address %lx", stored_result)); + mysql_free_result(stored_result); + stored_result= 0; } retval= index_end(); DBUG_RETURN(retval); @@ -2283,7 +2214,7 @@ int ha_federated::rnd_next(byte *buf) MYSQL_ROW row; DBUG_ENTER("ha_federated::rnd_next"); - if (result == 0) + if (stored_result == 0) { /* Return value of rnd_init is not always checked (see records.cc), @@ -2294,9 +2225,9 @@ int ha_federated::rnd_next(byte *buf) } /* Fetch a row, insert it back in a row format. */ - current_position= result->data_cursor; + current_position= stored_result->data_cursor; DBUG_PRINT("info", ("current position %d", current_position)); - if (!(row= mysql_fetch_row(result))) + if (!(row= mysql_fetch_row(stored_result))) DBUG_RETURN(HA_ERR_END_OF_FILE); retval= convert_row_to_internal_format(buf, row); @@ -2352,8 +2283,8 @@ int ha_federated::rnd_pos(byte *buf, byte *pos) &LOCK_status); memcpy_fixed(¤t_position, pos, sizeof(MYSQL_ROW_OFFSET)); // pos /* is not aligned */ - result->current_row= 0; - result->data_cursor= current_position; + stored_result->current_row= 0; + stored_result->data_cursor= current_position; retval= rnd_next(buf); DBUG_RETURN(retval); } @@ -2412,25 +2343,26 @@ void ha_federated::info(uint flag) char escaped_table_name[FEDERATED_QUERY_BUFFER_SIZE]; int error; uint error_code; - MYSQL_RES *result=0; + MYSQL_RES *result= 0; MYSQL_ROW row; String status_query_string(status_buf, sizeof(status_buf), &my_charset_bin); DBUG_ENTER("ha_federated::info"); - error_code= ER_QUERY_ON_FOREIGN_DATA_SRC; + error_code= ER_QUERY_ON_FOREIGN_DATA_SOURCE; /* we want not to show table status if not needed to do so */ if (flag & (HA_STATUS_VARIABLE | HA_STATUS_CONST)) { status_query_string.length(0); - status_query_string.append("SHOW TABLE STATUS LIKE '", 24); + status_query_string.append(FEDERATED_INFO); + status_query_string.append(FEDERATED_SQUOTE); escape_string_for_mysql(&my_charset_bin, (char *)escaped_table_name, sizeof(escaped_table_name), share->table_name, share->table_name_length); status_query_string.append(escaped_table_name); - status_query_string.append("'"); + status_query_string.append(FEDERATED_SQUOTE); if (mysql_real_query(mysql, status_query_string.ptr(), status_query_string.length())) @@ -2439,10 +2371,10 @@ void ha_federated::info(uint flag) status_query_string.length(0); result= mysql_store_result(mysql); - if (! result) + if (!result) goto error; - if (! mysql_num_rows(result)) + if (!mysql_num_rows(result)) goto error; if (!(row= mysql_fetch_row(result))) @@ -2478,22 +2410,15 @@ void ha_federated::info(uint flag) } if (result) - { mysql_free_result(result); - result= 0; - } + DBUG_VOID_RETURN; error: if (result) - { mysql_free_result(result); - result= 0; - } - my_sprintf(error_buffer, - (error_buffer, - ": %d : %s", - mysql_errno(mysql), mysql_error(mysql))); + my_sprintf(error_buffer, (error_buffer, ": %d : %s", + mysql_errno(mysql), mysql_error(mysql))); my_error(error_code, MYF(0), error_buffer); DBUG_VOID_RETURN; } @@ -2520,9 +2445,10 @@ int ha_federated::delete_all_rows() query.length(0); query.set_charset(system_charset_info); - query.append("TRUNCATE `", 10); + query.append(FEDERATED_TRUNCATE); + query.append(FEDERATED_BTICK); query.append(share->table_name); - query.append("`", 1); + query.append(FEDERATED_BTICK); /* TRUNCATE won't return anything in mysql_affected_rows @@ -2530,12 +2456,10 @@ int ha_federated::delete_all_rows() deleted+= records; if (mysql_real_query(mysql, query.ptr(), query.length())) { - int error_code= ER_QUERY_ON_FOREIGN_DATA_SRC; + int error_code= ER_QUERY_ON_FOREIGN_DATA_SOURCE; char error_buffer[FEDERATED_QUERY_BUFFER_SIZE]; - my_sprintf(error_buffer, - (error_buffer, - ": %d : %s", - mysql_errno(mysql), mysql_error(mysql))); + my_sprintf(error_buffer, (error_buffer, ": %d : %s", + mysql_errno(mysql), mysql_error(mysql))); my_error(error_code, MYF(0), error_buffer); DBUG_RETURN(error_code); } @@ -2632,25 +2556,17 @@ int ha_federated::create(const char *name, TABLE *table_arg, if (tmp_share.scheme) { - DBUG_PRINT("info", - ("ha_federated::create. Freeing tmp_share.scheme %lx", - tmp_share.scheme)); my_free((gptr) tmp_share.scheme, MYF(0)); tmp_share.scheme= 0; } - DBUG_PRINT("info", ("no errors, returning %d", retval)); DBUG_RETURN(retval); error: if (tmp_share.scheme) { - DBUG_PRINT("info", - ("error in ha_federated::create. Freeing tmp_share.scheme %lx", - tmp_share.scheme)); my_free((gptr) tmp_share.scheme, MYF(0)); tmp_share.scheme= 0; } - DBUG_PRINT("info", ("errors, returning %d", retval)); DBUG_RETURN(retval); } diff --git a/sql/ha_federated.h b/sql/ha_federated.h index e8bebe81768..a12cf14531f 100644 --- a/sql/ha_federated.h +++ b/sql/ha_federated.h @@ -30,6 +30,42 @@ #define FEDERATED_QUERY_BUFFER_SIZE STRING_BUFFER_USUAL_SIZE * 5 #define FEDERATED_RECORDS_IN_RANGE 2 +#define FEDERATED_INFO " SHOW TABLE STATUS LIKE " +#define FEDERATED_SELECT "SELECT " +#define FEDERATED_WHERE " WHERE " +#define FEDERATED_FROM " FROM " +#define FEDERATED_PERCENT "%" +#define FEDERATED_IS " IS " +#define FEDERATED_NULL " NULL " +#define FEDERATED_ISNULL " IS NULL " +#define FEDERATED_LIKE " LIKE " +#define FEDERATED_TRUNCATE "TRUNCATE " +#define FEDERATED_DELETE "DELETE " +#define FEDERATED_INSERT "INSERT INTO " +#define FEDERATED_LIMIT1 " LIMIT 1" +#define FEDERATED_VALUES "VALUES " +#define FEDERATED_UPDATE "UPDATE " +#define FEDERATED_SET "SET " +#define FEDERATED_AND " AND " +#define FEDERATED_CONJUNCTION ") AND (" +#define FEDERATED_OR " OR " +#define FEDERATED_NOT " NOT " +#define FEDERATED_STAR "* " +#define FEDERATED_SPACE " " +#define FEDERATED_SQUOTE "'" +#define FEDERATED_COMMA ", " +#define FEDERATED_DQOUTE '"' +#define FEDERATED_BTICK "`" +#define FEDERATED_OPENPAREN " (" +#define FEDERATED_CLOSEPAREN ") " +#define FEDERATED_NE " != " +#define FEDERATED_GT " > " +#define FEDERATED_LT " < " +#define FEDERATED_LE " <= " +#define FEDERATED_GE " >= " +#define FEDERATED_EQ " = " +#define FEDERATED_1EQ0 " 1=0" + /* FEDERATED_SHARE is a structure that will be shared amoung all open handlers The example implements the minimum of what you will probably need. @@ -65,7 +101,7 @@ class ha_federated: public handler THR_LOCK_DATA lock; /* MySQL lock */ FEDERATED_SHARE *share; /* Shared lock info */ MYSQL *mysql; /* MySQL connection */ - MYSQL_RES *result; + MYSQL_RES *stored_result; bool scan_flag; uint ref_length; uint fetch_num; // stores the fetch num @@ -84,7 +120,7 @@ private: public: ha_federated(TABLE *table): handler(table), - mysql(0), result(0), scan_flag(0), + mysql(0), stored_result(0), scan_flag(0), ref_length(sizeof(MYSQL_ROW_OFFSET)), current_position(0) { } @@ -145,13 +181,18 @@ public: double scan_time() { DBUG_PRINT("info", - ("records %d", records)); return (double)(records*1000); + ("records %d", records)); + return (double)(records*1000); } /* The next method will never be called if you do not implement indexes. */ double read_time(uint index, uint ranges, ha_rows rows) { + /* + Per Brian, this number is bugus, but this method must be implemented, + and at a later date, he intends to document this issue for handler code + */ return (double) rows / 20.0+1; } @@ -190,7 +231,7 @@ public: int rnd_init(bool scan); //required int rnd_end(); int rnd_next(byte *buf); //required - int rnd_pos(byte *buf, byte *pos); //required + int rnd_pos(byte *buf, byte *pos); //required void position(const byte *record); //required void info(uint); //required diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index 011a9b1cf91..489b5ef2fdc 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5370,11 +5370,11 @@ ER_SCALE_BIGGER_THAN_PRECISION 42000 S1009 eng "Scale may not be larger than the precision (column '%-.64s')." ER_WRONG_LOCK_OF_SYSTEM_TABLE eng "You can't combine write-locking of system '%-.64s.%-.64s' table with other tables" -ER_CONNECT_TO_FOREIGN_DATA_SRC +ER_CONNECT_TO_FOREIGN_DATA_SOURCE eng "Unable to connect to foreign data source - database '%s'!" -ER_QUERY_ON_FOREIGN_DATA_SRC +ER_QUERY_ON_FOREIGN_DATA_SOURCE eng "There was a problem processing the query on the foreign data source. Data source error: '%-.64s'" -ER_FOREIGN_DATA_SRC_DOESNT_EXIST +ER_FOREIGN_DATA_SOURCE_DOESNT_EXIST eng "The foreign data source you are trying to reference does not exist. Data source error : '%-.64s'" ER_FOREIGN_DATA_STRING_INVALID_CANT_CREATE eng "Can't create federated table. The data source connection string '%-.64s' is not in the correct format" From 54dd7e5ebf68b2a752c39791744ef8245a58594c Mon Sep 17 00:00:00 2001 From: "igor@rurik.mysql.com" <> Date: Sun, 17 Jul 2005 09:46:14 -0700 Subject: [PATCH 26/48] select.result, select.test: Added a test case for bug #11745. sql_select.cc: Fixed bug # 11745. Added support of where clause for queries with FROM DUAL. sql_yacc.yy: Fixed bug # 11745. Added optional where clause for queries with FROM DUAL. --- mysql-test/r/select.result | 16 ++++++++++++++++ mysql-test/t/select.test | 18 ++++++++++++++++++ sql/sql_select.cc | 2 +- sql/sql_yacc.yy | 2 +- 4 files changed, 36 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result index 6c9ec9d8297..2df60944999 100644 --- a/mysql-test/r/select.result +++ b/mysql-test/r/select.result @@ -2582,3 +2582,19 @@ a 254 255 drop table t2; +CREATE TABLE t1 (a int, b int, c int); +INSERT INTO t1 +SELECT 50, 3, 3 FROM DUAL +WHERE NOT EXISTS +(SELECT * FROM t1 WHERE a = 50 AND b = 3); +SELECT * FROM t1; +a b c +50 3 3 +INSERT INTO t1 +SELECT 50, 3, 3 FROM DUAL +WHERE NOT EXISTS +(SELECT * FROM t1 WHERE a = 50 AND b = 3); +SELECT * FROM t1; +a b c +50 3 3 +DROP TABLE t1; diff --git a/mysql-test/t/select.test b/mysql-test/t/select.test index caa9e332e57..893b9ba9267 100644 --- a/mysql-test/t/select.test +++ b/mysql-test/t/select.test @@ -2138,3 +2138,21 @@ insert into t2 values (0), (254), (255); explain select * from t2 where a > -1; select * from t2 where a > -1; drop table t2; + +# +# Bug #11745: SELECT ... FROM DUAL with WHERE condition +# + +CREATE TABLE t1 (a int, b int, c int); +INSERT INTO t1 + SELECT 50, 3, 3 FROM DUAL + WHERE NOT EXISTS + (SELECT * FROM t1 WHERE a = 50 AND b = 3); +SELECT * FROM t1; +INSERT INTO t1 + SELECT 50, 3, 3 FROM DUAL + WHERE NOT EXISTS + (SELECT * FROM t1 WHERE a = 50 AND b = 3); +SELECT * FROM t1; + +DROP TABLE t1; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index a70fa1c8d5e..5b4eab36dd2 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1072,7 +1072,7 @@ JOIN::exec() else { result->send_fields(fields_list,1); - if (!having || having->val_int()) + if (cond_value != Item::COND_FALSE && (!having || having->val_int())) { if (do_send_rows && (procedure ? (procedure->send_row(fields_list) || procedure->end_of_records()) diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 2587ce7e1a5..be8ead8e157 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -2459,7 +2459,7 @@ select_into: select_from: FROM join_table_list where_clause group_clause having_clause opt_order_clause opt_limit_clause procedure_clause - | FROM DUAL_SYM opt_limit_clause + | FROM DUAL_SYM where_clause opt_limit_clause /* oracle compatibility: oracle always requires FROM clause, and DUAL is system table without fields. Is "SELECT 1 FROM DUAL" any better than "SELECT 1" ? From b323a77375d6b65de4ef158b7dbc4d59fca85fc9 Mon Sep 17 00:00:00 2001 From: "joerg@mysql.com" <> Date: Mon, 18 Jul 2005 13:52:18 +0200 Subject: [PATCH 27/48] QNX does not know "uint", so the cast is rewritten as "unsigned int". --- cmd-line-utils/libedit/read.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-line-utils/libedit/read.c b/cmd-line-utils/libedit/read.c index 40093d6647f..051f3e8e42e 100644 --- a/cmd-line-utils/libedit/read.c +++ b/cmd-line-utils/libedit/read.c @@ -478,7 +478,7 @@ el_gets(EditLine *el, int *nread) #endif /* DEBUG_READ */ break; } - if ((uint)cmdnum >= el->el_map.nfunc) { /* BUG CHECK command */ + if ((unsigned int)cmdnum >= el->el_map.nfunc) { /* BUG CHECK command */ #ifdef DEBUG_EDIT (void) fprintf(el->el_errfile, "ERROR: illegal command from key 0%o\r\n", ch); From 78c65b5adc2b5a7160820ceed8273b684cc1e8e8 Mon Sep 17 00:00:00 2001 From: "monty@mishka.local" <> Date: Mon, 18 Jul 2005 15:33:18 +0300 Subject: [PATCH 28/48] Cleanups during review Changed defaults option --instance to --defaults-group-suffix Changed option handling to allow --defaults-file, --defaults-extra-file and --defaults-group-suffix to be given in any order Changed MYSQL_INSTANCE to MYSQL_GROUP_SUFFIX mysql_print_defaults now understands --defaults-group-suffix Remove usage of my_tempnam() (not safe function) if( -> if ( and while( to while ( --- VC++Files/client/mysqlclient.dsp | 4 - VC++Files/client/mysqlclient_ia64.dsp | 4 - VC++Files/libmysql/libmysql.dsp | 4 - VC++Files/libmysql/libmysql_ia64.dsp | 4 - VC++Files/mysys/mysys.dsp | 4 - VC++Files/mysys/mysys_ia64.dsp | 4 - client/mysql.cc | 26 ++-- client/mysqldump.c | 13 +- extra/my_print_defaults.c | 46 ++++--- extra/replace.c | 21 +-- include/config-win.h | 3 + include/my_sys.h | 7 +- libmysql/Makefile.shared | 1 + mysys/Makefile.am | 3 +- mysys/default.c | 190 +++++++++++++++----------- mysys/default_modify.c | 65 ++++----- mysys/my_bitmap.c | 1 + mysys/my_tempnam.c | 173 ----------------------- sql/ha_innodb.cc | 6 +- sql/ha_ndbcluster.cc | 4 +- sql/item_cmpfunc.cc | 8 +- sql/item_strfunc.cc | 2 +- sql/log.cc | 2 +- sql/mysqld.cc | 2 +- sql/opt_range.cc | 18 +-- sql/parse_file.cc | 2 +- sql/sql_cache.cc | 2 +- sql/sql_parse.cc | 2 +- sql/sql_prepare.cc | 6 +- sql/sql_select.cc | 11 +- sql/sql_show.cc | 2 +- sql/sql_yacc.yy | 2 +- tests/mysql_client_test.c | 142 +++++++++---------- 33 files changed, 315 insertions(+), 469 deletions(-) delete mode 100644 mysys/my_tempnam.c diff --git a/VC++Files/client/mysqlclient.dsp b/VC++Files/client/mysqlclient.dsp index c0d891b1c9a..5499379be8d 100644 --- a/VC++Files/client/mysqlclient.dsp +++ b/VC++Files/client/mysqlclient.dsp @@ -451,10 +451,6 @@ SOURCE=..\mysys\my_symlink2.c # End Source File # Begin Source File -SOURCE=..\mysys\my_tempnam.c -# End Source File -# Begin Source File - SOURCE=..\libmysql\my_time.c # End Source File # Begin Source File diff --git a/VC++Files/client/mysqlclient_ia64.dsp b/VC++Files/client/mysqlclient_ia64.dsp index e38f37d61e6..b982b3b281c 100644 --- a/VC++Files/client/mysqlclient_ia64.dsp +++ b/VC++Files/client/mysqlclient_ia64.dsp @@ -436,10 +436,6 @@ SOURCE=..\mysys\my_symlink2.c # End Source File # Begin Source File -SOURCE=..\mysys\my_tempnam.c -# End Source File -# Begin Source File - SOURCE=..\mysys\my_thr_init.c # End Source File # Begin Source File diff --git a/VC++Files/libmysql/libmysql.dsp b/VC++Files/libmysql/libmysql.dsp index 356aac3563f..883557e7b63 100644 --- a/VC++Files/libmysql/libmysql.dsp +++ b/VC++Files/libmysql/libmysql.dsp @@ -419,10 +419,6 @@ SOURCE=..\mysys\my_symlink2.c # End Source File # Begin Source File -SOURCE=..\mysys\my_tempnam.c -# End Source File -# Begin Source File - SOURCE=..\mysys\my_thr_init.c # End Source File # Begin Source File diff --git a/VC++Files/libmysql/libmysql_ia64.dsp b/VC++Files/libmysql/libmysql_ia64.dsp index 75586ef2cf6..717125d5497 100644 --- a/VC++Files/libmysql/libmysql_ia64.dsp +++ b/VC++Files/libmysql/libmysql_ia64.dsp @@ -406,10 +406,6 @@ SOURCE=..\mysys\my_symlink2.c # End Source File # Begin Source File -SOURCE=..\mysys\my_tempnam.c -# End Source File -# Begin Source File - SOURCE=..\mysys\my_thr_init.c # End Source File # Begin Source File diff --git a/VC++Files/mysys/mysys.dsp b/VC++Files/mysys/mysys.dsp index ab5077810b2..e239318d54d 100644 --- a/VC++Files/mysys/mysys.dsp +++ b/VC++Files/mysys/mysys.dsp @@ -533,10 +533,6 @@ SOURCE=.\my_sync.c # End Source File # Begin Source File -SOURCE=.\my_tempnam.c -# End Source File -# Begin Source File - SOURCE=.\my_thr_init.c # End Source File # Begin Source File diff --git a/VC++Files/mysys/mysys_ia64.dsp b/VC++Files/mysys/mysys_ia64.dsp index a0877457286..b0ce2b4c579 100644 --- a/VC++Files/mysys/mysys_ia64.dsp +++ b/VC++Files/mysys/mysys_ia64.dsp @@ -526,10 +526,6 @@ SOURCE=.\my_sync.c # End Source File # Begin Source File -SOURCE=.\my_tempnam.c -# End Source File -# Begin Source File - SOURCE=.\my_thr_init.c # End Source File # Begin Source File diff --git a/client/mysql.cc b/client/mysql.cc index f98bf467588..871936d7800 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -44,7 +44,7 @@ #include #endif -const char *VER= "14.11"; +const char *VER= "14.12"; /* Don't try to make a nice table if the data is too big */ #define MAX_COLUMN_LENGTH 1024 @@ -340,16 +340,15 @@ static sig_handler mysql_end(int sig); int main(int argc,char *argv[]) { char buff[80]; - char *defaults, *extra_defaults; - char *emb_argv[3]; - int emb_argc= 1; + char *defaults, *extra_defaults, *group_suffix; + char *emb_argv[4]; + int emb_argc; - emb_argv[0]= argv[0]; - get_defaults_files(argc, argv, &defaults, &extra_defaults); - if (defaults) - emb_argv[emb_argc++]= defaults; - if (extra_defaults) - emb_argv[emb_argc++]= extra_defaults; + /* Get --defaults-xxx args for mysql_server_init() */ + emb_argc= get_defaults_options(argc, argv, &defaults, &extra_defaults, + &group_suffix)+1; + memcpy((char*) emb_argv, (char*) argv, emb_argc * sizeof(*argv)); + emb_argv[emb_argc]= 0; MY_INIT(argv[0]); DBUG_ENTER("main"); @@ -2060,6 +2059,7 @@ static void end_tee() return; } + static int com_ego(String *buffer,char *line) { @@ -2071,8 +2071,10 @@ com_ego(String *buffer,char *line) return result; } -static char *fieldtype2str(enum enum_field_types type) { - switch(type) { + +static const char *fieldtype2str(enum enum_field_types type) +{ + switch (type) { case FIELD_TYPE_BIT: return "BIT"; case FIELD_TYPE_BLOB: return "BLOB"; case FIELD_TYPE_DATE: return "DATE"; diff --git a/client/mysqldump.c b/client/mysqldump.c index b2bf23c3e3a..28da11f5f9a 100644 --- a/client/mysqldump.c +++ b/client/mysqldump.c @@ -1212,7 +1212,7 @@ static uint get_table_structure(char *table, char *db) opt_quoted_table= quote_name(table, table_buff2, 0); if (opt_order_by_primary) - order_by = primary_key_fields(opt_quoted_table); + order_by = primary_key_fields(result_table); if (!opt_xml && !mysql_query_with_error_report(sock, 0, query_buff)) { @@ -1272,7 +1272,7 @@ static uint get_table_structure(char *table, char *db) /* Create temp table by selecting from the view */ my_snprintf(query_buff, sizeof(query_buff), - "CREATE TEMPORARY TABLE %s SELECT * FROM %s WHERE 0", + "CREATE TEMPORARY TABLE %s SELECT * FROM %s WHERE 0", result_table, result_table); if (mysql_query_with_error_report(sock, 0, query_buff)) { @@ -1391,7 +1391,7 @@ static uint get_table_structure(char *table, char *db) fprintf(sql_file, "\n--\n-- Table structure for table %s\n--\n\n", result_table); if (opt_drop) - fprintf(sql_file, "DROP TABLE IF EXISTS %s;\n",result_table); + fprintf(sql_file, "DROP TABLE IF EXISTS %s;\n", result_table); if (!opt_xml) fprintf(sql_file, "CREATE TABLE %s (\n", result_table); else @@ -2773,6 +2773,7 @@ static const char *check_if_ignore_table(const char *table_name) or if there is some failure. It is better to continue to dump the table unsorted, rather than exit without dumping the data. */ + static char *primary_key_fields(const char *table_name) { MYSQL_RES *res = NULL; @@ -2809,11 +2810,13 @@ static char *primary_key_fields(const char *table_name) } /* Build the ORDER BY clause result */ - if (result_length) { + if (result_length) + { char *end; /* result (terminating \0 is already in result_length) */ result = my_malloc(result_length + 10, MYF(MY_WME)); - if (!result) { + if (!result) + { fprintf(stderr, "Error: Not enough memory to store ORDER BY clause\n"); goto cleanup; } diff --git a/extra/my_print_defaults.c b/extra/my_print_defaults.c index 946ac219e1a..916203bc7d7 100644 --- a/extra/my_print_defaults.c +++ b/extra/my_print_defaults.c @@ -1,3 +1,4 @@ + /* Copyright (C) 2000 MySQL AB This program is free software; you can redistribute it and/or modify @@ -23,8 +24,10 @@ #include #include +#include #include + const char *config_file="my"; /* Default config file */ uint verbose= 0, opt_defaults_file_used= 0; const char *default_dbug_option="d:t:o,/tmp/my_print_defaults.trace"; @@ -48,6 +51,10 @@ static struct my_option my_long_options[] = "Read this file after the global /etc config file and before the config file in the users home directory.", (gptr*) &defaults_extra_file, (gptr*) &defaults_extra_file, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"defaults-group-suffix", 'g', + "In addition to the given groups, read also groups with this suffix", + (gptr*) &defaults_group_suffix, (gptr*) &defaults_group_suffix, + 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"extra-file", 'e', "Synonym for --defaults-extra-file.", (gptr*) &defaults_extra_file, (gptr*) &defaults_extra_file, 0, GET_STR, @@ -127,37 +134,32 @@ static int get_options(int *argc,char ***argv) return 0; } + int main(int argc, char **argv) { - int count, error; - char **load_default_groups, *tmp_arguments[3], - **argument, **arguments; - char *defaults, *extra_defaults; + int count, error, args_used; + char **load_default_groups, *tmp_arguments[6]; + char **argument, **arguments, **org_argv; + char *defaults, *extra_defaults, *group_suffix; MY_INIT(argv[0]); - get_defaults_files(argc, argv, &defaults, &extra_defaults); + org_argv= argv; + args_used= get_defaults_options(argc, argv, &defaults, &extra_defaults, + &group_suffix); - /* - ** Check out the args - */ - if (!(load_default_groups=(char**) my_malloc((argc+2)*sizeof(char*), + /* Copy defaults-xxx arguments & program name */ + count=args_used+1; + arguments= tmp_arguments; + memcpy((char*) arguments, (char*) org_argv, count * sizeof(*org_argv)); + arguments[count]= 0; + + /* Check out the args */ + if (!(load_default_groups=(char**) my_malloc((argc+1)*sizeof(char*), MYF(MY_WME)))) exit(1); if (get_options(&argc,&argv)) exit(1); - - for (count=0; *argv ; argv++,count++) - load_default_groups[count]= *argv; - load_default_groups[count]=0; - - count=0; - arguments=tmp_arguments; - arguments[count++]=my_progname; - if (extra_defaults) - arguments[count++]= extra_defaults; - if (defaults) - arguments[count++]= defaults; - arguments[count]= 0; + memcpy((char*) load_default_groups, (char*) argv, (argc + 1) * sizeof(*argv)); if ((error= load_defaults(config_file, (const char **) load_default_groups, &count, &arguments))) diff --git a/extra/replace.c b/extra/replace.c index d92355359d3..0b7d9600232 100644 --- a/extra/replace.c +++ b/extra/replace.c @@ -175,7 +175,7 @@ register char **argv[]; case 'I': case '?': help=1; /* Help text written */ - printf("%s Ver 1.3 for %s at %s\n",my_progname,SYSTEM_TYPE, + printf("%s Ver 1.4 for %s at %s\n",my_progname,SYSTEM_TYPE, MACHINE_TYPE); if (version) break; @@ -1048,23 +1048,25 @@ FILE *in,*out; } -static int convert_file(rep,name) -REPLACE *rep; -my_string name; +static int convert_file(REPLACE *rep, my_string name) { int error; FILE *in,*out; - char dir_buff[FN_REFLEN],*tempname; + char dir_buff[FN_REFLEN], tempname[FN_REFLEN]; + File temp_file; DBUG_ENTER("convert_file"); if (!(in=my_fopen(name,O_RDONLY,MYF(MY_WME)))) DBUG_RETURN(1); dirname_part(dir_buff,name); - tempname=my_tempnam(dir_buff,"PR",MYF(MY_WME)); - if (!(out=my_fopen(tempname,(int) (O_WRONLY | O_CREAT), - MYF(MY_WME)))) + if ((temp_file= create_temp_file(tempname, dir_buff, "PR", O_WRONLY, + MYF(MY_WME))) < 0) + { + my_fclose(in,MYF(0)); + DBUG_RETURN(1); + } + if (!(out= my_fdopen(temp_file, tempname, O_WRONLY, MYF(MY_WME)))) { - (*free)(tempname); my_fclose(in,MYF(0)); DBUG_RETURN(1); } @@ -1076,7 +1078,6 @@ my_string name; my_redel(name,tempname,MYF(MY_WME | MY_LINK_WARNING)); else my_delete(tempname,MYF(MY_WME)); - (*free)(tempname); if (!silent && ! error) { if (updated) diff --git a/include/config-win.h b/include/config-win.h index 2559b3b74fd..6e0740497b0 100644 --- a/include/config-win.h +++ b/include/config-win.h @@ -353,6 +353,9 @@ inline double ulonglong2double(ulonglong value) #ifndef DEFAULT_HOME_ENV #define DEFAULT_HOME_ENV MYSQL_HOME #endif +#ifndef DEFAULT_GROUP_SUFFIX_ENV +#define DEFAULT_GROUP_SUFFIX_ENV MYSQL_GROUP_SUFFIX +#endif /* File name handling */ diff --git a/include/my_sys.h b/include/my_sys.h index a3d6f6827d3..a7304b8c952 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -263,7 +263,7 @@ extern my_bool NEAR my_disable_locking,NEAR my_disable_async_io, extern char wild_many,wild_one,wild_prefix; extern const char *charsets_dir; extern char *defaults_extra_file; -extern const char *defaults_instance; +extern const char *defaults_group_suffix; extern my_bool timed_mutexes; @@ -785,8 +785,9 @@ extern void reset_root_defaults(MEM_ROOT *mem_root, uint block_size, extern char *strdup_root(MEM_ROOT *root,const char *str); extern char *strmake_root(MEM_ROOT *root,const char *str,uint len); extern char *memdup_root(MEM_ROOT *root,const char *str,uint len); -extern void get_defaults_files(int argc, char **argv, - char **defaults, char **extra_defaults); +extern int get_defaults_options(int argc, char **argv, + char **defaults, char **extra_defaults, + char **group_suffix); extern int load_defaults(const char *conf_file, const char **groups, int *argc, char ***argv); extern int modify_defaults_file(const char *file_location, const char *option, diff --git a/libmysql/Makefile.shared b/libmysql/Makefile.shared index a2bfa616f6b..fab1a402c1e 100644 --- a/libmysql/Makefile.shared +++ b/libmysql/Makefile.shared @@ -84,6 +84,7 @@ CLEANFILES = $(target_libadd) $(SHLIBOBJS) \ DEFS = -DDEFAULT_CHARSET_HOME="\"$(MYSQLBASEdir)\"" \ -DDATADIR="\"$(MYSQLDATAdir)\"" \ -DDEFAULT_HOME_ENV=MYSQL_HOME \ + -DDEFAULT_GROUP_SUFFIX_ENV=MYSQL_GROUP_SUFFIX \ -DSHAREDIR="\"$(MYSQLSHAREdir)\"" $(target_defs) # The automatic dependencies miss this diff --git a/mysys/Makefile.am b/mysys/Makefile.am index 03ee692645f..0007a8808bd 100644 --- a/mysys/Makefile.am +++ b/mysys/Makefile.am @@ -44,7 +44,7 @@ libmysys_a_SOURCES = my_init.c my_getwd.c mf_getdate.c my_mmap.c \ ptr_cmp.c mf_radix.c queues.c \ tree.c list.c hash.c array.c string.c typelib.c \ my_copy.c my_append.c my_lib.c \ - my_delete.c my_rename.c my_redel.c my_tempnam.c \ + my_delete.c my_rename.c my_redel.c \ my_chsize.c my_lread.c my_lwrite.c my_clock.c \ my_quick.c my_lockmem.c my_static.c \ my_sync.c my_getopt.c my_mkdir.c \ @@ -68,6 +68,7 @@ DEFS = -DDEFAULT_BASEDIR=\"$(prefix)\" \ -DDEFAULT_CHARSET_HOME="\"$(MYSQLBASEdir)\"" \ -DSHAREDIR="\"$(MYSQLSHAREdir)\"" \ -DDEFAULT_HOME_ENV=MYSQL_HOME \ + -DDEFAULT_GROUP_SUFFIX_ENV=MYSQL_GROUP_SUFFIX \ @DEFS@ libmysys_a_DEPENDENCIES= @THREAD_LOBJECTS@ diff --git a/mysys/default.c b/mysys/default.c index 1fa8deaa65c..994749f1c54 100644 --- a/mysys/default.c +++ b/mysys/default.c @@ -30,8 +30,8 @@ --no-defaults ; no options are read. --defaults-file=full-path-to-default-file ; Only this file will be read. --defaults-extra-file=full-path-to-default-file ; Read this file before ~/ - --print-defaults ; Print the modified command line and exit - --instance ; also read groups with concat(group, instance) + --defaults-group-suffix ; Also read groups with concat(group, suffix) + --print-defaults ; Print the modified command line and exit ****************************************************************************/ #include "mysys_priv.h" @@ -42,8 +42,7 @@ #include #endif -const char *defaults_instance=0; -static const char instance_option[] = "--instance="; +const char *defaults_group_suffix=0; char *defaults_extra_file=0; /* Which directories are searched for options (and in which order) */ @@ -60,6 +59,9 @@ static const char *f_extensions[]= { ".cnf", 0 }; #define NEWLINE "\n" #endif +static int handle_default_option(void *in_ctx, const char *group_name, + const char *option); + /* This structure defines the context that we pass to callback function 'handle_default_option' used in search_default_file @@ -100,35 +102,81 @@ static char *remove_end_comment(char *ptr); func_ctx It's context. Usually it is the structure to store additional options. DESCRIPTION + Process the default options from argc & argv + Read through each found config file looks and calls 'func' to process + each option. + + NOTES + --defaults-group-suffix is only processed if we are called from + load_defaults(). - This function looks for config files in default directories. Then it - travesrses each of the files and calls func to process each option. RETURN 0 ok 1 given cinf_file doesn't exist + + The global variable 'defaults_group_suffix' is updated with value for + --defaults_group_suffix */ int my_search_option_files(const char *conf_file, int *argc, char ***argv, - uint *args_used, Process_option_func func, - void *func_ctx) + uint *args_used, Process_option_func func, + void *func_ctx) { const char **dirs, *forced_default_file, *forced_extra_defaults; int error= 0; DBUG_ENTER("my_search_option_files"); /* Check if we want to force the use a specific default file */ - get_defaults_files(*argc - *args_used, *argv + *args_used, - (char **)&forced_default_file, - (char **)&forced_extra_defaults); - if (forced_default_file) - forced_default_file= strchr(forced_default_file,'=')+1; - if (forced_extra_defaults) - defaults_extra_file= strchr(forced_extra_defaults,'=')+1; + *args_used+= get_defaults_options(*argc - *args_used, *argv + *args_used, + (char **) &forced_default_file, + (char **) &forced_extra_defaults, + (char **) &defaults_group_suffix); - (*args_used)+= (forced_default_file ? 1 : 0) + - (forced_extra_defaults ? 1 : 0); + if (! defaults_group_suffix) + defaults_group_suffix= getenv(STRINGIFY_ARG(DEFAULT_GROUP_SUFFIX_ENV)); + + /* + We can only handle 'defaults-group-suffix' if we are called from + load_defaults() as otherwise we can't know the type of 'func_ctx' + */ + if (defaults_group_suffix && func == handle_default_option) + { + /* Handle --defaults-group-suffix= */ + uint i; + const char **extra_groups; + const uint instance_len= strlen(defaults_group_suffix); + struct handle_option_ctx *ctx= (struct handle_option_ctx*) func_ctx; + char *ptr; + TYPELIB *group= ctx->group; + + if (!(extra_groups= + (const char**)alloc_root(ctx->alloc, + (2*group->count+1)*sizeof(char*)))) + goto err; + + for (i= 0; i < group->count; i++) + { + uint len; + extra_groups[i]= group->type_names[i]; /** copy group */ + + len= strlen(extra_groups[i]); + if (!(ptr= alloc_root(ctx->alloc, len+instance_len+1))) + goto err; + + extra_groups[i+group->count]= ptr; + + /** Construct new group */ + memcpy(ptr, extra_groups[i], len); + memcpy(ptr+len, defaults_group_suffix, instance_len+1); + } + + group->count*= 2; + group->type_names= extra_groups; + group->type_names[group->count]= 0; + } + if (forced_default_file) { if ((error= search_default_file_with_ext(func, func_ctx, "", "", @@ -221,32 +269,54 @@ static int handle_default_option(void *in_ctx, const char *group_name, /* - Gets --defaults-file and --defaults-extra-file options from command line. + Gets options from the command line SYNOPSIS - get_defaults_files() + get_defaults_options() argc Pointer to argc of original program argv Pointer to argv of original program defaults --defaults-file option extra_defaults --defaults-extra-file option RETURN - defaults and extra_defaults will be set to appropriate items - of argv array, or to NULL if there are no such options + # Number of arguments used from *argv + defaults and extra_defaults will be set to option of the appropriate + items of argv array, or to NULL if there are no such options */ -void get_defaults_files(int argc, char **argv, - char **defaults, char **extra_defaults) +int get_defaults_options(int argc, char **argv, + char **defaults, + char **extra_defaults, + char **group_suffix) { - *defaults=0; - *extra_defaults=0; - if (argc >= 2) + int org_argc= argc, prev_argc= 0; + *defaults= *extra_defaults= *group_suffix= 0; + + while (argc >= 2 && argc != prev_argc) { - if (is_prefix(argv[1],"--defaults-file=")) - *defaults= argv[1]; - else if (is_prefix(argv[1],"--defaults-extra-file=")) - *extra_defaults= argv[1]; + /* Skip program name or previously handled argument */ + argv++; + prev_argc= argc; /* To check if we found */ + if (!*defaults && is_prefix(*argv,"--defaults-file=")) + { + *defaults= *argv + sizeof("--defaults-file=")-1; + argc--; + continue; + } + if (!*extra_defaults && is_prefix(*argv,"--defaults-extra-file=")) + { + *extra_defaults= *argv + sizeof("--defaults-extra-file=")-1; + argc--; + continue; + } + if (!*group_suffix && is_prefix(*argv, "--defaults-group-suffix=")) + { + *group_suffix= *argv + sizeof("--defaults-group-suffix=")-1; + argc--; + continue; + } } + return org_argc - argc; } @@ -296,6 +366,10 @@ int load_defaults(const char *conf_file, const char **groups, init_default_directories(); init_alloc_root(&alloc,512,0); + /* + Check if the user doesn't want any default option processing + --no-defaults is always the first option + */ if (*argc >= 2 && !strcmp(argv[0][1],"--no-defaults")) { /* remove the --no-defaults argument and return only the other arguments */ @@ -328,51 +402,8 @@ int load_defaults(const char *conf_file, const char **groups, ctx.args= &args; ctx.group= &group; - if (*argc >= 2 + args_used && - is_prefix(argv[0][1+args_used], instance_option)) - { - args_used++; - defaults_instance= argv[0][args_used]+sizeof(instance_option)-1; - } - else - { - defaults_instance= getenv("MYSQL_INSTANCE"); - } - - if (defaults_instance) - { - /** Handle --instance= */ - uint i, len; - const char **extra_groups; - const uint instance_len= strlen(defaults_instance); - - if (!(extra_groups= - (const char**)alloc_root(&alloc, (2*group.count+1)*sizeof(char*)))) - goto err; - - for (i= 0; i 0 only if we have a fully specified conf_file or a forced default file @@ -385,11 +416,14 @@ int load_defaults(const char *conf_file, const char **groups, /* copy name + found arguments + command line arguments to new array */ res[0]= argv[0][0]; /* Name MUST be set, even by embedded library */ memcpy((gptr) (res+1), args.buffer, args.elements*sizeof(char*)); - /* Skip --defaults-file and --defaults-extra-file */ + /* Skip --defaults-xxx options */ (*argc)-= args_used; (*argv)+= args_used; - /* Check if we wan't to see the new argument list */ + /* + Check if we wan't to see the new argument list + This options must always be the last of the default options + */ if (*argc >= 2 && !strcmp(argv[0][1],"--print-defaults")) { found_print_defaults=1; @@ -850,14 +884,14 @@ void print_defaults(const char *conf_file, const char **groups) fputs(*groups,stdout); } - if (defaults_instance) + if (defaults_group_suffix) { groups= groups_save; for ( ; *groups ; groups++) { fputc(' ',stdout); fputs(*groups,stdout); - fputs(defaults_instance,stdout); + fputs(defaults_group_suffix,stdout); } } puts("\nThe following options may be given as the first argument:\n\ diff --git a/mysys/default_modify.c b/mysys/default_modify.c index ea384f9f27a..de03d783c68 100644 --- a/mysys/default_modify.c +++ b/mysys/default_modify.c @@ -20,8 +20,7 @@ #include #define BUFF_SIZE 1024 -/* should be big enough to handle at least one line */ -#define RESERVE 1024 +#define RESERVE 1024 /* Extend buffer with this extent */ #ifdef __WIN__ #define NEWLINE "\r\n" @@ -70,7 +69,7 @@ int modify_defaults_file(const char *file_location, const char *option, char linebuff[BUFF_SIZE], *src_ptr, *dst_ptr, *file_buffer; uint opt_len, optval_len, sect_len, nr_newlines= 0, buffer_size; my_bool in_section= FALSE, opt_applied= 0; - uint reserve_extended= 1, old_opt_len= 0; + uint reserve_extended; uint new_opt_len; int reserve_occupied= 0; DBUG_ENTER("modify_defaults_file"); @@ -88,25 +87,21 @@ int modify_defaults_file(const char *file_location, const char *option, new_opt_len= opt_len + 1 + optval_len + NEWLINE_LEN; /* calculate the size of the buffer we need */ - buffer_size= sizeof(char) * (file_stat.st_size + - /* option name len */ - opt_len + - /* reserve for '=' char */ - 1 + - /* option value len */ - optval_len + - /* reserve space for newline */ - NEWLINE_LEN + - /* The ending zero */ - 1 + - /* reserve some additional space */ - RESERVE); + reserve_extended= (opt_len + + 1 + /* For '=' char */ + optval_len + /* Option value len */ + NEWLINE_LEN + /* Space for newline */ + RESERVE); /* Some additional space */ + + buffer_size= (file_stat.st_size + + 1); /* The ending zero */ /* Reserve space to read the contents of the file and some more for the option we want to add. */ - if (!(file_buffer= (char*) my_malloc(buffer_size, MYF(MY_WME)))) + if (!(file_buffer= (char*) my_malloc(buffer_size + reserve_extended, + MYF(MY_WME)))) goto malloc_err; sect_len= (uint) strlen(section_name); @@ -130,31 +125,20 @@ int modify_defaults_file(const char *file_location, const char *option, my_isspace(&my_charset_latin1, *(src_ptr + opt_len)) || *(src_ptr + opt_len) == '\0')) { - /* - we should change all options. If opt_applied is set, we are running - into reserved memory area. Hence we should check for overruns. - */ - if (opt_applied) + char *old_src_ptr= src_ptr; + src_ptr= strend(src_ptr+ opt_len); /* Find the end of the line */ + + /* could be negative */ + reserve_occupied+= (int) new_opt_len - (int) (src_ptr - old_src_ptr); + if (reserve_occupied >= (int) reserve_extended) { - src_ptr+= opt_len; /* If we correct an option, we know it's name */ - old_opt_len= opt_len; - - while (*src_ptr++) /* Find the end of the line */ - old_opt_len++; - - /* could be negative */ - reserve_occupied+= (int) new_opt_len - (int) old_opt_len; - if ((int) reserve_occupied > (int) (RESERVE*reserve_extended)) - { - if (!(file_buffer= (char*) my_realloc(file_buffer, buffer_size + - RESERVE*reserve_extended, - MYF(MY_WME|MY_FREE_ON_ERROR)))) - goto malloc_err; - reserve_extended++; - } + reserve_extended= (uint) reserve_occupied + RESERVE; + if (!(file_buffer= (char*) my_realloc(file_buffer, buffer_size + + reserve_extended, + MYF(MY_WME|MY_FREE_ON_ERROR)))) + goto malloc_err; } - else - opt_applied= 1; + opt_applied= 1; dst_ptr= add_option(dst_ptr, option_value, option, remove_option); } else @@ -164,6 +148,7 @@ int modify_defaults_file(const char *file_location, const char *option, { dst_ptr= add_option(dst_ptr, option_value, option, remove_option); opt_applied= 1; /* set the flag to do write() later */ + reserve_occupied= new_opt_len+ opt_len + 1 + NEWLINE_LEN; } for (; nr_newlines; nr_newlines--) diff --git a/mysys/my_bitmap.c b/mysys/my_bitmap.c index ba958b234d2..c70c0fa0754 100644 --- a/mysys/my_bitmap.c +++ b/mysys/my_bitmap.c @@ -152,6 +152,7 @@ my_bool bitmap_test_and_set(MY_BITMAP *map, uint bitmap_bit) bitmap_lock(map); res= bitmap_fast_test_and_set(map, bitmap_bit); bitmap_unlock(map); + return res; } uint bitmap_set_next(MY_BITMAP *map) diff --git a/mysys/my_tempnam.c b/mysys/my_tempnam.c deleted file mode 100644 index 9f765298fb6..00000000000 --- a/mysys/my_tempnam.c +++ /dev/null @@ -1,173 +0,0 @@ -/* Copyright (C) 2000 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 */ - -/* - This function is only used by some old ISAM code. - When we remove ISAM support from MySQL, we should also delete this file - - One should instead use the functions in mf_tempfile.c -*/ - -#include "mysys_priv.h" -#include - -/* HPUX 11.0 doesn't allow us to change the environ pointer */ -#ifdef HPUX11 -#undef HAVE_TEMPNAM -#endif - -#include "my_static.h" -#include "mysys_err.h" - -#define TMP_EXT ".tmp" /* Extension of tempfile */ -#if ! defined(P_tmpdir) -#define P_tmpdir "" -#endif - -#ifdef HAVE_TEMPNAM -#if !defined( MSDOS) && !defined(OS2) && !defined(__NETWARE__) -extern char **environ; -#endif -#endif - -/* Make a uniq temp file name by using dir and adding something after - pfx to make name uniq. Name is made by adding a uniq 8 length-string and - TMP_EXT after pfx. - Returns pointer to malloced area for filename. Should be freed by - free(). - The name should be uniq, but it isn't checked if it file allready exists. - Uses tempnam() if function exist on system. - This function fixes that if dir is given it's used. For example - MSDOS tempnam() uses always TMP environment-variable if it exists. -*/ - /* ARGSUSED */ - -my_string my_tempnam(const char *dir, const char *pfx, - myf MyFlags __attribute__((unused))) -{ -#ifdef _MSC_VER - char temp[FN_REFLEN],*end,*res,**old_env,*temp_env[1]; - old_env=environ; - if (dir) - { - end=strend(dir)-1; - if (!dir[0]) - { /* Change empty string to current dir */ - temp[0]= FN_CURLIB; - temp[1]= 0; - dir=temp; - } - else if (*end == FN_DEVCHAR) - { /* Get current dir for drive */ - _fullpath(temp,dir,FN_REFLEN); - dir=temp; - } - else if (*end == FN_LIBCHAR && dir < end && end[-1] != FN_DEVCHAR) - { - strmake(temp,dir,(uint) (end-dir)); /* Copy and remove last '\' */ - dir=temp; - } - environ=temp_env; /* Force use of dir (dir not checked) */ - temp_env[0]=0; - } - res=tempnam((char*) dir,(my_string) pfx); - environ=old_env; - return res; -#else -#ifdef __ZTC__ - if (!dir) - { /* If empty test first if TMP can be used */ - dir=getenv("TMP"); - } - return tempnam((char*) dir,(my_string) pfx); /* Use stand. dir with prefix */ -#else -#ifdef HAVE_TEMPNAM - char temp[2],*res,**old_env,*temp_env[1]; - - if (dir && !dir[0]) - { /* Change empty string to current dir */ - temp[0]= FN_CURLIB; - temp[1]= 0; - dir=temp; - } -#ifdef OS2 - /* changing environ variable doesn't work with VACPP */ - char buffer[256], *end; - buffer[sizeof[buffer)-1]= 0; - end= strxnmov(buffer, sizeof(buffer)-1, (char*) "TMP=", dir, NullS); - /* remove ending backslash */ - if (end[-1] == '\\') - end[-1]= 0; - putenv(buffer); -#elif !defined(__NETWARE__) - old_env=(char**)environ; - if (dir) - { /* Don't use TMPDIR if dir is given */ - /* - The following strange cast is required because the IBM compiler on AIX - doesn't allow us to cast the value of environ. - The cast of environ is needed as some systems doesn't allow us to - update environ with a char ** pointer. (const mismatch) - */ - (*(char***) &environ)=(char**) temp_env; - temp_env[0]=0; - } -#endif - res=tempnam((char*) dir,(my_string) pfx); /* Use stand. dir with prefix */ -#if !defined(OS2) && !defined(__NETWARE__) - (*(char***) &environ)=(char**) old_env; -#endif - if (!res) - DBUG_PRINT("error",("Got error: %d from tempnam",errno)); - return res; -#else - register long uniq; - register int length; - my_string pos,end_pos; - DBUG_ENTER("my_tempnam"); - /* Make a uniq nummber */ - pthread_mutex_lock(&THR_LOCK_open); - uniq= ((long) getpid() << 20) + (long) _my_tempnam_used++ ; - pthread_mutex_unlock(&THR_LOCK_open); - if (!dir && !(dir=getenv("TMPDIR"))) /* Use this if possibly */ - dir=P_tmpdir; /* Use system default */ - length=strlen(dir)+strlen(pfx)+1; - - DBUG_PRINT("test",("mallocing %d byte",length+8+sizeof(TMP_EXT)+1)); - if (!(pos=(char*) malloc(length+8+sizeof(TMP_EXT)+1))) - { - if (MyFlags & MY_FAE+MY_WME) - my_error(EE_OUTOFMEMORY, MYF(ME_BELL+ME_WAITTANG), - length+8+sizeof(TMP_EXT)+1); - DBUG_RETURN(NullS); - } - end_pos=strmov(pos,dir); - if (end_pos != pos && end_pos[-1] != FN_LIBCHAR) - *end_pos++=FN_LIBCHAR; - end_pos=strmov(end_pos,pfx); - - for (length=0 ; length < 8 && uniq ; length++) - { - *end_pos++= _dig_vec_upper[(int) (uniq & 31)]; - uniq >>= 5; - } - VOID(strmov(end_pos,TMP_EXT)); - DBUG_PRINT("exit",("tempnam: '%s'",pos)); - DBUG_RETURN(pos); -#endif /* HAVE_TEMPNAM */ -#endif /* __ZTC__ */ -#endif /* _MSC_VER */ -} /* my_tempnam */ diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index 8218e4fecc0..d49387ca4e5 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -5484,7 +5484,7 @@ ha_innobase::update_table_comment( external_lock(). To be safe, update the thd of the current table handle. */ - if(length > 64000 - 3) { + if (length > 64000 - 3) { return((char*)comment); /* string too long */ } @@ -5522,7 +5522,7 @@ ha_innobase::update_table_comment( if (str) { char* pos = str + length; - if(length) { + if (length) { memcpy(str, comment, length); *pos++ = ';'; *pos++ = ' '; @@ -5580,7 +5580,7 @@ ha_innobase::get_foreign_key_create_info(void) flen = ftell(file); if (flen < 0) { flen = 0; - } else if(flen > 64000 - 1) { + } else if (flen > 64000 - 1) { flen = 64000 - 1; } diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index def99b51f95..4ff0951d89b 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -5464,7 +5464,7 @@ ndb_get_table_statistics(Ndb* ndb, const char * table, Uint64 sum_commits= 0; Uint64 sum_row_size= 0; Uint64 sum_mem= 0; - while((check= pOp->nextResult(TRUE, TRUE)) == 0) + while ((check= pOp->nextResult(TRUE, TRUE)) == 0) { sum_rows+= rows; sum_commits+= commits; @@ -5492,7 +5492,7 @@ ndb_get_table_statistics(Ndb* ndb, const char * table, sum_mem, count)); DBUG_RETURN(0); - } while(0); + } while (0); if (pTrans) ndb->closeTransaction(pTrans); diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 5ed857319be..12342f707c3 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -2180,7 +2180,13 @@ void Item_func_in::fix_length_and_dec() return; for (arg=args+1, arg_end=args+arg_count; arg != arg_end ; arg++) - const_itm&= arg[0]->const_item(); + { + if (!arg[0]->const_item()) + { + const_itm= 0; + break; + } + } /* Row item with NULLs inside can return NULL or FALSE => diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 1ad65fb6208..44d78f28052 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -2093,7 +2093,7 @@ String *Item_func_rpad::val_str(String *str) func_name(), current_thd->variables.max_allowed_packet); goto err; } - if(args[2]->null_value || !pad_char_length) + if (args[2]->null_value || !pad_char_length) goto err; res_byte_length= res->length(); /* Must be done before alloc_buffer */ if (!(res= alloc_buffer(res,str,&tmp_value,byte_count))) diff --git a/sql/log.cc b/sql/log.cc index e69c0af15bd..6369699378b 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -2647,7 +2647,7 @@ int TC_LOG_MMAP::log(THD *thd, my_xid xid) { // somebody's syncing. let's wait p->waiters++; /* - note - it must be while(), not do ... while() here + note - it must be while (), not do ... while () here as p->state may be not DIRTY when we come here */ while (p->state == DIRTY && syncing) diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 78d9af387da..035f098c88c 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -882,7 +882,7 @@ static void __cdecl kill_server(int sig_ptr) unireg_end(); #ifdef __NETWARE__ - if(!event_flag) + if (!event_flag) pthread_join(select_thread, NULL); // wait for main thread #endif /* __NETWARE__ */ diff --git a/sql/opt_range.cc b/sql/opt_range.cc index c2760b08b6e..9965766af19 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -630,7 +630,7 @@ int imerge_list_or_tree(PARAM *param, { SEL_IMERGE *imerge; List_iterator it(*im1); - while((imerge= it++)) + while ((imerge= it++)) { if (imerge->or_sel_tree_with_checks(param, tree)) it.remove(); @@ -990,7 +990,7 @@ int QUICK_ROR_INTERSECT_SELECT::init_ror_merged_scan(bool reuse_handler) DBUG_RETURN(1); quick->file->extra(HA_EXTRA_KEYREAD_PRESERVE_FIELDS); } - while((quick= quick_it++)) + while ((quick= quick_it++)) { if (quick->init_ror_merged_scan(FALSE)) DBUG_RETURN(1); @@ -6942,7 +6942,7 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree) List_iterator select_items_it(join->fields_list); /* Check (SA1,SA4) and store the only MIN/MAX argument - the C attribute.*/ - if(join->make_sum_func_list(join->all_fields, join->fields_list, 1)) + if (join->make_sum_func_list(join->all_fields, join->fields_list, 1)) DBUG_RETURN(NULL); if (join->sum_funcs[0]) { @@ -7268,7 +7268,7 @@ check_group_min_max_predicates(COND *cond, Item_field *min_max_arg_item, Item *and_or_arg; while ((and_or_arg= li++)) { - if(!check_group_min_max_predicates(and_or_arg, min_max_arg_item, + if (!check_group_min_max_predicates(and_or_arg, min_max_arg_item, image_type)) DBUG_RETURN(FALSE); } @@ -7350,7 +7350,7 @@ check_group_min_max_predicates(COND *cond, Item_field *min_max_arg_item, } else if (cur_arg->type() == Item::FUNC_ITEM) { - if(!check_group_min_max_predicates(cur_arg, min_max_arg_item, + if (!check_group_min_max_predicates(cur_arg, min_max_arg_item, image_type)) DBUG_RETURN(FALSE); } @@ -7881,19 +7881,19 @@ int QUICK_GROUP_MIN_MAX_SELECT::init() if (min_max_arg_part) { - if(my_init_dynamic_array(&min_max_ranges, sizeof(QUICK_RANGE*), 16, 16)) + if (my_init_dynamic_array(&min_max_ranges, sizeof(QUICK_RANGE*), 16, 16)) return 1; if (have_min) { - if(!(min_functions= new List)) + if (!(min_functions= new List)) return 1; } else min_functions= NULL; if (have_max) { - if(!(max_functions= new List)) + if (!(max_functions= new List)) return 1; } else @@ -7967,7 +7967,7 @@ bool QUICK_GROUP_MIN_MAX_SELECT::add_range(SEL_ARG *sel_range) uint range_flag= sel_range->min_flag | sel_range->max_flag; /* Skip (-inf,+inf) ranges, e.g. (x < 5 or x > 4). */ - if((range_flag & NO_MIN_RANGE) && (range_flag & NO_MAX_RANGE)) + if ((range_flag & NO_MIN_RANGE) && (range_flag & NO_MAX_RANGE)) return FALSE; if (!(sel_range->min_flag & NO_MIN_RANGE) && diff --git a/sql/parse_file.cc b/sql/parse_file.cc index 7cc563901d2..abca8736916 100644 --- a/sql/parse_file.cc +++ b/sql/parse_file.cc @@ -728,7 +728,7 @@ File_parser::parse(gptr base, MEM_ROOT *mem_root, sizeof(LEX_STRING))) || list->push_back(str, mem_root)) goto list_err; - if(!(ptr= parse_quoted_escaped_string(ptr, end, mem_root, str))) + if (!(ptr= parse_quoted_escaped_string(ptr, end, mem_root, str))) goto list_err_w_message; switch (*ptr) { case '\n': diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 430e0fbcabf..2cfe30004c0 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -2052,7 +2052,7 @@ my_bool Query_cache::allocate_data_chain(Query_cache_block **result_block, */ data_len= len - new_block->length; prev_block= new_block; - } while(1); + } while (1); DBUG_RETURN(TRUE); } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index a57ad84da5b..e78b2b94199 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2151,7 +2151,7 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident, TABLE_LIST **query_tables_last= lex->query_tables_last; sel= new SELECT_LEX(); sel->init_query(); - if(!sel->add_table_to_list(thd, table_ident, 0, 0, TL_READ, + if (!sel->add_table_to_list(thd, table_ident, 0, 0, TL_READ, (List *) 0, (List *) 0)) DBUG_RETURN(1); lex->query_tables_last= query_tables_last; diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 53f706bd0f6..b5f6d39cbe6 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1752,6 +1752,10 @@ bool mysql_stmt_prepare(THD *thd, char *packet, uint packet_length, DBUG_RETURN(TRUE); } + /* + alloc_query() uses thd->memroot && thd->query, so we have to call + both of backup_statement() and backup_item_area() here. + */ thd->set_n_backup_statement(stmt, &stmt_backup); thd->set_n_backup_item_arena(stmt, &stmt_backup); @@ -2239,7 +2243,7 @@ void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length) cleanup_stmt_and_thd_after_use(stmt, thd); reset_stmt_params(stmt); /* - Must be the last, as some momory is still needed for + Must be the last, as some memory is still needed for the previous calls. */ free_root(cursor->mem_root, MYF(0)); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 66e783a2103..26b00559b1b 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -6697,7 +6697,7 @@ static COND *build_equal_items_for_cond(COND *cond, of the condition expression. */ li.rewind(); - while((item= li++)) + while ((item= li++)) { Item *new_item; if ((new_item = build_equal_items_for_cond(item, inherited))!= item) @@ -7506,7 +7506,7 @@ simplify_joins(JOIN *join, List *join_list, COND *conds, bool top) /* Flatten nested joins that can be flattened. */ li.rewind(); - while((table= li++)) + while ((table= li++)) { nested_join= table->nested_join; if (nested_join && !table->on_expr) @@ -12102,7 +12102,6 @@ create_distinct_group(THD *thd, Item **ref_pointer_array, List_iterator li(fields); Item *item; ORDER *order,*group,**prev; - uint index= 0; *all_order_by_fields_used= 1; while ((item=li++)) @@ -12139,12 +12138,12 @@ create_distinct_group(THD *thd, Item **ref_pointer_array, simple indexing of ref_pointer_array (order in the array and in the list are same) */ - ord->item= ref_pointer_array + index; + ord->item= ref_pointer_array; ord->asc=1; *prev=ord; prev= &ord->next; } - index++; + ref_pointer_array++; } *prev=0; return group; @@ -12973,7 +12972,7 @@ static bool change_group_ref(THD *thd, Item_func *expr, ORDER *group_list, if (item->eq(*group_tmp->item,0)) { Item *new_item; - if(!(new_item= new Item_ref(context, group_tmp->item, 0, + if (!(new_item= new Item_ref(context, group_tmp->item, 0, item->name))) return 1; // fatal_error is set thd->change_item_tree(arg, new_item); diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 72092db400d..197e939162a 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1709,7 +1709,7 @@ int make_table_list(THD *thd, SELECT_LEX *sel, ident_table.length= strlen(table); table_ident= new Table_ident(thd, ident_db, ident_table, 1); sel->init_query(); - if(!sel->add_table_to_list(thd, table_ident, 0, 0, TL_READ, + if (!sel->add_table_to_list(thd, table_ident, 0, 0, TL_READ, (List *) 0, (List *) 0)) return 1; return 0; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 4fc9819d0e1..71efd9b727c 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -45,7 +45,7 @@ int yylex(void *yylval, void *yythd); const LEX_STRING null_lex_str={0,0}; -#define yyoverflow(A,B,C,D,E,F) {ulong val= *(F); if(my_yyoverflow((B), (D), &val)) { yyerror((char*) (A)); return 2; } else { *(F)= (YYSIZE_T)val; }} +#define yyoverflow(A,B,C,D,E,F) {ulong val= *(F); if (my_yyoverflow((B), (D), &val)) { yyerror((char*) (A)); return 2; } else { *(F)= (YYSIZE_T)val; }} #define WARN_DEPRECATED(A,B) \ push_warning_printf(((THD *)yythd), MYSQL_ERROR::WARN_LEVEL_WARN, \ diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index a80d5e1d1be..1eceb58f6f6 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -1960,7 +1960,7 @@ static void test_fetch_null() myquery(rc); /* fetch */ - bzero(bind, sizeof(bind)); + bzero((char*) bind, sizeof(bind)); for (i= 0; i < (int) array_elements(bind); i++) { bind[i].buffer_type= MYSQL_TYPE_LONG; @@ -3401,7 +3401,7 @@ static void test_bind_result() /* fetch */ - bzero(bind, sizeof(bind)); + bzero((char*) bind, sizeof(bind)); bind[0].buffer_type= MYSQL_TYPE_LONG; bind[0].buffer= (void *) &nData; /* integer data */ bind[0].is_null= &is_null[0]; @@ -3494,7 +3494,7 @@ static void test_bind_result_ext() rc= mysql_commit(mysql); myquery(rc); - bzero(bind, sizeof(bind)); + bzero((char*) bind, sizeof(bind)); for (i= 0; i < (int) array_elements(bind); i++) { bind[i].length= &length[i]; @@ -3613,7 +3613,7 @@ static void test_bind_result_ext1() rc= mysql_commit(mysql); myquery(rc); - bzero(bind, sizeof(bind)); + bzero((char*) bind, sizeof(bind)); bind[0].buffer_type= MYSQL_TYPE_STRING; bind[0].buffer= (void *) t_data; bind[0].buffer_length= sizeof(t_data); @@ -3906,7 +3906,7 @@ static void test_fetch_date() rc= mysql_commit(mysql); myquery(rc); - bzero(bind, sizeof(bind)); + bzero((char*) bind, sizeof(bind)); for (i= 0; i < array_elements(bind); i++) { bind[i].is_null= &is_null[i]; @@ -5623,7 +5623,7 @@ static void test_store_result() myquery(rc); /* fetch */ - bzero(bind, sizeof(bind)); + bzero((char*) bind, sizeof(bind)); bind[0].buffer_type= MYSQL_TYPE_LONG; bind[0].buffer= (void *) &nData; /* integer data */ bind[0].length= &length; @@ -6337,7 +6337,7 @@ static void test_buffers() bzero(buffer, sizeof(buffer)); /* Avoid overruns in printf() */ - bzero(bind, sizeof(bind)); + bzero((char*) bind, sizeof(bind)); bind[0].length= &length; bind[0].is_null= &is_null; bind[0].buffer_length= 1; @@ -6526,7 +6526,7 @@ static void test_fetch_nobuffs() fprintf(stdout, "\n total rows : %d", rc); DIE_UNLESS(rc == 1); - bzero(bind, sizeof(MYSQL_BIND)); + bzero((char*) bind, sizeof(MYSQL_BIND)); bind[0].buffer_type= MYSQL_TYPE_STRING; bind[0].buffer= (void *)str[0]; bind[0].buffer_length= sizeof(str[0]); @@ -6596,7 +6596,7 @@ static void test_ushort_bug() rc= mysql_stmt_execute(stmt); check_execute(stmt, rc); - bzero(bind, sizeof(bind)); + bzero((char*) bind, sizeof(bind)); bind[0].buffer_type= MYSQL_TYPE_SHORT; bind[0].buffer= (void *)&short_value; bind[0].is_unsigned= TRUE; @@ -6684,7 +6684,7 @@ static void test_sshort_bug() rc= mysql_stmt_execute(stmt); check_execute(stmt, rc); - bzero(bind, sizeof(bind)); + bzero((char*) bind, sizeof(bind)); bind[0].buffer_type= MYSQL_TYPE_SHORT; bind[0].buffer= (void *)&short_value; bind[0].length= &s_length; @@ -6771,7 +6771,7 @@ static void test_stiny_bug() rc= mysql_stmt_execute(stmt); check_execute(stmt, rc); - bzero(bind, sizeof(bind)); + bzero((char*) bind, sizeof(bind)); bind[0].buffer_type= MYSQL_TYPE_SHORT; bind[0].buffer= (void *)&short_value; bind[0].length= &s_length; @@ -6879,7 +6879,7 @@ static void test_field_misc() rc= mysql_stmt_execute(stmt); check_execute(stmt, rc); - bzero(bind, sizeof(bind)); + bzero((char*) bind, sizeof(bind)); bind[0].buffer_type= MYSQL_TYPE_STRING; bind[0].buffer= table_type; bind[0].length= &type_length; @@ -7190,7 +7190,7 @@ static void test_frm_bug() rc= mysql_stmt_execute(stmt); check_execute(stmt, rc); - bzero(bind, sizeof(bind)); + bzero((char*) bind, sizeof(bind)); bind[0].buffer_type= MYSQL_TYPE_STRING; bind[0].buffer= data_dir; bind[0].buffer_length= FN_REFLEN; @@ -7928,7 +7928,7 @@ static void test_fetch_seek() stmt= mysql_simple_prepare(mysql, "select * from t1"); check_stmt(stmt); - bzero(bind, sizeof(bind)); + bzero((char*) bind, sizeof(bind)); bind[0].buffer_type= MYSQL_TYPE_LONG; bind[0].buffer= (void *)&c1; @@ -8024,7 +8024,7 @@ static void test_fetch_offset() stmt= mysql_simple_prepare(mysql, "select * from t1"); check_stmt(stmt); - bzero(bind, sizeof(bind)); + bzero((char*) bind, sizeof(bind)); bind[0].buffer_type= MYSQL_TYPE_STRING; bind[0].buffer= (void *)data; bind[0].buffer_length= 11; @@ -8111,7 +8111,7 @@ static void test_fetch_column() stmt= mysql_simple_prepare(mysql, "select * from t1 order by c2 desc"); check_stmt(stmt); - bzero(bind, sizeof(bind)); + bzero((char*) bind, sizeof(bind)); bind[0].buffer_type= MYSQL_TYPE_LONG; bind[0].buffer= (void *)&bc1; bind[0].buffer_length= 0; @@ -8359,7 +8359,7 @@ static void test_free_result() stmt= mysql_simple_prepare(mysql, "select * from test_free_result"); check_stmt(stmt); - bzero(bind, sizeof(bind)); + bzero((char*) bind, sizeof(bind)); bind[0].buffer_type= MYSQL_TYPE_LONG; bind[0].buffer= (void *)&bc1; bind[0].length= &bl1; @@ -8439,7 +8439,7 @@ static void test_free_store_result() stmt= mysql_simple_prepare(mysql, "select * from test_free_result"); check_stmt(stmt); - bzero(bind, sizeof(bind)); + bzero((char*) bind, sizeof(bind)); bind[0].buffer_type= MYSQL_TYPE_LONG; bind[0].buffer= (void *)&bc1; bind[0].buffer_length= 0; @@ -9900,7 +9900,7 @@ static void test_bug3035() rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text)); myquery(rc); - bzero(bind_array, sizeof(bind_array)); + bzero((char*) bind_array, sizeof(bind_array)); for (bind= bind_array; bind < bind_end; bind++) bind->error= &bind->error_value; @@ -10107,7 +10107,7 @@ static void test_bug1664() verify_param_count(stmt, 2); - bzero(&bind, sizeof(bind)); + bzero((char*) bind, sizeof(bind)); bind[0].buffer_type= MYSQL_TYPE_STRING; bind[0].buffer= (void *)str_data; @@ -10347,7 +10347,7 @@ static void test_ps_i18n() rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text)); myquery(rc); - bzero(bind_array, sizeof(bind_array)); + bzero((char*) bind_array, sizeof(bind_array)); bind_array[0].buffer_type= MYSQL_TYPE_STRING; bind_array[0].buffer= (void *) koi8; @@ -10531,7 +10531,7 @@ static void test_bug3796() check_execute(stmt, rc); /* Bind input buffers */ - bzero(bind, sizeof(bind)); + bzero((char*) bind, sizeof(bind)); bind[0].buffer_type= MYSQL_TYPE_STRING; bind[0].buffer= (void *) concat_arg0; @@ -10598,11 +10598,11 @@ static void test_bug4026() check_execute(stmt, rc); /* Bind input buffers */ - bzero(bind, sizeof(bind)); - bzero(&time_in, sizeof(time_in)); - bzero(&time_out, sizeof(time_out)); - bzero(&datetime_in, sizeof(datetime_in)); - bzero(&datetime_out, sizeof(datetime_out)); + bzero((char*) bind, sizeof(bind)); + bzero((char*) &time_in, sizeof(time_in)); + bzero((char*) &time_out, sizeof(time_out)); + bzero((char*) &datetime_in, sizeof(datetime_in)); + bzero((char*) &datetime_out, sizeof(datetime_out)); bind[0].buffer_type= MYSQL_TYPE_TIME; bind[0].buffer= (void *) &time_in; @@ -10680,7 +10680,7 @@ static void test_bug4079() check_execute(stmt, rc); /* Bind input buffers */ - bzero(bind, sizeof(bind)); + bzero((char*) bind, sizeof(bind)); bind[0].buffer_type= MYSQL_TYPE_LONG; bind[0].buffer= (void *) &res; @@ -10749,13 +10749,13 @@ static void test_bug4030() check_execute(stmt, rc); /* Bind output buffers */ - bzero(bind, sizeof(bind)); - bzero(&time_canonical, sizeof(time_canonical)); - bzero(&time_out, sizeof(time_out)); - bzero(&date_canonical, sizeof(date_canonical)); - bzero(&date_out, sizeof(date_out)); - bzero(&datetime_canonical, sizeof(datetime_canonical)); - bzero(&datetime_out, sizeof(datetime_out)); + bzero((char*) bind, sizeof(bind)); + bzero((char*) &time_canonical, sizeof(time_canonical)); + bzero((char*) &time_out, sizeof(time_out)); + bzero((char*) &date_canonical, sizeof(date_canonical)); + bzero((char*) &date_out, sizeof(date_out)); + bzero((char*) &datetime_canonical, sizeof(datetime_canonical)); + bzero((char*) &datetime_out, sizeof(datetime_out)); bind[0].buffer_type= MYSQL_TYPE_TIME; bind[0].buffer= (void *) &time_out; @@ -10858,7 +10858,7 @@ static void test_view() check_execute(stmt, rc); strmov(str_data, "TEST"); - bzero(bind, sizeof(bind)); + bzero((char*) bind, sizeof(bind)); bind[0].buffer_type= FIELD_TYPE_STRING; bind[0].buffer= (char *)&str_data; bind[0].buffer_length= 50; @@ -10977,7 +10977,7 @@ static void test_view_2where() " AENAME,T0001.DEPENDVARS AS DEPENDVARS,T0001.INACTIVE AS " " INACTIVE from LTDX T0001 where (T0001.SRTF2 = 0)"); myquery(rc); - bzero(bind, sizeof(bind)); + bzero((char*) bind, sizeof(bind)); for (i=0; i < 8; i++) { strmov(parms[i], "1"); bind[i].buffer_type = MYSQL_TYPE_VAR_STRING; @@ -11026,7 +11026,7 @@ static void test_view_star() myquery(rc); rc= mysql_query(mysql, "CREATE VIEW vt1 AS SELECT a FROM t1"); myquery(rc); - bzero(bind, sizeof(bind)); + bzero((char*) bind, sizeof(bind)); for (i= 0; i < 2; i++) { sprintf((char *)&parms[i], "%d", i); bind[i].buffer_type = MYSQL_TYPE_VAR_STRING; @@ -11092,7 +11092,7 @@ static void test_view_insert() rc= mysql_stmt_prepare(select_stmt, query, strlen(query)); check_execute(select_stmt, rc); - bzero(bind, sizeof(bind)); + bzero((char*) bind, sizeof(bind)); bind[0].buffer_type = FIELD_TYPE_LONG; bind[0].buffer = (char *)&my_val; bind[0].length = &my_length; @@ -11196,7 +11196,7 @@ static void test_view_insert_fields() " F7F8 AS F7F8, F6N4 AS F6N4, F5C8 AS F5C8, F9D8 AS F9D8" " from t1 T0001"); - bzero(bind, sizeof(bind)); + bzero((char*) bind, sizeof(bind)); for (i= 0; i < 11; i++) { l[i]= 20; @@ -11264,7 +11264,7 @@ static void test_bug5126() check_execute(stmt, rc); /* Bind output buffers */ - bzero(bind, sizeof(bind)); + bzero((char*) bind, sizeof(bind)); bind[0].buffer_type= MYSQL_TYPE_LONG; bind[0].buffer= &c1; @@ -11310,8 +11310,8 @@ static void test_bug4231() check_execute(stmt, rc); /* Bind input buffers */ - bzero(bind, sizeof(bind)); - bzero(tm, sizeof(tm)); + bzero((char*) bind, sizeof(bind)); + bzero((char*) tm, sizeof(tm)); bind[0].buffer_type= MYSQL_TYPE_DATE; bind[0].buffer= &tm[0]; @@ -11371,7 +11371,7 @@ static void test_bug5399() myheader("test_bug5399"); - bzero(bind, sizeof(bind)); + bzero((char*) bind, sizeof(bind)); bind[0].buffer_type= MYSQL_TYPE_LONG; bind[0].buffer= &no; @@ -11531,7 +11531,7 @@ static void test_bug5194() param_str_length= strlen(param_str); /* setup bind array */ - bzero(bind, MAX_PARAM_COUNT * sizeof(MYSQL_BIND)); + bzero((char*) bind, MAX_PARAM_COUNT * sizeof(MYSQL_BIND)); for (i= 0; i < MAX_PARAM_COUNT; ++i) { bind[i].buffer_type= MYSQL_TYPE_FLOAT; @@ -11649,7 +11649,7 @@ static void test_bug6049() rc= mysql_stmt_execute(stmt); check_execute(stmt, rc); - bzero(bind, sizeof(bind)); + bzero((char*) bind, sizeof(bind)); bind[0].buffer_type = MYSQL_TYPE_STRING; bind[0].buffer = &buffer; bind[0].buffer_length = sizeof(buffer); @@ -11698,7 +11698,7 @@ static void test_bug6058() rc= mysql_stmt_execute(stmt); check_execute(stmt, rc); - bzero(bind, sizeof(bind)); + bzero((char*) bind, sizeof(bind)); bind[0].buffer_type = MYSQL_TYPE_STRING; bind[0].buffer = &buffer; bind[0].buffer_length = sizeof(buffer); @@ -11766,7 +11766,7 @@ static void test_bug6046() check_execute(stmt, rc); b= 1; - bzero(bind, sizeof(bind)); + bzero((char*) bind, sizeof(bind)); bind[0].buffer= &b; bind[0].buffer_type= MYSQL_TYPE_SHORT; @@ -11945,7 +11945,7 @@ static void test_bug6096() /* Bind and fetch the data */ - bzero(bind, sizeof(bind)); + bzero((char*) bind, sizeof(bind)); for (i= 0; i < stmt_field_count; ++i) { bind[i].buffer_type= MYSQL_TYPE_STRING; @@ -12000,7 +12000,7 @@ static void test_datetime_ranges() check_stmt(stmt); verify_param_count(stmt, 6); - bzero(bind, sizeof(bind)); + bzero((char*) bind, sizeof(bind)); for (i= 0; i < 6; i++) { bind[i].buffer_type= MYSQL_TYPE_DATETIME; @@ -12131,7 +12131,7 @@ static void test_bug4172() rc= mysql_stmt_execute(stmt); check_execute(stmt, rc); - bzero(bind, sizeof(bind)); + bzero((char*) bind, sizeof(bind)); bind[0].buffer_type= MYSQL_TYPE_STRING; bind[0].buffer= f; bind[0].buffer_length= sizeof(f); @@ -12198,7 +12198,7 @@ static void test_conversion() rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text)); check_execute(stmt, rc); - bzero(bind, sizeof(bind)); + bzero((char*) bind, sizeof(bind)); bind[0].buffer= buff; bind[0].length= &length; bind[0].buffer_type= MYSQL_TYPE_STRING; @@ -12261,7 +12261,7 @@ static void test_rewind(void) rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text)); check_execute(stmt, rc); - bzero(&bind,sizeof(MYSQL_BIND)); + bzero((char*) &bind, sizeof(MYSQL_BIND)); bind.buffer_type= MYSQL_TYPE_LONG; bind.buffer= (void *)&Data; /* this buffer won't be altered */ bind.length= &length; @@ -12357,7 +12357,7 @@ static void test_truncation() /*************** Fill in the bind structure and bind it **************/ bind_array= malloc(sizeof(MYSQL_BIND) * bind_count); - bzero(bind_array, sizeof(MYSQL_BIND) * bind_count); + bzero((char*) bind_array, sizeof(MYSQL_BIND) * bind_count); for (bind= bind_array; bind < bind_array + bind_count; bind++) bind->error= &bind->error_value; bind= bind_array; @@ -12562,7 +12562,7 @@ static void test_truncation_option() rc= mysql_stmt_execute(stmt); check_execute(stmt, rc); - bzero(&bind, sizeof(MYSQL_BIND)); + bzero((char*) &bind, sizeof(MYSQL_BIND)); bind.buffer= (void*) &buf; bind.buffer_type= MYSQL_TYPE_TINY; @@ -12637,7 +12637,7 @@ static void test_bug8330() rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text)); myquery(rc); - bzero(bind, sizeof(bind)); + bzero((char*) bind, sizeof(bind)); for (i=0; i < 2; i++) { stmt[i]= mysql_stmt_init(mysql); @@ -12911,7 +12911,7 @@ static void test_bug9520() rc= mysql_stmt_execute(stmt); check_execute(stmt, rc); - bzero(bind, sizeof(bind)); + bzero((char*) bind, sizeof(bind)); bind[0].buffer_type= MYSQL_TYPE_STRING; bind[0].buffer= (char*) a; bind[0].buffer_length= sizeof(a); @@ -12960,7 +12960,7 @@ static void test_bug9478() stmt= open_cursor("select name from t1 where id=2"); - bzero(bind, sizeof(bind)); + bzero((char*) bind, sizeof(bind)); bind[0].buffer_type= MYSQL_TYPE_STRING; bind[0].buffer= (char*) a; bind[0].buffer_length= sizeof(a); @@ -13138,7 +13138,7 @@ static void test_bug9643() rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text)); check_execute(stmt, rc); - bzero(bind, sizeof(bind)); + bzero((char*) bind, sizeof(bind)); bind[0].buffer_type= MYSQL_TYPE_LONG; bind[0].buffer= (void*) &a; bind[0].buffer_length= sizeof(a); @@ -13194,7 +13194,7 @@ static void test_bug11111() mysql_stmt_prepare(stmt, query, strlen(query)); mysql_stmt_execute(stmt); - bzero(bind, sizeof(bind)); + bzero((char*) bind, sizeof(bind)); for (i=0; i < 2; i++) { bind[i].buffer_type= MYSQL_TYPE_STRING; @@ -13231,7 +13231,7 @@ static void test_bug10729() int rc; const char *stmt_text; int i= 0; - char *name_array[3]= { "aaa", "bbb", "ccc" }; + const char *name_array[3]= { "aaa", "bbb", "ccc" }; ulong type; myheader("test_bug10729"); @@ -13252,7 +13252,7 @@ static void test_bug10729() rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text)); check_execute(stmt, rc); - bzero(bind, sizeof(bind)); + bzero((char*) bind, sizeof(bind)); bind[0].buffer_type= MYSQL_TYPE_STRING; bind[0].buffer= (void*) a; bind[0].buffer_length= sizeof(a); @@ -13363,7 +13363,7 @@ static void test_bug10736() rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text)); check_execute(stmt, rc); - bzero(bind, sizeof(bind)); + bzero((char*) bind, sizeof(bind)); bind[0].buffer_type= MYSQL_TYPE_STRING; bind[0].buffer= (void*) a; bind[0].buffer_length= sizeof(a); @@ -13412,7 +13412,7 @@ static void test_bug10794() stmt_text= "insert into t1 (id, name) values (?, ?)"; rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text)); check_execute(stmt, rc); - bzero(bind, sizeof(bind)); + bzero((char*) bind, sizeof(bind)); bind[0].buffer_type= MYSQL_TYPE_LONG; bind[0].buffer= (void*) &id_val; bind[1].buffer_type= MYSQL_TYPE_STRING; @@ -13434,7 +13434,7 @@ static void test_bug10794() mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (const void*) &type); stmt1= mysql_stmt_init(mysql); mysql_stmt_attr_set(stmt1, STMT_ATTR_CURSOR_TYPE, (const void*) &type); - bzero(bind, sizeof(bind)); + bzero((char*) bind, sizeof(bind)); bind[0].buffer_type= MYSQL_TYPE_STRING; bind[0].buffer= (void*) a; bind[0].buffer_length= sizeof(a); @@ -13507,9 +13507,9 @@ static void test_bug11172() type= (ulong) CURSOR_TYPE_READ_ONLY; mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (const void*) &type); - bzero(bind_in, sizeof(bind_in)); - bzero(bind_out, sizeof(bind_out)); - bzero(&hired, sizeof(hired)); + bzero((char*) bind_in, sizeof(bind_in)); + bzero((char*) bind_out, sizeof(bind_out)); + bzero((char*) &hired, sizeof(hired)); hired.year= 1965; hired.month= 1; hired.day= 1; @@ -13577,9 +13577,9 @@ static void test_bug11656() type= (ulong) CURSOR_TYPE_READ_ONLY; mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (const void*) &type); - bzero(bind, sizeof(bind)); - strcpy(buf[0], "pcint502_MY2"); - strcpy(buf[1], "*"); + bzero((char*) bind, sizeof(bind)); + strmov(buf[0], "pcint502_MY2"); + strmov(buf[1], "*"); for (i=0; i < 2; i++) { bind[i].buffer_type= MYSQL_TYPE_STRING; From 7cd2fb13ff6934cea17b470c673b45f1836a85ea Mon Sep 17 00:00:00 2001 From: "gbichot@production.mysql.com" <> Date: Mon, 18 Jul 2005 14:46:53 +0200 Subject: [PATCH 29/48] Fix for BUG#12003 "assertion failure in testsuite (double lock of LOCK_thread_count)" and for BUG#12004 "SHOW BINARY LOGS reports 0 for the size of all binlogs but the current one". There are a lot of 4.1->5.0 unmerged changes (hardest are in the optimizer), can't merge; still pushing in 4.1 because my changes are very small. Feel free to ask me if you have problems merging them. --- mysql-test/r/rpl_log.result | 4 ++-- mysql-test/r/rpl_rotate_logs.result | 10 +++++----- sql/mysqld.cc | 2 +- sql/sql_repl.cc | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/mysql-test/r/rpl_log.result b/mysql-test/r/rpl_log.result index 7813d4d779d..9fcab2a7cbe 100644 --- a/mysql-test/r/rpl_log.result +++ b/mysql-test/r/rpl_log.result @@ -68,12 +68,12 @@ master-bin.000002 168 Query 1 168 use `test`; insert into t1 values (1) master-bin.000002 228 Query 1 228 use `test`; drop table t1 show binary logs; Log_name File_size -master-bin.000001 0 +master-bin.000001 1171 master-bin.000002 276 start slave; show binary logs; Log_name File_size -slave-bin.000001 0 +slave-bin.000001 1285 slave-bin.000002 170 show binlog events in 'slave-bin.000001' from 4; Log_name Pos Event_type Server_id Orig_log_pos Info diff --git a/mysql-test/r/rpl_rotate_logs.result b/mysql-test/r/rpl_rotate_logs.result index 66eef482a63..9f74cdb9502 100644 --- a/mysql-test/r/rpl_rotate_logs.result +++ b/mysql-test/r/rpl_rotate_logs.result @@ -27,8 +27,8 @@ insert into t2 values (34),(67),(123); flush logs; show binary logs; Log_name File_size -master-bin.000001 0 -master-bin.000002 0 +master-bin.000001 461 +master-bin.000002 213 master-bin.000003 4 create table t3 select * from temp_table; select * from t3; @@ -43,12 +43,12 @@ start slave; purge master logs to 'master-bin.000002'; show master logs; Log_name File_size -master-bin.000002 0 +master-bin.000002 213 master-bin.000003 229 purge binary logs to 'master-bin.000002'; show binary logs; Log_name File_size -master-bin.000002 0 +master-bin.000002 213 master-bin.000003 229 purge master logs before now(); show binary logs; @@ -74,7 +74,7 @@ count(*) create table t4 select * from temp_table; show binary logs; Log_name File_size -master-bin.000003 0 +master-bin.000003 4167 master-bin.000004 2886 show master status; File Position Binlog_Do_DB Binlog_Ignore_DB diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 02a775f64d1..3f4b002e369 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -663,7 +663,7 @@ static void close_connections(void) project. This will wake up the socket on Windows and prevent the printing of the error message that we are force closing a connection. */ - close_connection(tmp, 0, 1); + close_connection(tmp, 0, 0); if (tmp->mysys_var) { tmp->mysys_var->abort=1; diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index fe95ee26748..963c4ccf5a6 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -1384,7 +1384,7 @@ int show_binlogs(THD* thd) else { /* this is an old log, open it and find the size */ - if ((file= my_open(fname+dir_len, O_RDONLY | O_SHARE | O_BINARY, + if ((file= my_open(fname, O_RDONLY | O_SHARE | O_BINARY, MYF(0))) >= 0) { file_length= (ulonglong) my_seek(file, 0L, MY_SEEK_END, MYF(0)); From 8b3d39163af4075b039638ebad86ae21cbc81323 Mon Sep 17 00:00:00 2001 From: "konstantin@mysql.com" <> Date: Mon, 18 Jul 2005 18:03:58 +0400 Subject: [PATCH 30/48] Alleviate the autoconf requirement. --- configure.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.in b/configure.in index af7ce8b1c3d..b270e5b0013 100644 --- a/configure.in +++ b/configure.in @@ -118,7 +118,7 @@ AC_SUBST(SAVE_LDFLAGS) AC_SUBST(SAVE_CXXLDFLAGS) AC_SUBST(CXXLDFLAGS) -AC_PREREQ(2.58)dnl Minimum Autoconf version required. +AC_PREREQ(2.57)dnl Minimum Autoconf version required. #AC_ARG_PROGRAM # Automaticly invoked by AM_INIT_AUTOMAKE AM_SANITY_CHECK From 6e9447a80e1b3dbe9a29fa6b375bd9b2d00ba344 Mon Sep 17 00:00:00 2001 From: "evgen@moonbone.local" <> Date: Mon, 18 Jul 2005 18:30:19 +0400 Subject: [PATCH 31/48] Fix bug#11482 4.1.12 produces different resultset for a complex query than in previous 4.1.x Wrongly applied optimization were adding NOT NULL constraint which results in rejecting valid rows and reduced result set. The problem was that add_notnull_conds() while checking subquery were adding NOT NULL constraint to left joined table, to which, normally, optimization don't have to be applied. --- mysql-test/r/select.result | 11 +++++++++++ mysql-test/t/select.test | 11 +++++++++++ sql/sql_select.cc | 20 +++++++++++++++++++- 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result index 8160c5a2f3d..5c0616f9e54 100644 --- a/mysql-test/r/select.result +++ b/mysql-test/r/select.result @@ -2559,3 +2559,14 @@ WHERE COUNT(*) 4 drop table t1,t2,t3; +create table t1 (f1 int); +insert into t1 values (1),(NULL); +create table t2 (f2 int, f3 int, f4 int); +create index idx1 on t2 (f4); +insert into t2 values (1,2,3),(2,4,6); +select A.f2 from t1 left join t2 A on A.f2 = f1 where A.f3=(select min(f3) +from t2 C where A.f4 = C.f4) or A.f3 IS NULL; +f2 +1 +NULL +drop table t1,t2; diff --git a/mysql-test/t/select.test b/mysql-test/t/select.test index 37e4324152b..2e261d0611f 100644 --- a/mysql-test/t/select.test +++ b/mysql-test/t/select.test @@ -2116,3 +2116,14 @@ WHERE drop table t1,t2,t3; +# +# Bug #11482 4.1.12 produces different resultset for a complex query +# than in previous 4.1.x +create table t1 (f1 int); +insert into t1 values (1),(NULL); +create table t2 (f2 int, f3 int, f4 int); +create index idx1 on t2 (f4); +insert into t2 values (1,2,3),(2,4,6); +select A.f2 from t1 left join t2 A on A.f2 = f1 where A.f3=(select min(f3) +from t2 C where A.f4 = C.f4) or A.f3 IS NULL; +drop table t1,t2; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 75fc189b21f..43cf649685e 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -3508,7 +3508,23 @@ inline void add_cond_and_fix(Item **e1, Item *e2) (where othertbl is a non-const table and othertbl.field may be NULL) and add them to conditions on correspoding tables (othertbl in this example). - + + Exception from that is the case when referred_tab->join != join. + I.e. don't add NOT NULL constraints from any embedded subquery. + Consider this query: + SELECT A.f2 FROM t1 LEFT JOIN t2 A ON A.f2 = f1 + WHERE A.f3=(SELECT MIN(f3) FROM t2 C WHERE A.f4 = C.f4) OR A.f3 IS NULL; + Here condition A.f3 IS NOT NULL is going to be added to the WHERE + condition of the embedding query. + Another example: + SELECT * FROM t10, t11 WHERE (t10.a < 10 OR t10.a IS NULL) + AND t11.b <=> t10.b AND (t11.a = (SELECT MAX(a) FROM t12 + WHERE t12.b = t10.a )); + Here condition t10.a IS NOT NULL is going to be added. + In both cases addition of NOT NULL condition will erroneously reject + some rows of the result set. + referred_tab->join != join constraint would disallow such additions. + This optimization doesn't affect the choices that ref, range, or join optimizer make. This was intentional because this was added after 4.1 was GA. @@ -3539,6 +3555,8 @@ static void add_not_null_conds(JOIN *join) DBUG_ASSERT(item->type() == Item::FIELD_ITEM); Item_field *not_null_item= (Item_field*)item; JOIN_TAB *referred_tab= not_null_item->field->table->reginfo.join_tab; + if (referred_tab->join != join) + continue; Item *notnull; if (!(notnull= new Item_func_isnotnull(not_null_item))) DBUG_VOID_RETURN; From 0fda7038f9e474675d0fd88650e68508f4b09ceb Mon Sep 17 00:00:00 2001 From: "evgen@moonbone.local" <> Date: Mon, 18 Jul 2005 19:34:29 +0400 Subject: [PATCH 32/48] select.test: Changed test case comment to be more meaningful --- mysql-test/t/select.test | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mysql-test/t/select.test b/mysql-test/t/select.test index 69343524928..279e6070f06 100644 --- a/mysql-test/t/select.test +++ b/mysql-test/t/select.test @@ -2331,8 +2331,8 @@ WHERE drop table t1,t2,t3; # -# Bug #11482 4.1.12 produces different resultset for a complex query -# than in previous 4.1.x +# Bug #11482 Wrongly applied optimization was erroneously rejecting valid +# rows create table t1 (f1 int); insert into t1 values (1),(NULL); create table t2 (f2 int, f3 int, f4 int); From 21ae963205998ea905601c2fe7450404b7ab37c8 Mon Sep 17 00:00:00 2001 From: "monty@mishka.local" <> Date: Mon, 18 Jul 2005 20:05:30 +0300 Subject: [PATCH 33/48] Fixes to previous changeset get_defaults_file() -> get_defaults_options() --- include/my_sys.h | 1 - libmysql/libmysql.def | 2 +- libmysqld/libmysqld.def | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/include/my_sys.h b/include/my_sys.h index 4c385e9d1b3..6747733da9c 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -553,7 +553,6 @@ extern gptr my_once_alloc(uint Size,myf MyFlags); extern void my_once_free(void); extern char *my_once_strdup(const char *src,myf myflags); extern char *my_once_memdup(const char *src, uint len, myf myflags); -extern my_string my_tempnam(const char *dir,const char *pfx,myf MyFlags); extern File my_open(const char *FileName,int Flags,myf MyFlags); extern File my_register_filename(File fd, const char *FileName, enum file_type type_of_file, diff --git a/libmysql/libmysql.def b/libmysql/libmysql.def index 0688ea5732b..a469c67c466 100644 --- a/libmysql/libmysql.def +++ b/libmysql/libmysql.def @@ -150,5 +150,5 @@ EXPORTS mysql_server_end mysql_set_character_set mysql_get_character_set_info - get_defaults_files + get_defaults_options modify_defaults_file diff --git a/libmysqld/libmysqld.def b/libmysqld/libmysqld.def index dcb14e95d36..93456901a7d 100644 --- a/libmysqld/libmysqld.def +++ b/libmysqld/libmysqld.def @@ -158,7 +158,7 @@ EXPORTS mysql_stmt_attr_get mysql_stmt_attr_set mysql_stmt_field_count - get_defaults_files + get_defaults_options my_charset_bin my_charset_same modify_defaults_file From 086c3206f0e4236e9645eb41f7171bb172ef37b3 Mon Sep 17 00:00:00 2001 From: "konstantin@mysql.com" <> Date: Mon, 18 Jul 2005 21:33:05 +0400 Subject: [PATCH 34/48] Fix BUILD/autorun.sh to really bail out on error. --- BUILD/autorun.sh | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/BUILD/autorun.sh b/BUILD/autorun.sh index 3adfafb0ce6..47a80a709a8 100755 --- a/BUILD/autorun.sh +++ b/BUILD/autorun.sh @@ -1,21 +1,21 @@ #!/bin/sh # Create MySQL autotools infrastructure -aclocal || (echo "Can't execute aclocal" && exit 1) -autoheader || (echo "Can't execute autoheader" && exit 1) +die() { echo "$@"; exit 1; } + +aclocal || die "Can't execute aclocal" +autoheader || die "Can't execute autoheader" # --force means overwrite ltmain.sh script if it already exists # Added glibtoolize reference to make native OSX autotools work if [ -f /usr/bin/glibtoolize ] ; then - glibtoolize --automake --force \ - || (echo "Can't execute glibtoolize" && exit 1) + glibtoolize --automake --force || die "Can't execute glibtoolize" else - libtoolize --automake --force \ - || (echo "Can't execute libtoolize" && exit 1) + libtoolize --automake --force || die "Can't execute libtoolize" fi + # --add-missing instructs automake to install missing auxiliary files # and --force to overwrite them if they already exist -automake --add-missing --force \ - || (echo "Can't execute automake" && exit 1) -autoconf || (echo "Can't execute autoconf" && exit 1) +automake --add-missing --force || die "Can't execute automake" +autoconf || die "Can't execute autoconf" (cd bdb/dist && sh s_all) (cd innobase && aclocal && autoheader && aclocal && automake && autoconf) From 8e60817ddabccca1757283c992317619dd798567 Mon Sep 17 00:00:00 2001 From: "shuichi@mysql.com" <> Date: Mon, 18 Jul 2005 13:38:56 -0700 Subject: [PATCH 35/48] Fixed the ucs2 -> eucjpms conversion bug (bug#11717) --- mysql-test/r/ctype_eucjpms.result | 14 ++++++++++++++ mysql-test/t/ctype_eucjpms.test | 12 ++++++++++++ strings/ctype-eucjpms.c | 2 +- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/ctype_eucjpms.result b/mysql-test/r/ctype_eucjpms.result index a07ff9b28be..cdb28cd0fdd 100755 --- a/mysql-test/r/ctype_eucjpms.result +++ b/mysql-test/r/ctype_eucjpms.result @@ -9785,6 +9785,20 @@ DROP TABLE t1; DROP TABLE t2; DROP TABLE t3; DROP TABLE t4; +CREATE TABLE t1(c1 varchar(10)) default character set = eucjpms; +insert into t1 values(_ucs2 0x00F7); +insert into t1 values(_eucjpms 0xA1E0); +insert into t1 values(_ujis 0xA1E0); +insert into t1 values(_sjis 0x8180); +insert into t1 values(_cp932 0x8180); +SELECT HEX(c1) FROM t1; +HEX(c1) +A1E0 +A1E0 +A1E0 +A1E0 +A1E0 +DROP TABLE t1; SET collation_connection='eucjpms_japanese_ci'; create table t1 select repeat('a',4000) a; delete from t1; diff --git a/mysql-test/t/ctype_eucjpms.test b/mysql-test/t/ctype_eucjpms.test index cec1e2a9861..3609407fe96 100644 --- a/mysql-test/t/ctype_eucjpms.test +++ b/mysql-test/t/ctype_eucjpms.test @@ -346,6 +346,18 @@ DROP TABLE t2; DROP TABLE t3; DROP TABLE t4; +#Test bug#11717 +CREATE TABLE t1(c1 varchar(10)) default character set = eucjpms; + +insert into t1 values(_ucs2 0x00F7); +insert into t1 values(_eucjpms 0xA1E0); +insert into t1 values(_ujis 0xA1E0); +insert into t1 values(_sjis 0x8180); +insert into t1 values(_cp932 0x8180); + +SELECT HEX(c1) FROM t1; + +DROP TABLE t1; SET collation_connection='eucjpms_japanese_ci'; -- source include/ctype_filesort.inc diff --git a/strings/ctype-eucjpms.c b/strings/ctype-eucjpms.c index 58caac15256..2ab103d65ac 100644 --- a/strings/ctype-eucjpms.c +++ b/strings/ctype-eucjpms.c @@ -1584,7 +1584,7 @@ static uint16 tab_uni_jisx02082[]={ /* page 3 0x00F7-0x00F7 */ static uint16 tab_uni_jisx02083[]={ -0x07}; +0x2160}; /* page 4 0x0391-0x03C9 */ static uint16 tab_uni_jisx02084[]={ From 22be12ab86e0114b8a72c4962380c25bfe57b10c Mon Sep 17 00:00:00 2001 From: "elliot@mysql.com" <> Date: Mon, 18 Jul 2005 17:47:26 -0400 Subject: [PATCH 36/48] BUG#11567 Fixed binlog tests on windows. --- sql/log_event.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/log_event.cc b/sql/log_event.cc index 0873ee50743..90837f98d6a 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -242,7 +242,7 @@ static void print_set_option(FILE* file, uint32 bits_changed, uint32 option, { if (*need_comma) fprintf(file,", "); - fprintf(file,"%s=%d", name, (bool)(flags & option)); + fprintf(file,"%s=%d", name, test(flags & option)); *need_comma= 1; } } From 52ba7b69d7ae26fc1f370e70332d0a272a1f0748 Mon Sep 17 00:00:00 2001 From: "konstantin@mysql.com" <> Date: Tue, 19 Jul 2005 03:01:25 +0400 Subject: [PATCH 37/48] After-merge fixes. --- configure.in | 6 +++--- mysql-test/r/olap.result | 1 + mysql-test/r/rpl_log.result | 4 ++-- mysql-test/r/rpl_rotate_logs.result | 12 ++++++------ mysql-test/t/olap.test | 1 + 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/configure.in b/configure.in index d34d8bd75e7..12293ecf5ec 100644 --- a/configure.in +++ b/configure.in @@ -1965,7 +1965,7 @@ AC_LANG_CPLUSPLUS if test "$ac_cv_prog_gxx" = "yes" -a "$with_other_libc" = "no" then - CXXFLAGS=`echo "$CXXFLAGS -Werror" | sed -e 's/-fbranch-probabilities//; s/-Wall//; s/-Wcheck//'` + CXXFLAGS=`echo "$CXXFLAGS -Werror" | sed -e 's/-fbranch-probabilities//; s/-Wall//; s/-ansi//; s/-pedantic//; s/-Wcheck//'` fi AC_TRY_COMPILE( @@ -2000,7 +2000,7 @@ AC_LANG_SAVE AC_LANG_CPLUSPLUS if test "$ac_cv_prog_gxx" = "yes" -a "$with_other_libc" = "no" then - CXXFLAGS=`echo "$CXXFLAGS -Werror" | sed -e 's/-fbranch-probabilities//; s/-Wall//; s/-Wcheck//'` + CXXFLAGS=`echo "$CXXFLAGS -Werror" | sed -e 's/-fbranch-probabilities//; s/-Wall//; s/-ansi//; s/-pedantic//; s/-Wcheck//'` fi AC_TRY_COMPILE( [#undef inline @@ -2033,7 +2033,7 @@ AC_LANG_SAVE AC_LANG_CPLUSPLUS if test "$ac_cv_prog_gxx" = "yes" -a "$with_other_libc" = "no" then - CXXFLAGS=`echo "$CXXFLAGS -Werror" | sed -e 's/-fbranch-probabilities//; s/-Wall//; s/-Wcheck//'` + CXXFLAGS=`echo "$CXXFLAGS -Werror" | sed -e 's/-fbranch-probabilities//; s/-Wall//; s/-ansi//; s/-pedantic//; s/-Wcheck//'` fi AC_TRY_COMPILE( [#undef inline diff --git a/mysql-test/r/olap.result b/mysql-test/r/olap.result index 0b422047a49..69871b2110b 100644 --- a/mysql-test/r/olap.result +++ b/mysql-test/r/olap.result @@ -576,6 +576,7 @@ EXPLAIN SELECT type FROM v1 GROUP BY type WITH ROLLUP; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 10 Using filesort DROP VIEW v1; +DROP TABLE t1; CREATE TABLE t1 (a INT(10) NOT NULL, b INT(10) NOT NULL); INSERT INTO t1 VALUES (1, 1); INSERT INTO t1 VALUES (1, 2); diff --git a/mysql-test/r/rpl_log.result b/mysql-test/r/rpl_log.result index bc2e3370019..be8d5291ad9 100644 --- a/mysql-test/r/rpl_log.result +++ b/mysql-test/r/rpl_log.result @@ -69,12 +69,12 @@ master-bin.000002 346 Query 1 434 use `test`; insert into t1 values (1) master-bin.000002 434 Query 1 510 use `test`; drop table t1 show binary logs; Log_name File_size -master-bin.000001 1171 +master-bin.000001 1389 master-bin.000002 510 start slave; show binary logs; Log_name File_size -slave-bin.000001 1285 +slave-bin.000001 1559 slave-bin.000002 348 show binlog events in 'slave-bin.000001' from 4; Log_name Pos Event_type Server_id End_log_pos Info diff --git a/mysql-test/r/rpl_rotate_logs.result b/mysql-test/r/rpl_rotate_logs.result index f1618e246fa..bf2ef98c87f 100644 --- a/mysql-test/r/rpl_rotate_logs.result +++ b/mysql-test/r/rpl_rotate_logs.result @@ -27,8 +27,8 @@ insert into t2 values (34),(67),(123); flush logs; show binary logs; Log_name File_size -master-bin.000001 461 -master-bin.000002 213 +master-bin.000001 592 +master-bin.000002 363 master-bin.000003 98 create table t3 select * from temp_table; select * from t3; @@ -43,12 +43,12 @@ start slave; purge master logs to 'master-bin.000002'; show master logs; Log_name File_size -master-bin.000002 213 +master-bin.000002 363 master-bin.000003 407 purge binary logs to 'master-bin.000002'; show binary logs; Log_name File_size -master-bin.000002 213 +master-bin.000002 363 master-bin.000003 407 purge master logs before now(); show binary logs; @@ -74,8 +74,8 @@ count(*) create table t4 select * from temp_table; show binary logs; Log_name File_size -master-bin.000003 4167 -master-bin.000004 0 +master-bin.000003 4185 +master-bin.000004 4190 master-bin.000005 2032 show master status; File Position Binlog_Do_DB Binlog_Ignore_DB diff --git a/mysql-test/t/olap.test b/mysql-test/t/olap.test index 78ea56c8090..f3017978588 100644 --- a/mysql-test/t/olap.test +++ b/mysql-test/t/olap.test @@ -265,6 +265,7 @@ SELECT type FROM v1 GROUP BY type WITH ROLLUP; EXPLAIN SELECT type FROM v1 GROUP BY type WITH ROLLUP; DROP VIEW v1; +DROP TABLE t1; # Test for bug #11543: ROLLUP query with a repeated column in GROUP BY # From 7cf58d1f86dbaeed7e88bda6a85b27db180875b2 Mon Sep 17 00:00:00 2001 From: "jimw@mysql.com" <> Date: Mon, 18 Jul 2005 17:22:38 -0700 Subject: [PATCH 38/48] Upgrade bundled readline to version 5.0. --- cmd-line-utils/readline/INSTALL | 2 +- cmd-line-utils/readline/Makefile.am | 3 +- cmd-line-utils/readline/README | 2 +- cmd-line-utils/readline/bind.c | 156 +++++++++---- cmd-line-utils/readline/callback.c | 6 +- cmd-line-utils/readline/chardefs.h | 6 +- cmd-line-utils/readline/compat.c | 113 +++++++++ cmd-line-utils/readline/complete.c | 304 +++++++++++++++++++----- cmd-line-utils/readline/configure.in | 15 +- cmd-line-utils/readline/display.c | 238 +++++++++++++------ cmd-line-utils/readline/funmap.c | 5 +- cmd-line-utils/readline/histexpand.c | 338 +++++++++++++++++---------- cmd-line-utils/readline/histfile.c | 137 ++++++++--- cmd-line-utils/readline/history.c | 94 ++++++-- cmd-line-utils/readline/history.h | 28 ++- cmd-line-utils/readline/histsearch.c | 10 +- cmd-line-utils/readline/input.c | 30 ++- cmd-line-utils/readline/isearch.c | 12 +- cmd-line-utils/readline/keymaps.c | 17 +- cmd-line-utils/readline/kill.c | 61 ++++- cmd-line-utils/readline/macro.c | 10 +- cmd-line-utils/readline/mbutil.c | 57 +++-- cmd-line-utils/readline/misc.c | 38 ++- cmd-line-utils/readline/nls.c | 41 +++- cmd-line-utils/readline/parens.c | 8 +- cmd-line-utils/readline/posixdir.h | 10 +- cmd-line-utils/readline/readline.c | 71 ++++-- cmd-line-utils/readline/readline.h | 63 +++-- cmd-line-utils/readline/rldefs.h | 6 +- cmd-line-utils/readline/rlmbutil.h | 19 +- cmd-line-utils/readline/rlprivate.h | 32 +-- cmd-line-utils/readline/rlstdc.h | 2 +- cmd-line-utils/readline/rltty.c | 175 +++++++++----- cmd-line-utils/readline/rltty.h | 32 +-- cmd-line-utils/readline/rltypedefs.h | 8 +- cmd-line-utils/readline/savestring.c | 37 +++ cmd-line-utils/readline/search.c | 24 +- cmd-line-utils/readline/shell.c | 8 +- cmd-line-utils/readline/signals.c | 12 +- cmd-line-utils/readline/terminal.c | 97 ++++---- cmd-line-utils/readline/text.c | 71 +++--- cmd-line-utils/readline/tilde.c | 8 +- cmd-line-utils/readline/undo.c | 11 +- cmd-line-utils/readline/util.c | 12 +- cmd-line-utils/readline/vi_mode.c | 136 ++++++----- cmd-line-utils/readline/xmalloc.c | 4 +- 46 files changed, 1841 insertions(+), 728 deletions(-) create mode 100644 cmd-line-utils/readline/compat.c create mode 100644 cmd-line-utils/readline/savestring.c diff --git a/cmd-line-utils/readline/INSTALL b/cmd-line-utils/readline/INSTALL index adb27a9f222..cb4a06fb701 100644 --- a/cmd-line-utils/readline/INSTALL +++ b/cmd-line-utils/readline/INSTALL @@ -1,7 +1,7 @@ Basic Installation ================== -These are installation instructions for Readline-4.3. +These are installation instructions for Readline-5.0. The simplest way to compile readline is: diff --git a/cmd-line-utils/readline/Makefile.am b/cmd-line-utils/readline/Makefile.am index 14d25ff62c0..f5e41b7e2e9 100644 --- a/cmd-line-utils/readline/Makefile.am +++ b/cmd-line-utils/readline/Makefile.am @@ -17,7 +17,8 @@ libreadline_a_SOURCES = readline.c funmap.c keymaps.c \ callback.c terminal.c xmalloc.c \ history.c histsearch.c histexpand.c \ histfile.c nls.c search.c \ - shell.c tilde.c misc.c text.c mbutil.c + shell.c tilde.c misc.c text.c mbutil.c \ + compat.c savestring.c pkginclude_HEADERS = readline.h chardefs.h keymaps.h \ history.h tilde.h rlmbutil.h rltypedefs.h rlprivate.h \ diff --git a/cmd-line-utils/readline/README b/cmd-line-utils/readline/README index 7aa939452fb..ac4e3a767f9 100644 --- a/cmd-line-utils/readline/README +++ b/cmd-line-utils/readline/README @@ -1,7 +1,7 @@ Introduction ============ -This is the Gnu Readline library, version 4.3. +This is the Gnu Readline library, version 5.0. The Readline library provides a set of functions for use by applications that allow users to edit command lines as they are typed in. Both diff --git a/cmd-line-utils/readline/bind.c b/cmd-line-utils/readline/bind.c index 0e8efc5c636..15d0e4e3a65 100644 --- a/cmd-line-utils/readline/bind.c +++ b/cmd-line-utils/readline/bind.c @@ -19,9 +19,16 @@ is generally kept in a file called COPYING or LICENSE. If you do not have a copy of the license, write to the Free Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ + #define READLINE_LIBRARY -#include "config_readline.h" +#if defined (__TANDEM) +# include +#endif + +#if defined (HAVE_CONFIG_H) +# include +#endif #include #include @@ -146,6 +153,34 @@ rl_bind_key_in_map (key, function, map) return (result); } +/* Bind key sequence KEYSEQ to DEFAULT_FUNC if KEYSEQ is unbound. Right + now, this is always used to attempt to bind the arrow keys, hence the + check for rl_vi_movement_mode. */ +int +rl_bind_key_if_unbound_in_map (key, default_func, kmap) + int key; + rl_command_func_t *default_func; + Keymap kmap; +{ + char keyseq[2]; + + keyseq[0] = (unsigned char)key; + keyseq[1] = '\0'; + return (rl_bind_keyseq_if_unbound_in_map (keyseq, default_func, kmap)); +} + +int +rl_bind_key_if_unbound (key, default_func) + int key; + rl_command_func_t *default_func; +{ + char keyseq[2]; + + keyseq[0] = (unsigned char)key; + keyseq[1] = '\0'; + return (rl_bind_keyseq_if_unbound_in_map (keyseq, default_func, _rl_keymap)); +} + /* Make KEY do nothing in the currently selected keymap. Returns non-zero in case of error. */ int @@ -197,10 +232,31 @@ rl_unbind_command_in_map (command, map) return (rl_unbind_function_in_map (func, map)); } +/* Bind the key sequence represented by the string KEYSEQ to + FUNCTION, starting in the current keymap. This makes new + keymaps as necessary. */ +int +rl_bind_keyseq (keyseq, function) + const char *keyseq; + rl_command_func_t *function; +{ + return (rl_generic_bind (ISFUNC, keyseq, (char *)function, _rl_keymap)); +} + /* Bind the key sequence represented by the string KEYSEQ to FUNCTION. This makes new keymaps as necessary. The initial place to do bindings is in MAP. */ int +rl_bind_keyseq_in_map (keyseq, function, map) + const char *keyseq; + rl_command_func_t *function; + Keymap map; +{ + return (rl_generic_bind (ISFUNC, keyseq, (char *)function, map)); +} + +/* Backwards compatibility; equivalent to rl_bind_keyseq_in_map() */ +int rl_set_key (keyseq, function, map) const char *keyseq; rl_command_func_t *function; @@ -209,6 +265,40 @@ rl_set_key (keyseq, function, map) return (rl_generic_bind (ISFUNC, keyseq, (char *)function, map)); } +/* Bind key sequence KEYSEQ to DEFAULT_FUNC if KEYSEQ is unbound. Right + now, this is always used to attempt to bind the arrow keys, hence the + check for rl_vi_movement_mode. */ +int +rl_bind_keyseq_if_unbound_in_map (keyseq, default_func, kmap) + const char *keyseq; + rl_command_func_t *default_func; + Keymap kmap; +{ + rl_command_func_t *func; + + if (keyseq) + { + func = rl_function_of_keyseq (keyseq, kmap, (int *)NULL); +#if defined (VI_MODE) + if (!func || func == rl_do_lowercase_version || func == rl_vi_movement_mode) +#else + if (!func || func == rl_do_lowercase_version) +#endif + return (rl_bind_keyseq_in_map (keyseq, default_func, kmap)); + else + return 1; + } + return 0; +} + +int +rl_bind_keyseq_if_unbound (keyseq, default_func) + const char *keyseq; + rl_command_func_t *default_func; +{ + return (rl_bind_keyseq_if_unbound_in_map (keyseq, default_func, _rl_keymap)); +} + /* Bind the key sequence represented by the string KEYSEQ to the string of characters MACRO. This makes new keymaps as necessary. The initial place to do bindings is in MAP. */ @@ -346,7 +436,7 @@ rl_translate_keyseq (seq, array, len) { register int i, c, l, temp; - for (i = l = 0; (c = seq[i]); i++) + for (i = l = 0; c = seq[i]; i++) { if (c == '\\') { @@ -678,7 +768,7 @@ _rl_read_file (filename, sizep) /* Re-read the current keybindings file. */ int rl_re_read_init_file (count, ignore) - int count __attribute__((unused)), ignore __attribute__((unused)); + int count, ignore; { int r; r = rl_read_init_file ((const char *)NULL); @@ -900,7 +990,7 @@ parser_if (args) /* Invert the current parser state if there is anything on the stack. */ static int parser_else (args) - char *args __attribute__((unused)); + char *args; { register int i; @@ -910,9 +1000,15 @@ parser_else (args) return 0; } +#if 0 /* Check the previous (n - 1) levels of the stack to make sure that we haven't previously turned off parsing. */ for (i = 0; i < if_stack_depth - 1; i++) +#else + /* Check the previous (n) levels of the stack to make sure that + we haven't previously turned off parsing. */ + for (i = 0; i < if_stack_depth; i++) +#endif if (if_stack[i] == 1) return 0; @@ -925,7 +1021,7 @@ parser_else (args) _rl_parsing_conditionalized_out from the stack. */ static int parser_endif (args) - char *args __attribute__((unused)); + char *args; { if (if_stack_depth) _rl_parsing_conditionalized_out = if_stack[--if_stack_depth]; @@ -1048,7 +1144,7 @@ rl_parse_and_bind (string) { int passc = 0; - for (i = 1; (c = string[i]); i++) + for (i = 1; c = string[i]; i++) { if (passc) { @@ -1124,7 +1220,7 @@ rl_parse_and_bind (string) { int delimiter = string[i++], passc; - for (passc = 0; (c = string[i]); i++) + for (passc = 0; c = string[i]; i++) { if (passc) { @@ -1159,7 +1255,7 @@ rl_parse_and_bind (string) } /* If this is a new-style key-binding, then do the binding with - rl_set_key (). Otherwise, let the older code deal with it. */ + rl_bind_keyseq (). Otherwise, let the older code deal with it. */ if (*string == '"') { char *seq; @@ -1198,7 +1294,7 @@ rl_parse_and_bind (string) rl_macro_bind (seq, &funname[1], _rl_keymap); } else - rl_set_key (seq, rl_named_function (funname), _rl_keymap); + rl_bind_keyseq (seq, rl_named_function (funname)); free (seq); return 0; @@ -1279,10 +1375,11 @@ static struct { { "prefer-visible-bell", &_rl_prefer_visible_bell, V_SPECIAL }, { "print-completions-horizontally", &_rl_print_completions_horizontally, 0 }, { "show-all-if-ambiguous", &_rl_complete_show_all, 0 }, + { "show-all-if-unmodified", &_rl_complete_show_unmodified, 0 }, #if defined (VISIBLE_STATS) { "visible-stats", &rl_visible_stats, 0 }, #endif /* VISIBLE_STATS */ - { (char *)NULL, (int *)NULL, 0 } + { (char *)NULL, (int *)NULL } }; static int @@ -1351,7 +1448,7 @@ static struct { { "editing-mode", V_STRING, sv_editmode }, { "isearch-terminators", V_STRING, sv_isrchterm }, { "keymap", V_STRING, sv_keymap }, - { (char *)NULL, 0, 0 } + { (char *)NULL, 0 } }; static int @@ -1626,8 +1723,7 @@ rl_set_keymap_from_edit_mode () #endif /* VI_MODE */ } - -const char * +char * rl_get_keymap_name_from_edit_mode () { if (rl_editing_mode == emacs_mode) @@ -1637,7 +1733,7 @@ rl_get_keymap_name_from_edit_mode () return "vi"; #endif /* VI_MODE */ else - return "nope"; + return "none"; } /* **************************************************************** */ @@ -1649,7 +1745,7 @@ rl_get_keymap_name_from_edit_mode () /* Each of the following functions produces information about the state of keybindings and functions known to Readline. The info is always printed to rl_outstream, and in such a way that it can - be read back in (i.e., passed to rl_parse_and_bind (). */ + be read back in (i.e., passed to rl_parse_and_bind ()). */ /* Print the names of functions known to Readline. */ void @@ -1872,7 +1968,7 @@ rl_function_dumper (print_readably) fprintf (rl_outstream, "\n"); - for (i = 0; (name = names[i]); i++) + for (i = 0; name = names[i]; i++) { rl_command_func_t *function; char **invokers; @@ -1932,7 +2028,7 @@ rl_function_dumper (print_readably) the output in such a way that it can be read back in. */ int rl_dump_functions (count, key) - int count __attribute__((unused)), key __attribute__((unused)); + int count, key; { if (rl_dispatching) fprintf (rl_outstream, "\r\n"); @@ -2012,7 +2108,7 @@ rl_macro_dumper (print_readably) int rl_dump_macros (count, key) - int count __attribute__((unused)), key __attribute__((unused)); + int count, key; { if (rl_dispatching) fprintf (rl_outstream, "\r\n"); @@ -2102,7 +2198,7 @@ rl_variable_dumper (print_readably) the output in such a way that it can be read back in. */ int rl_dump_variables (count, key) - int count __attribute__((unused)), key __attribute__((unused)); + int count, key; { if (rl_dispatching) fprintf (rl_outstream, "\r\n"); @@ -2111,28 +2207,6 @@ rl_dump_variables (count, key) return (0); } -/* Bind key sequence KEYSEQ to DEFAULT_FUNC if KEYSEQ is unbound. Right - now, this is always used to attempt to bind the arrow keys, hence the - check for rl_vi_movement_mode. */ -void -_rl_bind_if_unbound (keyseq, default_func) - const char *keyseq; - rl_command_func_t *default_func; -{ - rl_command_func_t *func; - - if (keyseq) - { - func = rl_function_of_keyseq (keyseq, _rl_keymap, (int *)NULL); -#if defined (VI_MODE) - if (!func || func == rl_do_lowercase_version || func == rl_vi_movement_mode) -#else - if (!func || func == rl_do_lowercase_version) -#endif - rl_set_key (keyseq, default_func, _rl_keymap); - } -} - /* Return non-zero if any members of ARRAY are a substring in STRING. */ static int substring_member_of_array (string, array) diff --git a/cmd-line-utils/readline/callback.c b/cmd-line-utils/readline/callback.c index 737f483eed0..5b05ea5c5b7 100644 --- a/cmd-line-utils/readline/callback.c +++ b/cmd-line-utils/readline/callback.c @@ -21,7 +21,9 @@ 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#include "config_readline.h" +#if defined (HAVE_CONFIG_H) +# include +#endif #include "rlconf.h" @@ -129,7 +131,7 @@ rl_callback_read_char () if (in_handler == 0 && rl_linefunc) _rl_callback_newline (); } - if (rl_pending_input) + if (rl_pending_input || _rl_pushed_input_available ()) eof = readline_internal_char (); else break; diff --git a/cmd-line-utils/readline/chardefs.h b/cmd-line-utils/readline/chardefs.h index a537be220b0..cb04c982343 100644 --- a/cmd-line-utils/readline/chardefs.h +++ b/cmd-line-utils/readline/chardefs.h @@ -77,7 +77,11 @@ # define isxdigit(c) (isdigit((c)) || ((c) >= 'a' && (c) <= 'f') || ((c) >= 'A' && (c) <= 'F')) #endif -#define NON_NEGATIVE(c) ((unsigned char)(c) == (c)) +#if defined (CTYPE_NON_ASCII) +# define NON_NEGATIVE(c) 1 +#else +# define NON_NEGATIVE(c) ((unsigned char)(c) == (c)) +#endif /* Some systems define these; we want our definitions. */ #undef ISPRINT diff --git a/cmd-line-utils/readline/compat.c b/cmd-line-utils/readline/compat.c new file mode 100644 index 00000000000..a66d210fd2e --- /dev/null +++ b/cmd-line-utils/readline/compat.c @@ -0,0 +1,113 @@ +/* compat.c -- backwards compatibility functions. */ + +/* Copyright (C) 2000 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library, a library for + reading lines of text with interactive input and history editing. + + The GNU Readline Library 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, or + (at your option) any later version. + + The GNU Readline Library 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. + + The GNU General Public License is often shipped with GNU software, and + is generally kept in a file called COPYING or LICENSE. If you do not + have a copy of the license, write to the Free Software Foundation, + 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ +#define READLINE_LIBRARY + +#if defined (HAVE_CONFIG_H) +# include +#endif + +#include + +#include "rlstdc.h" +#include "rltypedefs.h" + +extern void rl_free_undo_list PARAMS((void)); +extern int rl_maybe_save_line PARAMS((void)); +extern int rl_maybe_unsave_line PARAMS((void)); +extern int rl_maybe_replace_line PARAMS((void)); + +extern int rl_crlf PARAMS((void)); +extern int rl_ding PARAMS((void)); +extern int rl_alphabetic PARAMS((int)); + +extern char **rl_completion_matches PARAMS((const char *, rl_compentry_func_t *)); +extern char *rl_username_completion_function PARAMS((const char *, int)); +extern char *rl_filename_completion_function PARAMS((const char *, int)); + +/* Provide backwards-compatible entry points for old function names. */ + +void +free_undo_list () +{ + rl_free_undo_list (); +} + +int +maybe_replace_line () +{ + return rl_maybe_replace_line (); +} + +int +maybe_save_line () +{ + return rl_maybe_save_line (); +} + +int +maybe_unsave_line () +{ + return rl_maybe_unsave_line (); +} + +int +ding () +{ + return rl_ding (); +} + +int +crlf () +{ + return rl_crlf (); +} + +int +alphabetic (c) + int c; +{ + return rl_alphabetic (c); +} + +char ** +completion_matches (s, f) + const char *s; + rl_compentry_func_t *f; +{ + return rl_completion_matches (s, f); +} + +char * +username_completion_function (s, i) + const char *s; + int i; +{ + return rl_username_completion_function (s, i); +} + +char * +filename_completion_function (s, i) + const char *s; + int i; +{ + return rl_filename_completion_function (s, i); +} diff --git a/cmd-line-utils/readline/complete.c b/cmd-line-utils/readline/complete.c index 749875e0e5e..d212f61840e 100644 --- a/cmd-line-utils/readline/complete.c +++ b/cmd-line-utils/readline/complete.c @@ -1,6 +1,6 @@ /* complete.c -- filename completion for readline. */ -/* Copyright (C) 1987, 1989, 1992 Free Software Foundation, Inc. +/* Copyright (C) 1987-2004 Free Software Foundation, Inc. This file is part of the GNU Readline Library, a library for reading lines of text with interactive input and history editing. @@ -21,12 +21,14 @@ 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#include "config_readline.h" +#if defined (HAVE_CONFIG_H) +# include +#endif #include #include #if defined (HAVE_SYS_FILE_H) -#include +# include #endif #if defined (HAVE_UNISTD_H) @@ -97,12 +99,16 @@ rl_compdisp_func_t *rl_completion_display_matches_hook = (rl_compdisp_func_t *)N static int stat_char PARAMS((char *)); #endif +static int path_isdir PARAMS((const char *)); + static char *rl_quote_filename PARAMS((char *, int, char *)); static void set_completion_defaults PARAMS((int)); static int get_y_or_n PARAMS((int)); static int _rl_internal_pager PARAMS((int)); static char *printable_part PARAMS((char *)); +static int fnwidth PARAMS((const char *)); +static int fnprint PARAMS((const char *)); static int print_filename PARAMS((char *, char *)); static char **gen_completion_matches PARAMS((char *, int, int, rl_compentry_func_t *, int, int)); @@ -128,6 +134,10 @@ static char *make_quoted_replacement PARAMS((char *, int, char *)); /* If non-zero, non-unique completions always show the list of matches. */ int _rl_complete_show_all = 0; +/* If non-zero, non-unique completions show the list of matches, unless it + is not possible to do partial completion and modify the line. */ +int _rl_complete_show_unmodified = 0; + /* If non-zero, completed directory names have a slash appended. */ int _rl_complete_mark_directories = 1; @@ -212,7 +222,12 @@ const char *rl_basic_quote_characters = "\"'"; /* The list of characters that signal a break between words for rl_complete_internal. The default list is the contents of rl_basic_word_break_characters. */ -const char *rl_completer_word_break_characters = (const char *)NULL; +/*const*/ char *rl_completer_word_break_characters = (/*const*/ char *)NULL; + +/* Hook function to allow an application to set the completion word + break characters before readline breaks up the line. Allows + position-dependent word break characters. */ +rl_cpvfunc_t *rl_completion_word_break_hook = (rl_cpvfunc_t *)NULL; /* List of characters which can be used to quote a substring of the line. Completion occurs on the entire substring, and within the substring @@ -280,6 +295,19 @@ int rl_completion_suppress_append = 0; default is a space. */ int rl_completion_append_character = ' '; +/* If non-zero, the completion functions don't append any closing quote. + This is set to 0 by rl_complete_internal and may be changed by an + application-specific completion function. */ +int rl_completion_suppress_quote = 0; + +/* Set to any quote character readline thinks it finds before any application + completion function is called. */ +int rl_completion_quote_character; + +/* Set to a non-zero value if readline found quoting anywhere in the word to + be completed; set before any application completion function is called. */ +int rl_completion_found_quote; + /* If non-zero, a slash will be appended to completed filenames that are symbolic links to directory names, subject to the value of the mark-directories variable (which is user-settable). This exists so @@ -318,6 +346,8 @@ rl_complete (ignore, invoking_key) return (rl_complete_internal ('?')); else if (_rl_complete_show_all) return (rl_complete_internal ('!')); + else if (_rl_complete_show_unmodified) + return (rl_complete_internal ('@')); else return (rl_complete_internal (TAB)); } @@ -325,14 +355,14 @@ rl_complete (ignore, invoking_key) /* List the possible completions. See description of rl_complete (). */ int rl_possible_completions (ignore, invoking_key) - int ignore __attribute__((unused)), invoking_key __attribute__((unused)); + int ignore, invoking_key; { return (rl_complete_internal ('?')); } int rl_insert_completions (ignore, invoking_key) - int ignore __attribute__((unused)), invoking_key __attribute__((unused)); + int ignore, invoking_key; { return (rl_complete_internal ('*')); } @@ -350,6 +380,8 @@ rl_completion_mode (cfunc) return '?'; else if (_rl_complete_show_all) return '!'; + else if (_rl_complete_show_unmodified) + return '@'; else return TAB; } @@ -370,7 +402,7 @@ set_completion_defaults (what_to_do) rl_filename_completion_desired = 0; rl_filename_quoting_desired = 1; rl_completion_type = what_to_do; - rl_completion_suppress_append = 0; + rl_completion_suppress_append = rl_completion_suppress_quote = 0; /* The completion entry function may optionally change this. */ rl_completion_mark_symlink_dirs = _rl_complete_mark_symlink_dirs; @@ -421,6 +453,15 @@ _rl_internal_pager (lines) return 0; } +static int +path_isdir (filename) + const char *filename; +{ + struct stat finfo; + + return (stat (filename, &finfo) == 0 && S_ISDIR (finfo.st_mode)); +} + #if defined (VISIBLE_STATS) /* Return the character which best describes FILENAME. `@' for symbolic links @@ -518,53 +559,140 @@ printable_part (pathname) return ++temp; } +/* Compute width of STRING when displayed on screen by print_filename */ +static int +fnwidth (string) + const char *string; +{ + int width, pos; +#if defined (HANDLE_MULTIBYTE) + mbstate_t ps; + int left, w; + size_t clen; + wchar_t wc; + + left = strlen (string) + 1; + memset (&ps, 0, sizeof (mbstate_t)); +#endif + + width = pos = 0; + while (string[pos]) + { + if (CTRL_CHAR (*string) || *string == RUBOUT) + { + width += 2; + pos++; + } + else + { +#if defined (HANDLE_MULTIBYTE) + clen = mbrtowc (&wc, string + pos, left - pos, &ps); + if (MB_INVALIDCH (clen)) + { + width++; + pos++; + memset (&ps, 0, sizeof (mbstate_t)); + } + else if (MB_NULLWCH (clen)) + break; + else + { + pos += clen; + w = wcwidth (wc); + width += (w >= 0) ? w : 1; + } +#else + width++; + pos++; +#endif + } + } + + return width; +} + +static int +fnprint (to_print) + const char *to_print; +{ + int printed_len; + const char *s; +#if defined (HANDLE_MULTIBYTE) + mbstate_t ps; + const char *end; + size_t tlen; + + end = to_print + strlen (to_print) + 1; + memset (&ps, 0, sizeof (mbstate_t)); +#endif + + printed_len = 0; + s = to_print; + while (*s) + { + if (CTRL_CHAR (*s)) + { + putc ('^', rl_outstream); + putc (UNCTRL (*s), rl_outstream); + printed_len += 2; + s++; +#if defined (HANDLE_MULTIBYTE) + memset (&ps, 0, sizeof (mbstate_t)); +#endif + } + else if (*s == RUBOUT) + { + putc ('^', rl_outstream); + putc ('?', rl_outstream); + printed_len += 2; + s++; +#if defined (HANDLE_MULTIBYTE) + memset (&ps, 0, sizeof (mbstate_t)); +#endif + } + else + { +#if defined (HANDLE_MULTIBYTE) + tlen = mbrlen (s, end - s, &ps); + if (MB_INVALIDCH (tlen)) + { + tlen = 1; + memset (&ps, 0, sizeof (mbstate_t)); + } + else if (MB_NULLWCH (tlen)) + break; + fwrite (s, 1, tlen, rl_outstream); + s += tlen; +#else + putc (*s, rl_outstream); + s++; +#endif + printed_len++; + } + } + + return printed_len; +} + /* Output TO_PRINT to rl_outstream. If VISIBLE_STATS is defined and we are using it, check for and output a single character for `special' filenames. Return the number of characters we output. */ -#define PUTX(c) \ - do { \ - if (CTRL_CHAR (c)) \ - { \ - putc ('^', rl_outstream); \ - putc (UNCTRL (c), rl_outstream); \ - printed_len += 2; \ - } \ - else if (c == RUBOUT) \ - { \ - putc ('^', rl_outstream); \ - putc ('?', rl_outstream); \ - printed_len += 2; \ - } \ - else \ - { \ - putc (c, rl_outstream); \ - printed_len++; \ - } \ - } while (0) - static int print_filename (to_print, full_pathname) char *to_print, *full_pathname; { - int printed_len = 0; -#if !defined (VISIBLE_STATS) - char *s; - - for (s = to_print; *s; s++) - { - PUTX (*s); - } -#else + int printed_len, extension_char, slen, tlen; char *s, c, *new_full_pathname; - int extension_char, slen, tlen; - for (s = to_print; *s; s++) - { - PUTX (*s); - } + extension_char = 0; + printed_len = fnprint (to_print); - if (rl_filename_completion_desired && rl_visible_stats) +#if defined (VISIBLE_STATS) + if (rl_filename_completion_desired && (rl_visible_stats || _rl_complete_mark_directories)) +#else + if (rl_filename_completion_desired && _rl_complete_mark_directories) +#endif { /* If to_print != full_pathname, to_print is the basename of the path passed. In this case, we try to expand the directory @@ -591,7 +719,13 @@ print_filename (to_print, full_pathname) new_full_pathname[slen] = '/'; strcpy (new_full_pathname + slen + 1, to_print); - extension_char = stat_char (new_full_pathname); +#if defined (VISIBLE_STATS) + if (rl_visible_stats) + extension_char = stat_char (new_full_pathname); + else +#endif + if (path_isdir (new_full_pathname)) + extension_char = '/'; free (new_full_pathname); to_print[-1] = c; @@ -599,7 +733,13 @@ print_filename (to_print, full_pathname) else { s = tilde_expand (full_pathname); - extension_char = stat_char (s); +#if defined (VISIBLE_STATS) + if (rl_visible_stats) + extension_char = stat_char (s); + else +#endif + if (path_isdir (s)) + extension_char = '/'; } free (s); @@ -609,14 +749,14 @@ print_filename (to_print, full_pathname) printed_len++; } } -#endif /* VISIBLE_STATS */ + return printed_len; } static char * rl_quote_filename (s, rtype, qcp) char *s; - int rtype __attribute__((unused)); + int rtype; char *qcp; { char *r; @@ -649,19 +789,32 @@ _rl_find_completion_word (fp, dp) int *fp, *dp; { int scan, end, found_quote, delimiter, pass_next, isbrk; - char quote_char; + char quote_char, *brkchars; end = rl_point; found_quote = delimiter = 0; quote_char = '\0'; + brkchars = 0; + if (rl_completion_word_break_hook) + brkchars = (*rl_completion_word_break_hook) (); + if (brkchars == 0) + brkchars = rl_completer_word_break_characters; + if (rl_completer_quote_characters) { /* We have a list of characters which can be used in pairs to quote substrings for the completer. Try to find the start of an unclosed quoted substring. */ /* FOUND_QUOTE is set so we know what kind of quotes we found. */ +#if defined (HANDLE_MULTIBYTE) + for (scan = pass_next = 0; scan < end; + scan = ((MB_CUR_MAX == 1 || rl_byte_oriented) + ? (scan + 1) + : _rl_find_next_mbchar (rl_line_buffer, scan, 1, MB_FIND_ANY))) +#else for (scan = pass_next = 0; scan < end; scan++) +#endif { if (pass_next) { @@ -719,7 +872,7 @@ _rl_find_completion_word (fp, dp) { scan = rl_line_buffer[rl_point]; - if (strchr (rl_completer_word_break_characters, scan) == 0) + if (strchr (brkchars, scan) == 0) continue; /* Call the application-specific function to tell us whether @@ -747,9 +900,9 @@ _rl_find_completion_word (fp, dp) if (rl_char_is_quoted_p) isbrk = (found_quote == 0 || (*rl_char_is_quoted_p) (rl_line_buffer, rl_point) == 0) && - strchr (rl_completer_word_break_characters, scan) != 0; + strchr (brkchars, scan) != 0; else - isbrk = strchr (rl_completer_word_break_characters, scan) != 0; + isbrk = strchr (brkchars, scan) != 0; if (isbrk) { @@ -784,6 +937,9 @@ gen_completion_matches (text, start, end, our_func, found_quote, quote_char) { char **matches, *temp; + rl_completion_found_quote = found_quote; + rl_completion_quote_character = quote_char; + /* If the user wants to TRY to complete, but then wants to give up and use the default completion function, they set the variable rl_attempted_completion_function. */ @@ -887,6 +1043,7 @@ compute_lcd_of_matches (match_list, matches, text) { register int i, c1, c2, si; int low; /* Count of max-matched characters. */ + char *dtext; /* dequoted TEXT, if needed */ #if defined (HANDLE_MULTIBYTE) int v; mbstate_t ps1, ps2; @@ -978,6 +1135,26 @@ compute_lcd_of_matches (match_list, matches, text) the user typed in the face of multiple matches differing in case. */ if (_rl_completion_case_fold) { + /* We're making an assumption here: + IF we're completing filenames AND + the application has defined a filename dequoting function AND + we found a quote character AND + the application has requested filename quoting + THEN + we assume that TEXT was dequoted before checking against + the file system and needs to be dequoted here before we + check against the list of matches + FI */ + dtext = (char *)NULL; + if (rl_filename_completion_desired && + rl_filename_dequoting_function && + rl_completion_found_quote && + rl_filename_quoting_desired) + { + dtext = (*rl_filename_dequoting_function) (text, rl_completion_quote_character); + text = dtext; + } + /* sort the list to get consistent answers. */ qsort (match_list+1, matches, sizeof(char *), (QSFUNC *)_rl_qsort_string_compare); @@ -997,6 +1174,8 @@ compute_lcd_of_matches (match_list, matches, text) else /* otherwise, just use the text the user typed. */ strncpy (match_list[0], text, low); + + FREE (dtext); } else strncpy (match_list[0], match_list[1], low); @@ -1201,7 +1380,7 @@ display_matches (matches) for (max = 0, i = 1; matches[i]; i++) { temp = printable_part (matches[i]); - len = strlen (temp); + len = fnwidth (temp); if (len > max) max = len; @@ -1336,7 +1515,8 @@ append_to_match (text, delimiter, quote_char, nontrivial_match) struct stat finfo; temp_string_index = 0; - if (quote_char && rl_point && rl_line_buffer[rl_point - 1] != quote_char) + if (quote_char && rl_point && rl_completion_suppress_quote == 0 && + rl_line_buffer[rl_point - 1] != quote_char) temp_string[temp_string_index++] = quote_char; if (delimiter) @@ -1447,7 +1627,9 @@ _rl_free_match_list (matches) TAB means do standard completion. `*' means insert all of the possible completions. `!' means to do standard completion, and list all possible completions if - there is more than one. */ + there is more than one. + `@' means to do standard completion, and list all possible completions if + there is more than one and partial completion is not possible. */ int rl_complete_internal (what_to_do) int what_to_do; @@ -1466,7 +1648,6 @@ rl_complete_internal (what_to_do) our_func = rl_completion_entry_function ? rl_completion_entry_function : rl_filename_completion_function; - /* We now look backwards for the start of a filename/variable word. */ end = rl_point; found_quote = delimiter = 0; @@ -1514,6 +1695,7 @@ rl_complete_internal (what_to_do) { case TAB: case '!': + case '@': /* Insert the first match with proper quoting. */ if (*matches[0]) insert_match (matches[0], start, matches[1] ? MULT_MATCH : SINGLE_MATCH, "e_char); @@ -1533,6 +1715,12 @@ rl_complete_internal (what_to_do) display_matches (matches); break; } + else if (what_to_do == '@') + { + if (nontrivial_lcd == 0) + display_matches (matches); + break; + } else if (rl_editing_mode != vi_mode) rl_ding (); /* There are other matches remaining. */ } @@ -1610,7 +1798,7 @@ rl_completion_matches (text, entry_function) match_list = (char **)xmalloc ((match_list_size + 1) * sizeof (char *)); match_list[1] = (char *)NULL; - while ((string = (*entry_function) (text, matches))) + while (string = (*entry_function) (text, matches)) { if (matches + 1 == match_list_size) match_list = (char **)xrealloc @@ -1660,7 +1848,7 @@ rl_username_completion_function (text, state) setpwent (); } - while ((entry = getpwent ())) + while (entry = getpwent ()) { /* Null usernames should result in all users as possible completions. */ if (namelen == 0 || (STREQN (username, entry->pw_name, namelen))) @@ -1897,7 +2085,7 @@ rl_filename_completion_function (text, state) ring the bell, and reset the counter to zero. */ int rl_menu_complete (count, ignore) - int count, ignore __attribute__((unused)); + int count, ignore; { rl_compentry_func_t *our_func; int matching_filenames, found_quote; diff --git a/cmd-line-utils/readline/configure.in b/cmd-line-utils/readline/configure.in index bc78f8a0f60..31e17606024 100644 --- a/cmd-line-utils/readline/configure.in +++ b/cmd-line-utils/readline/configure.in @@ -4,9 +4,9 @@ dnl dnl report bugs to chet@po.cwru.edu dnl dnl Process this file with autoconf to produce a configure script. -AC_REVISION([for Readline 4.3, version 2.45, from autoconf version] AC_ACVERSION) +AC_REVISION([for Readline 5.0, version 2.52, from autoconf version] AC_ACVERSION) -AC_INIT(readline, 4.3, bug-readline@gnu.org) +AC_INIT(readline, 5.0-rc1, bug-readline@gnu.org) dnl make sure we are using a recent autoconf version AC_PREREQ(2.50) @@ -16,7 +16,7 @@ AC_CONFIG_AUX_DIR(./support) AC_CONFIG_HEADERS(config.h) dnl update the value of RL_READLINE_VERSION in readline.h when this changes -LIBVERSION=4.3 +LIBVERSION=5.0 AC_CANONICAL_HOST @@ -31,12 +31,18 @@ if test "$opt_curses" = "yes"; then fi dnl option parsing for optional features +opt_multibyte=yes opt_static_libs=yes opt_shared_libs=yes +AC_ARG_ENABLE(multibyte, AC_HELP_STRING([--enable-multibyte], [enable multibyte characters if OS supports them]), opt_multibyte=$enableval) AC_ARG_ENABLE(shared, AC_HELP_STRING([--enable-shared], [build shared libraries [[default=YES]]]), opt_shared_libs=$enableval) AC_ARG_ENABLE(static, AC_HELP_STRING([--enable-static], [build static libraries [[default=YES]]]), opt_static_libs=$enableval) +if test $opt_multibyte = no; then +AC_DEFINE(NO_MULTIBYTE_SUPPORT) +fi + echo "" echo "Beginning configuration for readline-$LIBVERSION for ${host_cpu}-${host_vendor}-${host_os}" echo "" @@ -72,6 +78,8 @@ AC_TYPE_SIGNAL AC_TYPE_SIZE_T AC_CHECK_TYPE(ssize_t, int) +AC_HEADER_STDC + AC_HEADER_STAT AC_HEADER_DIRENT @@ -90,6 +98,7 @@ BASH_SYS_REINSTALL_SIGHANDLERS BASH_FUNC_POSIX_SETJMP BASH_FUNC_LSTAT BASH_FUNC_STRCOLL +BASH_FUNC_CTYPE_NONASCII BASH_CHECK_GETPW_FUNCS diff --git a/cmd-line-utils/readline/display.c b/cmd-line-utils/readline/display.c index 7c393f1c8a5..0ff428e44e9 100644 --- a/cmd-line-utils/readline/display.c +++ b/cmd-line-utils/readline/display.c @@ -1,6 +1,6 @@ /* display.c -- readline redisplay facility. */ -/* Copyright (C) 1987, 1989, 1992 Free Software Foundation, Inc. +/* Copyright (C) 1987-2004 Free Software Foundation, Inc. This file is part of the GNU Readline Library, a library for reading lines of text with interactive input and history editing. @@ -21,7 +21,9 @@ 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#include "config_readline.h" +#if defined (HAVE_CONFIG_H) +# include +#endif #include @@ -119,7 +121,7 @@ int _rl_suppress_redisplay = 0; /* The stuff that gets printed out before the actual text of the line. This is usually pointing to rl_prompt. */ -const char *rl_display_prompt = (char *)NULL; +char *rl_display_prompt = (char *)NULL; /* Pseudo-global variables declared here. */ /* The visible cursor position. If you print some text, adjust this. */ @@ -176,12 +178,15 @@ static int prompt_invis_chars_first_line; static int prompt_last_screen_line; +static int prompt_physical_chars; + /* Expand the prompt string S and return the number of visible characters in *LP, if LP is not null. This is currently more-or-less a placeholder for expansion. LIP, if non-null is a place to store the index of the last invisible character in the returned string. NIFLP, if non-zero, is a place to store the number of invisible characters in - the first prompt line. */ + the first prompt line. The previous are used as byte counts -- indexes + into a character buffer. */ /* Current implementation: \001 (^A) start non-visible characters @@ -191,19 +196,25 @@ static int prompt_last_screen_line; \002 are assumed to be `visible'. */ static char * -expand_prompt (pmt, lp, lip, niflp) +expand_prompt (pmt, lp, lip, niflp, vlp) char *pmt; - int *lp, *lip, *niflp; + int *lp, *lip, *niflp, *vlp; { char *r, *ret, *p; - int l, rl, last, ignoring, ninvis, invfl; + int l, rl, last, ignoring, ninvis, invfl, ind, pind, physchars; /* Short-circuit if we can. */ - if (strchr (pmt, RL_PROMPT_START_IGNORE) == 0) + if ((MB_CUR_MAX <= 1 || rl_byte_oriented) && strchr (pmt, RL_PROMPT_START_IGNORE) == 0) { r = savestring (pmt); if (lp) *lp = strlen (r); + if (lip) + *lip = 0; + if (niflp) + *niflp = 0; + if (vlp) + *vlp = lp ? *lp : strlen (r); return r; } @@ -212,7 +223,7 @@ expand_prompt (pmt, lp, lip, niflp) invfl = 0; /* invisible chars in first line of prompt */ - for (rl = ignoring = last = ninvis = 0, p = pmt; p && *p; p++) + for (rl = ignoring = last = ninvis = physchars = 0, p = pmt; p && *p; p++) { /* This code strips the invisible character string markers RL_PROMPT_START_IGNORE and RL_PROMPT_END_IGNORE */ @@ -229,13 +240,35 @@ expand_prompt (pmt, lp, lip, niflp) } else { - *r++ = *p; - if (!ignoring) - rl++; +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + { + pind = p - pmt; + ind = _rl_find_next_mbchar (pmt, pind, 1, MB_FIND_NONZERO); + l = ind - pind; + while (l--) + *r++ = *p++; + if (!ignoring) + rl += ind - pind; + else + ninvis += ind - pind; + p--; /* compensate for later increment */ + } else - ninvis++; - if (rl == _rl_screenwidth) +#endif + { + *r++ = *p; + if (!ignoring) + rl++; /* visible length byte counter */ + else + ninvis++; /* invisible chars byte counter */ + } + + if (rl >= _rl_screenwidth) invfl = ninvis; + + if (ignoring == 0) + physchars++; } } @@ -249,6 +282,8 @@ expand_prompt (pmt, lp, lip, niflp) *lip = last; if (niflp) *niflp = invfl; + if (vlp) + *vlp = physchars; return ret; } @@ -260,7 +295,7 @@ _rl_strip_prompt (pmt) { char *ret; - ret = expand_prompt (pmt, (int *)NULL, (int *)NULL, (int *)NULL); + ret = expand_prompt (pmt, (int *)NULL, (int *)NULL, (int *)NULL, (int *)NULL); return ret; } @@ -304,7 +339,8 @@ rl_expand_prompt (prompt) /* The prompt is only one logical line, though it might wrap. */ local_prompt = expand_prompt (prompt, &prompt_visible_length, &prompt_last_invisible, - &prompt_invis_chars_first_line); + &prompt_invis_chars_first_line, + &prompt_physical_chars); local_prompt_prefix = (char *)0; return (prompt_visible_length); } @@ -314,13 +350,15 @@ rl_expand_prompt (prompt) t = ++p; local_prompt = expand_prompt (p, &prompt_visible_length, &prompt_last_invisible, - &prompt_invis_chars_first_line); + (int *)NULL, + (int *)NULL); c = *t; *t = '\0'; /* The portion of the prompt string up to and including the final newline is now null-terminated. */ local_prompt_prefix = expand_prompt (prompt, &prompt_prefix_length, (int *)NULL, - &prompt_invis_chars_first_line); + &prompt_invis_chars_first_line, + &prompt_physical_chars); *t = c; return (prompt_prefix_length); } @@ -379,8 +417,8 @@ rl_redisplay () register int in, out, c, linenum, cursor_linenum; register char *line; int c_pos, inv_botlin, lb_botlin, lb_linenum; - int newlines, lpos, temp; - const char *prompt_this_line; + int newlines, lpos, temp, modmark; + char *prompt_this_line; #if defined (HANDLE_MULTIBYTE) wchar_t wc; size_t wc_bytes; @@ -409,10 +447,12 @@ rl_redisplay () /* Mark the line as modified or not. We only do this for history lines. */ + modmark = 0; if (_rl_mark_modified_lines && current_history () && rl_undo_list) { line[out++] = '*'; line[out] = '\0'; + modmark = 1; } /* If someone thought that the redisplay was handled, but the currently @@ -466,7 +506,7 @@ rl_redisplay () } } - pmtlen = strlen (prompt_this_line); + prompt_physical_chars = pmtlen = strlen (prompt_this_line); temp = pmtlen + out + 2; if (temp >= line_size) { @@ -525,7 +565,12 @@ rl_redisplay () /* inv_lbreaks[i] is where line i starts in the buffer. */ inv_lbreaks[newlines = 0] = 0; +#if 0 lpos = out - wrap_offset; +#else + lpos = prompt_physical_chars + modmark; +#endif + #if defined (HANDLE_MULTIBYTE) memset (_rl_wrapped_line, 0, vis_lbsize); #endif @@ -544,15 +589,13 @@ rl_redisplay () prompt_invis_chars_first_line variable could be made into an array saying how many invisible characters there are per line, but that's probably too much work for the benefit gained. How many people have - prompts that exceed two physical lines? */ + prompts that exceed two physical lines? + Additional logic fix from Edward Catmur */ temp = ((newlines + 1) * _rl_screenwidth) + -#if 0 - ((newlines == 0) ? prompt_invis_chars_first_line : 0) + -#else - ((newlines == 0 && local_prompt_prefix == 0) ? prompt_invis_chars_first_line : 0) + -#endif - ((newlines == 1) ? wrap_offset : 0); - + ((local_prompt_prefix == 0) ? ((newlines == 0) ? prompt_invis_chars_first_line + : ((newlines == 1) ? wrap_offset : 0)) + : ((newlines == 0) ? wrap_offset :0)); + inv_lbreaks[++newlines] = temp; lpos -= _rl_screenwidth; } @@ -584,7 +627,7 @@ rl_redisplay () #if defined (HANDLE_MULTIBYTE) if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) { - if (wc_bytes == (size_t)-1 || wc_bytes == (size_t)-2) + if (MB_INVALIDCH (wc_bytes)) { /* Byte sequence is invalid or shortened. Assume that the first byte represents a character. */ @@ -593,12 +636,12 @@ rl_redisplay () wc_width = 1; memset (&ps, 0, sizeof (mbstate_t)); } - else if (wc_bytes == (size_t)0) + else if (MB_NULLWCH (wc_bytes)) break; /* Found '\0' */ else { temp = wcwidth (wc); - wc_width = (temp < 0) ? 1 : temp; + wc_width = (temp >= 0) ? temp : 1; } } #endif @@ -788,7 +831,7 @@ rl_redisplay () #define VIS_LLEN(l) ((l) > _rl_vis_botlin ? 0 : (vis_lbreaks[l+1] - vis_lbreaks[l])) #define INV_LLEN(l) (inv_lbreaks[l+1] - inv_lbreaks[l]) #define VIS_CHARS(line) (visible_line + vis_lbreaks[line]) -#define VIS_LINE(line) ((line) > _rl_vis_botlin) ? (char*)"" : VIS_CHARS(line) +#define VIS_LINE(line) ((line) > _rl_vis_botlin) ? "" : VIS_CHARS(line) #define INV_LINE(line) (invisible_line + inv_lbreaks[line]) /* For each line in the buffer, do the updating display. */ @@ -829,7 +872,7 @@ rl_redisplay () _rl_move_vert (linenum); _rl_move_cursor_relative (0, tt); _rl_clear_to_eol - ((linenum == _rl_vis_botlin) ? (int)strlen (tt) : _rl_screenwidth); + ((linenum == _rl_vis_botlin) ? strlen (tt) : _rl_screenwidth); } } _rl_vis_botlin = inv_botlin; @@ -865,7 +908,7 @@ rl_redisplay () #endif _rl_output_some_chars (local_prompt, nleft); if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) - _rl_last_c_pos = _rl_col_width(local_prompt, 0, nleft); + _rl_last_c_pos = _rl_col_width (local_prompt, 0, nleft); else _rl_last_c_pos = nleft; } @@ -1067,12 +1110,12 @@ update_line (old, new, current_line, omax, nmax, inv_botlin) memset (&ps, 0, sizeof (mbstate_t)); ret = mbrtowc (&wc, new, MB_CUR_MAX, &ps); - if (ret == (size_t)-1 || ret == (size_t)-2) + if (MB_INVALIDCH (ret)) { tempwidth = 1; ret = 1; } - else if (ret == 0) + else if (MB_NULLWCH (ret)) tempwidth = 0; else tempwidth = wcwidth (wc); @@ -1089,7 +1132,7 @@ update_line (old, new, current_line, omax, nmax, inv_botlin) ret = mbrtowc (&wc, old, MB_CUR_MAX, &ps); if (ret != 0 && bytes != 0) { - if (ret == (size_t)-1 || ret == (size_t)-2) + if (MB_INVALIDCH (ret)) memmove (old+bytes, old+1, strlen (old+1)); else memmove (old+bytes, old+ret, strlen (old+ret)); @@ -1124,18 +1167,37 @@ update_line (old, new, current_line, omax, nmax, inv_botlin) #if defined (HANDLE_MULTIBYTE) if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) { - memset (&ps_new, 0, sizeof(mbstate_t)); - memset (&ps_old, 0, sizeof(mbstate_t)); - - new_offset = old_offset = 0; - for (ofd = old, nfd = new; - (ofd - old < omax) && *ofd && - _rl_compare_chars(old, old_offset, &ps_old, new, new_offset, &ps_new); ) + /* See if the old line is a subset of the new line, so that the + only change is adding characters. */ + temp = (omax < nmax) ? omax : nmax; + if (memcmp (old, new, temp) == 0) { - old_offset = _rl_find_next_mbchar (old, old_offset, 1, MB_FIND_ANY); - new_offset = _rl_find_next_mbchar (new, new_offset, 1, MB_FIND_ANY); - ofd = old + old_offset; - nfd = new + new_offset; + ofd = old + temp; + nfd = new + temp; + } + else + { + memset (&ps_new, 0, sizeof(mbstate_t)); + memset (&ps_old, 0, sizeof(mbstate_t)); + + if (omax == nmax && STREQN (new, old, omax)) + { + ofd = old + omax; + nfd = new + nmax; + } + else + { + new_offset = old_offset = 0; + for (ofd = old, nfd = new; + (ofd - old < omax) && *ofd && + _rl_compare_chars(old, old_offset, &ps_old, new, new_offset, &ps_new); ) + { + old_offset = _rl_find_next_mbchar (old, old_offset, 1, MB_FIND_ANY); + new_offset = _rl_find_next_mbchar (new, new_offset, 1, MB_FIND_ANY); + ofd = old + old_offset; + nfd = new + new_offset; + } + } } } else @@ -1167,8 +1229,11 @@ update_line (old, new, current_line, omax, nmax, inv_botlin) memset (&ps_old, 0, sizeof (mbstate_t)); memset (&ps_new, 0, sizeof (mbstate_t)); +#if 0 + /* On advice from jir@yamato.ibm.com */ _rl_adjust_point (old, ols - old, &ps_old); _rl_adjust_point (new, nls - new, &ps_new); +#endif if (_rl_compare_chars (old, ols - old, &ps_old, new, nls - new, &ps_new) == 0) break; @@ -1322,7 +1387,7 @@ update_line (old, new, current_line, omax, nmax, inv_botlin) insert_some_chars (nfd, lendiff, col_lendiff); _rl_last_c_pos += col_lendiff; } - else if (*ols == 0) + else if (*ols == 0 && lendiff > 0) { /* At the end of a line the characters do not have to be "inserted". They can just be placed on the screen. */ @@ -1345,10 +1410,14 @@ update_line (old, new, current_line, omax, nmax, inv_botlin) if ((temp - lendiff) > 0) { _rl_output_some_chars (nfd + lendiff, temp - lendiff); -#if 0 - _rl_last_c_pos += _rl_col_width (nfd+lendiff, 0, temp-lendiff) - col_lendiff; -#else +#if 1 + /* XXX -- this bears closer inspection. Fixes a redisplay bug + reported against bash-3.0-alpha by Andreas Schwab involving + multibyte characters and prompt strings with invisible + characters, but was previously disabled. */ _rl_last_c_pos += _rl_col_width (nfd+lendiff, 0, temp-col_lendiff); +#else + _rl_last_c_pos += _rl_col_width (nfd+lendiff, 0, temp-lendiff); #endif } } @@ -1424,12 +1493,13 @@ rl_on_new_line () /* Tell the update routines that we have moved onto a new line with the prompt already displayed. Code originally from the version of readline - distributed with CLISP. */ + distributed with CLISP. rl_expand_prompt must have already been called + (explicitly or implicitly). This still doesn't work exactly right. */ int rl_on_new_line_with_prompt () { int prompt_size, i, l, real_screenwidth, newlines; - char *prompt_last_line; + char *prompt_last_line, *lprompt; /* Initialize visible_line and invisible_line to ensure that they can hold the already-displayed prompt. */ @@ -1438,8 +1508,9 @@ rl_on_new_line_with_prompt () /* Make sure the line structures hold the already-displayed prompt for redisplay. */ - strcpy (visible_line, rl_prompt); - strcpy (invisible_line, rl_prompt); + lprompt = local_prompt ? local_prompt : rl_prompt; + strcpy (visible_line, lprompt); + strcpy (invisible_line, lprompt); /* If the prompt contains newlines, take the last tail. */ prompt_last_line = strrchr (rl_prompt, '\n'); @@ -1474,6 +1545,8 @@ rl_on_new_line_with_prompt () vis_lbreaks[newlines] = l; visible_wrap_offset = 0; + rl_display_prompt = rl_prompt; /* XXX - make sure it's set */ + return 0; } @@ -1508,8 +1581,15 @@ _rl_move_cursor_relative (new, data) #if defined (HANDLE_MULTIBYTE) /* If we have multibyte characters, NEW is indexed by the buffer point in a multibyte string, but _rl_last_c_pos is the display position. In - this case, NEW's display position is not obvious. */ - if ((MB_CUR_MAX == 1 || rl_byte_oriented ) && _rl_last_c_pos == new) return; + this case, NEW's display position is not obvious and must be + calculated. */ + if (MB_CUR_MAX == 1 || rl_byte_oriented) + { + if (_rl_last_c_pos == new) + return; + } + else if (_rl_last_c_pos == _rl_col_width (data, 0, new)) + return; #else if (_rl_last_c_pos == new) return; #endif @@ -1592,11 +1672,7 @@ _rl_move_cursor_relative (new, data) #endif { if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) - { - tputs (_rl_term_cr, 1, _rl_output_character_function); - for (i = 0; i < new; i++) - putc (data[i], rl_outstream); - } + _rl_backspace (_rl_last_c_pos - _rl_col_width (data, 0, new)); else _rl_backspace (_rl_last_c_pos - new); } @@ -1734,7 +1810,6 @@ rl_message (va_alist) int rl_message (format, arg1, arg2) char *format; - int arg1, arg2; { sprintf (msg_buf, format, arg1, arg2); msg_buf[sizeof(msg_buf) - 1] = '\0'; /* overflow? */ @@ -1763,10 +1838,14 @@ rl_reset_line_state () return 0; } +/* These are getting numerous enough that it's time to create a struct. */ + static char *saved_local_prompt; static char *saved_local_prefix; static int saved_last_invisible; static int saved_visible_length; +static int saved_invis_chars_first_line; +static int saved_physical_chars; void rl_save_prompt () @@ -1775,9 +1854,12 @@ rl_save_prompt () saved_local_prefix = local_prompt_prefix; saved_last_invisible = prompt_last_invisible; saved_visible_length = prompt_visible_length; + saved_invis_chars_first_line = prompt_invis_chars_first_line; + saved_physical_chars = prompt_physical_chars; local_prompt = local_prompt_prefix = (char *)0; prompt_last_invisible = prompt_visible_length = 0; + prompt_invis_chars_first_line = prompt_physical_chars = 0; } void @@ -1790,6 +1872,8 @@ rl_restore_prompt () local_prompt_prefix = saved_local_prefix; prompt_last_invisible = saved_last_invisible; prompt_visible_length = saved_visible_length; + prompt_invis_chars_first_line = saved_invis_chars_first_line; + prompt_physical_chars = saved_physical_chars; } char * @@ -1822,6 +1906,7 @@ _rl_make_prompt_for_search (pchar) prompt_last_invisible = saved_last_invisible; prompt_visible_length = saved_visible_length + 1; } + return pmt; } @@ -1997,9 +2082,8 @@ static void redraw_prompt (t) char *t; { - const char *oldp; - char *oldl, *oldlprefix; - int oldlen, oldlast, oldplen, oldninvis; + char *oldp, *oldl, *oldlprefix; + int oldlen, oldlast, oldplen, oldninvis, oldphyschars; /* Geez, I should make this a struct. */ oldp = rl_display_prompt; @@ -2009,11 +2093,13 @@ redraw_prompt (t) oldplen = prompt_prefix_length; oldlast = prompt_last_invisible; oldninvis = prompt_invis_chars_first_line; + oldphyschars = prompt_physical_chars; rl_display_prompt = t; local_prompt = expand_prompt (t, &prompt_visible_length, &prompt_last_invisible, - &prompt_invis_chars_first_line); + &prompt_invis_chars_first_line, + &prompt_physical_chars); local_prompt_prefix = (char *)NULL; rl_forced_update_display (); @@ -2024,6 +2110,7 @@ redraw_prompt (t) prompt_prefix_length = oldplen; prompt_last_invisible = oldlast; prompt_invis_chars_first_line = oldninvis; + prompt_physical_chars = oldphyschars; } /* Redisplay the current line after a SIGWINCH is received. */ @@ -2133,7 +2220,7 @@ _rl_col_width (str, start, end) while (point < start) { tmp = mbrlen (str + point, max, &ps); - if ((size_t)tmp == (size_t)-1 || (size_t)tmp == (size_t)-2) + if (MB_INVALIDCH ((size_t)tmp)) { /* In this case, the bytes are invalid or too short to compose a multibyte character, so we assume that the first byte represents @@ -2145,8 +2232,8 @@ _rl_col_width (str, start, end) effect of mbstate is undefined. */ memset (&ps, 0, sizeof (mbstate_t)); } - else if (tmp == 0) - break; /* Found '\0' */ + else if (MB_NULLWCH (tmp)) + break; /* Found '\0' */ else { point += tmp; @@ -2162,7 +2249,7 @@ _rl_col_width (str, start, end) while (point < end) { tmp = mbrtowc (&wc, str + point, max, &ps); - if ((size_t)tmp == (size_t)-1 || (size_t)tmp == (size_t)-2) + if (MB_INVALIDCH ((size_t)tmp)) { /* In this case, the bytes are invalid or too short to compose a multibyte character, so we assume that the first byte represents @@ -2177,8 +2264,8 @@ _rl_col_width (str, start, end) effect of mbstate is undefined. */ memset (&ps, 0, sizeof (mbstate_t)); } - else if (tmp == 0) - break; /* Found '\0' */ + else if (MB_NULLWCH (tmp)) + break; /* Found '\0' */ else { point += tmp; @@ -2193,4 +2280,3 @@ _rl_col_width (str, start, end) return width; } #endif /* HANDLE_MULTIBYTE */ - diff --git a/cmd-line-utils/readline/funmap.c b/cmd-line-utils/readline/funmap.c index 53fd22754ab..28c8cc67c9f 100644 --- a/cmd-line-utils/readline/funmap.c +++ b/cmd-line-utils/readline/funmap.c @@ -21,7 +21,9 @@ 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#include "config_readline.h" +#if defined (HAVE_CONFIG_H) +# include +#endif #if !defined (BUFSIZ) #include @@ -129,6 +131,7 @@ static FUNMAP default_funmap[] = { { "tty-status", rl_tty_status }, { "undo", rl_undo_command }, { "universal-argument", rl_universal_argument }, + { "unix-filename-rubout", rl_unix_filename_rubout }, { "unix-line-discard", rl_unix_line_discard }, { "unix-word-rubout", rl_unix_word_rubout }, { "upcase-word", rl_upcase_word }, diff --git a/cmd-line-utils/readline/histexpand.c b/cmd-line-utils/readline/histexpand.c index eed8d5a365e..2ab34cba269 100644 --- a/cmd-line-utils/readline/histexpand.c +++ b/cmd-line-utils/readline/histexpand.c @@ -1,6 +1,6 @@ /* histexpand.c -- history expansion. */ -/* Copyright (C) 1989, 1992 Free Software Foundation, Inc. +/* Copyright (C) 1989-2004 Free Software Foundation, Inc. This file contains the GNU History Library (the Library), a set of routines for managing the text of previously typed lines. @@ -22,7 +22,9 @@ #define READLINE_LIBRARY -#include "config_readline.h" +#if defined (HAVE_CONFIG_H) +# include +#endif #include @@ -50,6 +52,8 @@ #define HISTORY_WORD_DELIMITERS " \t\n;&()|<>" #define HISTORY_QUOTE_CHARACTERS "\"'`" +#define slashify_in_quotes "\\`\"$" + typedef int _hist_search_func_t PARAMS((const char *, int)); extern int rl_byte_oriented; /* declared in mbutil.c */ @@ -63,6 +67,8 @@ static int subst_rhs_len; static char *get_history_word_specifier PARAMS((char *, char *, int *)); static char *history_find_word PARAMS((char *, int)); +static int history_tokenize_word PARAMS((const char *, int)); +static char *history_substring PARAMS((const char *, int, int)); static char *quote_breaks PARAMS((char *)); @@ -83,14 +89,14 @@ char history_comment_char = '\0'; /* The list of characters which inhibit the expansion of text if found immediately following history_expansion_char. */ -const char *history_no_expand_chars = " \t\n\r="; +char *history_no_expand_chars = " \t\n\r="; /* If set to a non-zero value, single quotes inhibit history expansion. The default is 0. */ int history_quotes_inhibit_expansion = 0; /* Used to split words by history_tokenize_internal. */ -const char *history_word_delimiters = HISTORY_WORD_DELIMITERS; +char *history_word_delimiters = HISTORY_WORD_DELIMITERS; /* If set, this points to a function that is called to verify that a particular history expansion should be performed. */ @@ -199,7 +205,7 @@ get_history_event (string, caller_index, delimiting_quote) } /* Only a closing `?' or a newline delimit a substring search string. */ - for (local_index = i; (c = string[i]); i++) + for (local_index = i; c = string[i]; i++) #if defined (HANDLE_MULTIBYTE) if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) { @@ -209,8 +215,8 @@ get_history_event (string, caller_index, delimiting_quote) memset (&ps, 0, sizeof (mbstate_t)); /* These produce warnings because we're passing a const string to a function that takes a non-const string. */ - _rl_adjust_point (string, i, &ps); - if ((v = _rl_get_char_len (string + i, &ps)) > 1) + _rl_adjust_point ((char *)string, i, &ps); + if ((v = _rl_get_char_len ((char *)string + i, &ps)) > 1) { i += v - 1; continue; @@ -515,7 +521,7 @@ history_expand_internal (string, start, end_index_ptr, ret_string, current_line) char *current_line; /* for !# */ { int i, n, starting_index; - int substitute_globally, want_quotes, print_only; + int substitute_globally, subst_bywords, want_quotes, print_only; char *event, *temp, *result, *tstr, *t, c, *word_spec; int result_len; #if defined (HANDLE_MULTIBYTE) @@ -597,19 +603,25 @@ history_expand_internal (string, start, end_index_ptr, ret_string, current_line) FREE (word_spec); /* Perhaps there are other modifiers involved. Do what they say. */ - want_quotes = substitute_globally = print_only = 0; + want_quotes = substitute_globally = subst_bywords = print_only = 0; starting_index = i; while (string[i] == ':') { c = string[i + 1]; - if (c == 'g') + if (c == 'g' || c == 'a') { substitute_globally = 1; i++; c = string[i + 1]; } + else if (c == 'G') + { + subst_bywords = 1; + i++; + c = string[i + 1]; + } switch (c) { @@ -681,7 +693,7 @@ history_expand_internal (string, start, end_index_ptr, ret_string, current_line) case 's': { char *new_event; - int delimiter, failed, si, l_temp; + int delimiter, failed, si, l_temp, ws, we; if (c == 's') { @@ -758,33 +770,67 @@ history_expand_internal (string, start, end_index_ptr, ret_string, current_line) } /* Find the first occurrence of THIS in TEMP. */ - si = 0; + /* Substitute SUBST_RHS for SUBST_LHS in TEMP. There are three + cases to consider: + + 1. substitute_globally == subst_bywords == 0 + 2. substitute_globally == 1 && subst_bywords == 0 + 3. substitute_globally == 0 && subst_bywords == 1 + + In the first case, we substitute for the first occurrence only. + In the second case, we substitute for every occurrence. + In the third case, we tokenize into words and substitute the + first occurrence of each word. */ + + si = we = 0; for (failed = 1; (si + subst_lhs_len) <= l_temp; si++) - if (STREQN (temp+si, subst_lhs, subst_lhs_len)) - { - int len = subst_rhs_len - subst_lhs_len + l_temp; - new_event = (char *)xmalloc (1 + len); - strncpy (new_event, temp, si); - strncpy (new_event + si, subst_rhs, subst_rhs_len); - strncpy (new_event + si + subst_rhs_len, - temp + si + subst_lhs_len, - l_temp - (si + subst_lhs_len)); - new_event[len] = '\0'; - free (temp); - temp = new_event; + { + /* First skip whitespace and find word boundaries if + we're past the end of the word boundary we found + the last time. */ + if (subst_bywords && si > we) + { + for (; temp[si] && whitespace (temp[si]); si++) + ; + ws = si; + we = history_tokenize_word (temp, si); + } - failed = 0; + if (STREQN (temp+si, subst_lhs, subst_lhs_len)) + { + int len = subst_rhs_len - subst_lhs_len + l_temp; + new_event = (char *)xmalloc (1 + len); + strncpy (new_event, temp, si); + strncpy (new_event + si, subst_rhs, subst_rhs_len); + strncpy (new_event + si + subst_rhs_len, + temp + si + subst_lhs_len, + l_temp - (si + subst_lhs_len)); + new_event[len] = '\0'; + free (temp); + temp = new_event; - if (substitute_globally) - { - si += subst_rhs_len; - l_temp = strlen (temp); - substitute_globally++; - continue; - } - else - break; - } + failed = 0; + + if (substitute_globally) + { + /* Reported to fix a bug that causes it to skip every + other match when matching a single character. Was + si += subst_rhs_len previously. */ + si += subst_rhs_len - 1; + l_temp = strlen (temp); + substitute_globally++; + continue; + } + else if (subst_bywords) + { + si = we; + l_temp = strlen (temp); + continue; + } + else + break; + } + } if (substitute_globally > 1) { @@ -877,7 +923,7 @@ history_expand (hstring, output) char **output; { register int j; - int i, r, l, passc, cc, modified, eindex, only_printing; + int i, r, l, passc, cc, modified, eindex, only_printing, dquote; char *string; /* The output string, and its length. */ @@ -940,7 +986,7 @@ history_expand (hstring, output) /* `!' followed by one of the characters in history_no_expand_chars is NOT an expansion. */ - for (i = 0; string[i]; i++) + for (i = dquote = 0; string[i]; i++) { #if defined (HANDLE_MULTIBYTE) if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) @@ -982,9 +1028,19 @@ history_expand (hstring, output) else break; } - /* XXX - at some point, might want to extend this to handle - double quotes as well. */ - else if (history_quotes_inhibit_expansion && string[i] == '\'') + /* Shell-like quoting: allow backslashes to quote double quotes + inside a double-quoted string. */ + else if (dquote && string[i] == '\\' && cc == '"') + i++; + /* More shell-like quoting: if we're paying attention to single + quotes and letting them quote the history expansion character, + then we need to pay attention to double quotes, because single + quotes are not special inside double-quoted strings. */ + else if (history_quotes_inhibit_expansion && string[i] == '"') + { + dquote = 1 - dquote; + } + else if (dquote == 0 && history_quotes_inhibit_expansion && string[i] == '\'') { /* If this is bash, single quotes inhibit history expansion. */ i++; @@ -997,6 +1053,7 @@ history_expand (hstring, output) if (cc == '\'' || cc == history_expansion_char) i++; } + } if (string[i] != history_expansion_char) @@ -1008,7 +1065,7 @@ history_expand (hstring, output) } /* Extract and perform the substitution. */ - for (passc = i = j = 0; i < l; i++) + for (passc = dquote = i = j = 0; i < l; i++) { int tchar = string[i]; @@ -1059,11 +1116,16 @@ history_expand (hstring, output) ADD_CHAR (tchar); break; + case '"': + dquote = 1 - dquote; + ADD_CHAR (tchar); + break; + case '\'': { /* If history_quotes_inhibit_expansion is set, single quotes inhibit history expansion. */ - if (history_quotes_inhibit_expansion) + if (dquote == 0 && history_quotes_inhibit_expansion) { int quote, slen; @@ -1158,7 +1220,9 @@ history_expand (hstring, output) if (only_printing) { +#if 0 add_history (result); +#endif return (2); } @@ -1221,7 +1285,10 @@ get_history_word_specifier (spec, from, caller_index) if (spec[i] == '-') first = 0; else if (spec[i] == '^') - first = 1; + { + first = 1; + i++; + } else if (_rl_digit_p (spec[i]) && expecting_word_spec) { for (first = 0; _rl_digit_p (spec[i]); i++) @@ -1336,7 +1403,103 @@ history_arg_extract (first, last, string) return (result); } -#define slashify_in_quotes "\\`\"$" +static int +history_tokenize_word (string, ind) + const char *string; + int ind; +{ + register int i; + int delimiter; + + i = ind; + delimiter = 0; + + if (member (string[i], "()\n")) + { + i++; + return i; + } + + if (member (string[i], "<>;&|$")) + { + int peek = string[i + 1]; + + if (peek == string[i] && peek != '$') + { + if (peek == '<' && string[i + 2] == '-') + i++; + i += 2; + return i; + } + else + { + if ((peek == '&' && (string[i] == '>' || string[i] == '<')) || + (peek == '>' && string[i] == '&') || + (peek == '(' && (string[i] == '>' || string[i] == '<')) || /* ) */ + (peek == '(' && string[i] == '$')) /* ) */ + { + i += 2; + return i; + } + } + + if (string[i] != '$') + { + i++; + return i; + } + } + + /* Get word from string + i; */ + + if (member (string[i], HISTORY_QUOTE_CHARACTERS)) + delimiter = string[i++]; + + for (; string[i]; i++) + { + if (string[i] == '\\' && string[i + 1] == '\n') + { + i++; + continue; + } + + if (string[i] == '\\' && delimiter != '\'' && + (delimiter != '"' || member (string[i], slashify_in_quotes))) + { + i++; + continue; + } + + if (delimiter && string[i] == delimiter) + { + delimiter = 0; + continue; + } + + if (!delimiter && (member (string[i], history_word_delimiters))) + break; + + if (!delimiter && member (string[i], HISTORY_QUOTE_CHARACTERS)) + delimiter = string[i]; + } + + return i; +} + +static char * +history_substring (string, start, end) + const char *string; + int start, end; +{ + register int len; + register char *result; + + len = end - start; + result = (char *)xmalloc (len + 1); + strncpy (result, string + start, len); + result[len] = '\0'; + return result; +} /* Parse STRING into tokens and return an array of strings. If WIND is not -1 and INDP is not null, we also want the word surrounding index @@ -1349,7 +1512,6 @@ history_tokenize_internal (string, wind, indp) { char **result; register int i, start, result_index, size; - int len, delimiter; /* If we're searching for a string that's not part of a word (e.g., " "), make sure we set *INDP to a reasonable value. */ @@ -1360,8 +1522,6 @@ history_tokenize_internal (string, wind, indp) exactly where the shell would split them. */ for (i = result_index = size = 0, result = (char **)NULL; string[i]; ) { - delimiter = 0; - /* Skip leading whitespace. */ for (; string[i] && whitespace (string[i]); i++) ; @@ -1369,88 +1529,30 @@ history_tokenize_internal (string, wind, indp) return (result); start = i; - - if (member (string[i], "()\n")) + + i = history_tokenize_word (string, start); + + /* If we have a non-whitespace delimiter character (which would not be + skipped by the loop above), use it and any adjacent delimiters to + make a separate field. Any adjacent white space will be skipped the + next time through the loop. */ + if (i == start && history_word_delimiters) { i++; - goto got_token; + while (string[i] && member (string[i], history_word_delimiters)) + i++; } - if (member (string[i], "<>;&|$")) - { - int peek = string[i + 1]; - - if (peek == string[i] && peek != '$') - { - if (peek == '<' && string[i + 2] == '-') - i++; - i += 2; - goto got_token; - } - else - { - if ((peek == '&' && (string[i] == '>' || string[i] == '<')) || - ((peek == '>') && (string[i] == '&')) || - ((peek == '(') && (string[i] == '$'))) - { - i += 2; - goto got_token; - } - } - if (string[i] != '$') - { - i++; - goto got_token; - } - } - - /* Get word from string + i; */ - - if (member (string[i], HISTORY_QUOTE_CHARACTERS)) - delimiter = string[i++]; - - for (; string[i]; i++) - { - if (string[i] == '\\' && string[i + 1] == '\n') - { - i++; - continue; - } - - if (string[i] == '\\' && delimiter != '\'' && - (delimiter != '"' || member (string[i], slashify_in_quotes))) - { - i++; - continue; - } - - if (delimiter && string[i] == delimiter) - { - delimiter = 0; - continue; - } - - if (!delimiter && (member (string[i], history_word_delimiters))) - break; - - if (!delimiter && member (string[i], HISTORY_QUOTE_CHARACTERS)) - delimiter = string[i]; - } - - got_token: - /* If we are looking for the word in which the character at a particular index falls, remember it. */ if (indp && wind != -1 && wind >= start && wind < i) *indp = result_index; - len = i - start; if (result_index + 2 >= size) result = (char **)xrealloc (result, ((size += 10) * sizeof (char *))); - result[result_index] = (char *)xmalloc (1 + len); - strncpy (result[result_index], string + start, len); - result[result_index][len] = '\0'; - result[++result_index] = (char *)NULL; + + result[result_index++] = history_substring (string, start, i); + result[result_index] = (char *)NULL; } return (result); diff --git a/cmd-line-utils/readline/histfile.c b/cmd-line-utils/readline/histfile.c index 77f757eac1d..717bbee6fd7 100644 --- a/cmd-line-utils/readline/histfile.c +++ b/cmd-line-utils/readline/histfile.c @@ -1,6 +1,6 @@ /* histfile.c - functions to manipulate the history file. */ -/* Copyright (C) 1989, 1992 Free Software Foundation, Inc. +/* Copyright (C) 1989-2003 Free Software Foundation, Inc. This file contains the GNU History Library (the Library), a set of routines for managing the text of previously typed lines. @@ -23,14 +23,21 @@ /* The goal is to make the implementation transparent, so that you don't have to know what data types are used, just what functions you can call. I think I have done that. */ + #define READLINE_LIBRARY -#include "config_readline.h" +#if defined (__TANDEM) +# include +#endif + +#if defined (HAVE_CONFIG_H) +# include +#endif #include #include -#ifndef _MINIX +#if ! defined (_MINIX) && defined (HAVE_SYS_FILE_H) # include #endif #include "posixstat.h" @@ -50,7 +57,7 @@ # undef HAVE_MMAP #endif -#ifdef HAVE_MMAP +#ifdef HISTORY_USE_MMAP # include # ifdef MAP_FILE @@ -65,7 +72,7 @@ # define MAP_FAILED ((void *)-1) # endif -#endif /* HAVE_MMAP */ +#endif /* HISTORY_USE_MMAP */ /* If we're compiling for __EMX__ (OS/2) or __CYGWIN__ (cygwin32 environment on win 95/98/nt), we want to open files with O_BINARY mode so that there @@ -91,6 +98,13 @@ extern int errno; #include "rlshell.h" #include "xmalloc.h" +/* If non-zero, we write timestamps to the history file in history_do_write() */ +int history_write_timestamps = 0; + +/* Does S look like the beginning of a history timestamp entry? Placeholder + for more extensive tests. */ +#define HIST_TIMESTAMP_START(s) (*(s) == history_comment_char) + /* Return the string that should be used in the place of this filename. This only matters when you don't specify the filename to read_history (), or write_history (). */ @@ -149,13 +163,20 @@ read_history_range (filename, from, to) const char *filename; int from, to; { - register char *line_start, *line_end; - char *input, *buffer, *bufend; + register char *line_start, *line_end, *p; + char *input, *buffer, *bufend, *last_ts; int file, current_line, chars_read; struct stat finfo; size_t file_size; +#if defined (EFBIG) + int overflow_errno = EFBIG; +#elif defined (EOVERFLOW) + int overflow_errno = EOVERFLOW; +#else + int overflow_errno = EIO; +#endif - buffer = (char *)NULL; + buffer = last_ts = (char *)NULL; input = history_filename (filename); file = open (input, O_RDONLY|O_BINARY, 0666); @@ -167,37 +188,42 @@ read_history_range (filename, from, to) /* check for overflow on very large files */ if (file_size != finfo.st_size || file_size + 1 < file_size) { -#if defined (EFBIG) - errno = EFBIG; -#elif defined (EOVERFLOW) - errno = EOVERFLOW; -#endif + errno = overflow_errno; goto error_and_exit; } -#ifdef HAVE_MMAP +#ifdef HISTORY_USE_MMAP /* We map read/write and private so we can change newlines to NULs without affecting the underlying object. */ buffer = (char *)mmap (0, file_size, PROT_READ|PROT_WRITE, MAP_RFLAGS, file, 0); if ((void *)buffer == MAP_FAILED) - goto error_and_exit; + { + errno = overflow_errno; + goto error_and_exit; + } chars_read = file_size; #else buffer = (char *)malloc (file_size + 1); if (buffer == 0) - goto error_and_exit; + { + errno = overflow_errno; + goto error_and_exit; + } chars_read = read (file, buffer, file_size); #endif if (chars_read < 0) { error_and_exit: - chars_read = errno; + if (errno != 0) + chars_read = errno; + else + chars_read = EIO; if (file >= 0) close (file); FREE (input); -#ifndef HAVE_MMAP +#ifndef HISTORY_USE_MMAP FREE (buffer); #endif @@ -218,8 +244,12 @@ read_history_range (filename, from, to) for (line_start = line_end = buffer; line_end < bufend && current_line < from; line_end++) if (*line_end == '\n') { - current_line++; - line_start = line_end + 1; + p = line_end + 1; + /* If we see something we think is a timestamp, continue with this + line. We should check more extensively here... */ + if (HIST_TIMESTAMP_START(p) == 0) + current_line++; + line_start = p; } /* If there are lines left to gobble, then gobble them now. */ @@ -229,7 +259,22 @@ read_history_range (filename, from, to) *line_end = '\0'; if (*line_start) - add_history (line_start); + { + if (HIST_TIMESTAMP_START(line_start) == 0) + { + add_history (line_start); + if (last_ts) + { + add_history_time (last_ts); + last_ts = NULL; + } + } + else + { + last_ts = line_start; + current_line--; + } + } current_line++; @@ -240,7 +285,7 @@ read_history_range (filename, from, to) } FREE (input); -#ifndef HAVE_MMAP +#ifndef HISTORY_USE_MMAP FREE (buffer); #else munmap (buffer, file_size); @@ -257,7 +302,7 @@ history_truncate_file (fname, lines) const char *fname; int lines; { - char *buffer, *filename, *bp; + char *buffer, *filename, *bp, *bp1; /* bp1 == bp+1 */ int file, chars_read, rv; struct stat finfo; size_t file_size; @@ -320,11 +365,14 @@ history_truncate_file (fname, lines) } /* Count backwards from the end of buffer until we have passed - LINES lines. */ - for (bp = buffer + chars_read - 1; lines && bp > buffer; bp--) + LINES lines. bp1 is set funny initially. But since bp[1] can't + be a comment character (since it's off the end) and *bp can't be + both a newline and the history comment character, it should be OK. */ + for (bp1 = bp = buffer + chars_read - 1; lines && bp > buffer; bp--) { - if (*bp == '\n') + if (*bp == '\n' && HIST_TIMESTAMP_START(bp1) == 0) lines--; + bp1 = bp; } /* If this is the first line, then the file contains exactly the @@ -333,11 +381,14 @@ history_truncate_file (fname, lines) the current value of i and 0. Otherwise, write from the start of this line until the end of the buffer. */ for ( ; bp > buffer; bp--) - if (*bp == '\n') - { - bp++; - break; - } + { + if (*bp == '\n' && HIST_TIMESTAMP_START(bp1) == 0) + { + bp++; + break; + } + bp1 = bp; + } /* Write only if there are more lines in the file than we want to truncate to. */ @@ -372,9 +423,9 @@ history_do_write (filename, nelements, overwrite) register int i; char *output; int file, mode, rv; +#ifdef HISTORY_USE_MMAP size_t cursize; -#ifdef HAVE_MMAP mode = overwrite ? O_RDWR|O_CREAT|O_TRUNC|O_BINARY : O_RDWR|O_APPEND|O_BINARY; #else mode = overwrite ? O_WRONLY|O_CREAT|O_TRUNC|O_BINARY : O_WRONLY|O_APPEND|O_BINARY; @@ -388,7 +439,7 @@ history_do_write (filename, nelements, overwrite) return (errno); } -#ifdef HAVE_MMAP +#ifdef HISTORY_USE_MMAP cursize = overwrite ? 0 : lseek (file, 0, SEEK_END); #endif @@ -406,10 +457,18 @@ history_do_write (filename, nelements, overwrite) the_history = history_list (); /* Calculate the total number of bytes to write. */ for (buffer_size = 0, i = history_length - nelements; i < history_length; i++) - buffer_size += 1 + strlen (the_history[i]->line); +#if 0 + buffer_size += 2 + HISTENT_BYTES (the_history[i]); +#else + { + if (history_write_timestamps && the_history[i]->timestamp && the_history[i]->timestamp[0]) + buffer_size += strlen (the_history[i]->timestamp) + 1; + buffer_size += strlen (the_history[i]->line) + 1; + } +#endif /* Allocate the buffer, and fill it. */ -#ifdef HAVE_MMAP +#ifdef HISTORY_USE_MMAP if (ftruncate (file, buffer_size+cursize) == -1) goto mmap_error; buffer = (char *)mmap (0, buffer_size, PROT_READ|PROT_WRITE, MAP_WFLAGS, file, cursize); @@ -434,12 +493,18 @@ mmap_error: for (j = 0, i = history_length - nelements; i < history_length; i++) { + if (history_write_timestamps && the_history[i]->timestamp && the_history[i]->timestamp[0]) + { + strcpy (buffer + j, the_history[i]->timestamp); + j += strlen (the_history[i]->timestamp); + buffer[j++] = '\n'; + } strcpy (buffer + j, the_history[i]->line); j += strlen (the_history[i]->line); buffer[j++] = '\n'; } -#ifdef HAVE_MMAP +#ifdef HISTORY_USE_MMAP if (msync (buffer, buffer_size, 0) != 0 || munmap (buffer, buffer_size) != 0) rv = errno; #else diff --git a/cmd-line-utils/readline/history.c b/cmd-line-utils/readline/history.c index 759ff9e0de9..d99b76e8744 100644 --- a/cmd-line-utils/readline/history.c +++ b/cmd-line-utils/readline/history.c @@ -1,6 +1,6 @@ -/* History.c -- standalone history library */ +/* history.c -- standalone history library */ -/* Copyright (C) 1989, 1992 Free Software Foundation, Inc. +/* Copyright (C) 1989-2003 Free Software Foundation, Inc. This file contains the GNU History Library (the Library), a set of routines for managing the text of previously typed lines. @@ -25,7 +25,9 @@ you can call. I think I have done that. */ #define READLINE_LIBRARY -#include "config_readline.h" +#if defined (HAVE_CONFIG_H) +# include +#endif #include @@ -50,6 +52,8 @@ /* The number of slots to increase the_history by. */ #define DEFAULT_HISTORY_GROW_SIZE 50 +static char *hist_inittime PARAMS((void)); + /* **************************************************************** */ /* */ /* History Functions */ @@ -121,14 +125,15 @@ using_history () } /* Return the number of bytes that the primary history entries are using. - This just adds up the lengths of the_history->lines. */ + This just adds up the lengths of the_history->lines and the associated + timestamps. */ int history_total_bytes () { register int i, result; for (i = result = 0; the_history && the_history[i]; i++) - result += strlen (the_history[i]->line); + result += HISTENT_BYTES (the_history[i]); return (result); } @@ -204,6 +209,40 @@ history_get (offset) : the_history[local_index]; } +time_t +history_get_time (hist) + HIST_ENTRY *hist; +{ + char *ts; + time_t t; + + if (hist == 0 || hist->timestamp == 0) + return 0; + ts = hist->timestamp; + if (ts[0] != history_comment_char) + return 0; + t = (time_t) atol (ts + 1); /* XXX - should use strtol() here */ + return t; +} + +static char * +hist_inittime () +{ + time_t t; + char ts[64], *ret; + + t = (time_t) time ((time_t *)0); +#if defined (HAVE_VSNPRINTF) /* assume snprintf if vsnprintf exists */ + snprintf (ts, sizeof (ts) - 1, "X%lu", (unsigned long) t); +#else + sprintf (ts, "X%lu", (unsigned long) t); +#endif + ret = savestring (ts); + ret[0] = history_comment_char; + + return ret; +} + /* Place STRING at the end of the history list. The data field is set to NULL. */ void @@ -223,10 +262,7 @@ add_history (string) /* If there is something in the slot, then remove it. */ if (the_history[0]) - { - free (the_history[0]->line); - free (the_history[0]); - } + (void) free_history_entry (the_history[0]); /* Copy the rest of the entries, moving down one slot. */ for (i = 0; i < history_length; i++) @@ -258,10 +294,41 @@ add_history (string) temp->line = savestring (string); temp->data = (char *)NULL; + temp->timestamp = hist_inittime (); + the_history[history_length] = (HIST_ENTRY *)NULL; the_history[history_length - 1] = temp; } +/* Change the time stamp of the most recent history entry to STRING. */ +void +add_history_time (string) + const char *string; +{ + HIST_ENTRY *hs; + + hs = the_history[history_length - 1]; + FREE (hs->timestamp); + hs->timestamp = savestring (string); +} + +/* Free HIST and return the data so the calling application can free it + if necessary and desired. */ +histdata_t +free_history_entry (hist) + HIST_ENTRY *hist; +{ + histdata_t x; + + if (hist == 0) + return ((histdata_t) 0); + FREE (hist->line); + FREE (hist->timestamp); + x = hist->data; + free (hist); + return (x); +} + /* Make the history entry at WHICH have LINE and DATA. This returns the old entry so you can dispose of the data. In the case of an invalid WHICH, a NULL pointer is returned. */ @@ -281,6 +348,7 @@ replace_history_entry (which, line, data) temp->line = savestring (line); temp->data = data; + temp->timestamp = savestring (old_value->timestamp); the_history[which] = temp; return (old_value); @@ -325,10 +393,7 @@ stifle_history (max) { /* This loses because we cannot free the data. */ for (i = 0, j = history_length - max; i < j; i++) - { - free (the_history[i]->line); - free (the_history[i]); - } + free_history_entry (the_history[i]); history_base = i; for (j = 0, i = history_length - max; j < max; i++, j++) @@ -370,8 +435,7 @@ clear_history () /* This loses because we cannot free the data. */ for (i = 0; i < history_length; i++) { - free (the_history[i]->line); - free (the_history[i]); + free_history_entry (the_history[i]); the_history[i] = (HIST_ENTRY *)NULL; } diff --git a/cmd-line-utils/readline/history.h b/cmd-line-utils/readline/history.h index afd85104554..14ca2a996c7 100644 --- a/cmd-line-utils/readline/history.h +++ b/cmd-line-utils/readline/history.h @@ -1,5 +1,5 @@ -/* History.h -- the names of functions that you can call in history. */ -/* Copyright (C) 1989, 1992 Free Software Foundation, Inc. +/* history.h -- the names of functions that you can call in history. */ +/* Copyright (C) 1989-2003 Free Software Foundation, Inc. This file contains the GNU History Library (the Library), a set of routines for managing the text of previously typed lines. @@ -26,6 +26,8 @@ extern "C" { #endif +#include /* XXX - for history timestamp code */ + #if defined READLINE_LIBRARY # include "rlstdc.h" # include "rltypedefs.h" @@ -43,9 +45,13 @@ typedef char *histdata_t; /* The structure used to store a history entry. */ typedef struct _hist_entry { char *line; + char *timestamp; /* char * rather than time_t for read/write */ histdata_t data; } HIST_ENTRY; +/* Size of the history-library-managed space in history entry HS. */ +#define HISTENT_BYTES(hs) (strlen ((hs)->line) + strlen ((hs)->timestamp)) + /* A structure used to pass the current state of the history stuff around. */ typedef struct _hist_state { HIST_ENTRY **entries; /* Pointer to the entries themselves. */ @@ -76,11 +82,19 @@ extern void history_set_history_state PARAMS((HISTORY_STATE *)); The associated data field (if any) is set to NULL. */ extern void add_history PARAMS((const char *)); +/* Change the timestamp associated with the most recent history entry to + STRING. */ +extern void add_history_time PARAMS((const char *)); + /* A reasonably useless function, only here for completeness. WHICH is the magic number that tells us which element to delete. The elements are numbered from 0. */ extern HIST_ENTRY *remove_history PARAMS((int)); +/* Free the history entry H and return any application-specific data + associated with it. */ +extern histdata_t free_history_entry PARAMS((HIST_ENTRY *)); + /* Make the history entry at WHICH have LINE and DATA. This returns the old entry so you can dispose of the data. In the case of an invalid WHICH, a NULL pointer is returned. */ @@ -119,6 +133,10 @@ extern HIST_ENTRY *current_history PARAMS((void)); array. OFFSET is relative to history_base. */ extern HIST_ENTRY *history_get PARAMS((int)); +/* Return the timestamp associated with the HIST_ENTRY * passed as an + argument */ +extern time_t history_get_time PARAMS((HIST_ENTRY *)); + /* Return the number of bytes that the primary history entries are using. This just adds up the lengths of the_history->lines. */ extern int history_total_bytes PARAMS((void)); @@ -225,12 +243,14 @@ extern int history_length; extern int history_max_entries; extern char history_expansion_char; extern char history_subst_char; -extern const char *history_word_delimiters; +extern char *history_word_delimiters; extern char history_comment_char; -extern const char *history_no_expand_chars; +extern char *history_no_expand_chars; extern char *history_search_delimiter_chars; extern int history_quotes_inhibit_expansion; +extern int history_write_timestamps; + /* Backwards compatibility */ extern int max_input_history; diff --git a/cmd-line-utils/readline/histsearch.c b/cmd-line-utils/readline/histsearch.c index ffc97d720db..1cc5875a4b4 100644 --- a/cmd-line-utils/readline/histsearch.c +++ b/cmd-line-utils/readline/histsearch.c @@ -22,7 +22,9 @@ #define READLINE_LIBRARY -#include "config_readline.h" +#if defined (HAVE_CONFIG_H) +# include +#endif #include #if defined (HAVE_STDLIB_H) @@ -75,11 +77,11 @@ history_search_internal (string, direction, anchored) if (string == 0 || *string == '\0') return (-1); - if (!history_length || ((i == history_length) && !reverse)) + if (!history_length || ((i >= history_length) && !reverse)) return (-1); - if (reverse && (i == history_length)) - i--; + if (reverse && (i >= history_length)) + i = history_length - 1; #define NEXT_LINE() do { if (reverse) i--; else i++; } while (0) diff --git a/cmd-line-utils/readline/input.c b/cmd-line-utils/readline/input.c index d9c52dfcec8..044338e879b 100644 --- a/cmd-line-utils/readline/input.c +++ b/cmd-line-utils/readline/input.c @@ -21,7 +21,13 @@ 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#include "config_readline.h" +#if defined (__TANDEM) +# include +#endif + +#if defined (HAVE_CONFIG_H) +# include +#endif #include #include @@ -152,6 +158,12 @@ _rl_unget_char (key) return (0); } +int +_rl_pushed_input_available () +{ + return (push_index != pop_index); +} + /* If a character is available to be read, then read it and stuff it into IBUFFER. Otherwise, just return. Returns number of characters read (0 if none available) and -1 on error (EIO). */ @@ -160,7 +172,7 @@ rl_gather_tyi () { int tty; register int tem, result; - int chars_avail; + int chars_avail, k; char input; #if defined(HAVE_SELECT) fd_set readfds, exceptfds; @@ -200,6 +212,11 @@ rl_gather_tyi () fcntl (tty, F_SETFL, tem); if (chars_avail == -1 && errno == EAGAIN) return 0; + if (chars_avail == 0) /* EOF */ + { + rl_stuff_char (EOF); + return (0); + } } #endif /* O_NDELAY */ @@ -223,7 +240,12 @@ rl_gather_tyi () if (result != -1) { while (chars_avail--) - rl_stuff_char ((*rl_getc_function) (rl_instream)); + { + k = (*rl_getc_function) (rl_instream); + rl_stuff_char (k); + if (k == NEWLINE || k == RETURN) + break; + } } else { @@ -385,7 +407,7 @@ rl_read_key () else { /* If input is coming from a macro, then use that. */ - if ((c = _rl_next_macro_key ())) + if (c = _rl_next_macro_key ()) return (c); /* If the user has an event function, then call it periodically. */ diff --git a/cmd-line-utils/readline/isearch.c b/cmd-line-utils/readline/isearch.c index 1de16c6a56c..c1ea5b30852 100644 --- a/cmd-line-utils/readline/isearch.c +++ b/cmd-line-utils/readline/isearch.c @@ -26,7 +26,9 @@ 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#include "config_readline.h" +#if defined (HAVE_CONFIG_H) +# include +#endif #include @@ -68,7 +70,7 @@ static char *prev_line_found; static char *last_isearch_string; static int last_isearch_string_len; -static const char *default_isearch_terminators = "\033\012"; +static char *default_isearch_terminators = "\033\012"; /* Search backwards through the history looking for a string which is typed interactively. Start with the current line. */ @@ -96,7 +98,7 @@ rl_forward_search_history (sign, key) static void rl_display_search (search_string, reverse_p, where) char *search_string; - int reverse_p, where __attribute__((unused)); + int reverse_p, where; { char *message; int msglen, searchlen; @@ -144,7 +146,7 @@ rl_display_search (search_string, reverse_p, where) backwards. */ static int rl_search_history (direction, invoking_key) - int direction, invoking_key __attribute__((unused)); + int direction, invoking_key; { /* The string that the user types in to search for. */ char *search_string; @@ -184,7 +186,7 @@ rl_search_history (direction, invoking_key) /* The list of characters which terminate the search, but are not subsequently executed. If the variable isearch-terminators has been set, we use that value, otherwise we use ESC and C-J. */ - const char *isearch_terminators; + char *isearch_terminators; RL_SETSTATE(RL_STATE_ISEARCH); orig_point = rl_point; diff --git a/cmd-line-utils/readline/keymaps.c b/cmd-line-utils/readline/keymaps.c index 9972d83e4f1..70d0cc08d3f 100644 --- a/cmd-line-utils/readline/keymaps.c +++ b/cmd-line-utils/readline/keymaps.c @@ -20,7 +20,9 @@ Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#include "config_readline.h" +#if defined (HAVE_CONFIG_H) +# include +#endif #if defined (HAVE_STDLIB_H) # include @@ -62,11 +64,13 @@ rl_make_bare_keymap () keymap[i].function = (rl_command_func_t *)NULL; } +#if 0 for (i = 'A'; i < ('Z' + 1); i++) { keymap[i].type = ISFUNC; keymap[i].function = rl_do_lowercase_version; } +#endif return (keymap); } @@ -77,8 +81,9 @@ rl_copy_keymap (map) Keymap map; { register int i; - Keymap temp = rl_make_bare_keymap (); + Keymap temp; + temp = rl_make_bare_keymap (); for (i = 0; i < KEYMAP_SIZE; i++) { temp[i].type = map[i].type; @@ -107,12 +112,8 @@ rl_make_keymap () newmap[CTRL('H')].function = rl_rubout; #if KEYMAP_SIZE > 128 - /* Printing characters in some 8-bit character sets. */ - for (i = 128; i < 160; i++) - newmap[i].function = rl_insert; - - /* ISO Latin-1 printing characters should self-insert. */ - for (i = 160; i < 256; i++) + /* Printing characters in ISO Latin-1 and some 8-bit character sets. */ + for (i = 128; i < 256; i++) newmap[i].function = rl_insert; #endif /* KEYMAP_SIZE > 128 */ diff --git a/cmd-line-utils/readline/kill.c b/cmd-line-utils/readline/kill.c index 32a661f076f..1d3254c3275 100644 --- a/cmd-line-utils/readline/kill.c +++ b/cmd-line-utils/readline/kill.c @@ -21,7 +21,9 @@ 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#include "config_readline.h" +#if defined (HAVE_CONFIG_H) +# include +#endif #include @@ -77,7 +79,7 @@ static int rl_yank_nth_arg_internal PARAMS((int, int, int)); of kill material. */ int rl_set_retained_kills (num) - int num __attribute__((unused)); + int num; { return 0; } @@ -294,7 +296,7 @@ rl_backward_kill_line (direction, ignore) /* Kill the whole line, no matter where point is. */ int rl_kill_full_line (count, ignore) - int count __attribute__((unused)), ignore __attribute__((unused)); + int count, ignore; { rl_begin_undo_group (); rl_point = 0; @@ -312,7 +314,7 @@ rl_kill_full_line (count, ignore) using behaviour that they expect. */ int rl_unix_word_rubout (count, key) - int count, key __attribute__((unused)); + int count, key; { int orig_point; @@ -337,6 +339,47 @@ rl_unix_word_rubout (count, key) if (rl_editing_mode == emacs_mode) rl_mark = rl_point; } + + return 0; +} + +/* This deletes one filename component in a Unix pathname. That is, it + deletes backward to directory separator (`/') or whitespace. */ +int +rl_unix_filename_rubout (count, key) + int count, key; +{ + int orig_point, c; + + if (rl_point == 0) + rl_ding (); + else + { + orig_point = rl_point; + if (count <= 0) + count = 1; + + while (count--) + { + c = rl_line_buffer[rl_point - 1]; + while (rl_point && (whitespace (c) || c == '/')) + { + rl_point--; + c = rl_line_buffer[rl_point - 1]; + } + + while (rl_point && (whitespace (c) == 0) && c != '/') + { + rl_point--; + c = rl_line_buffer[rl_point - 1]; + } + } + + rl_kill_text (orig_point, rl_point); + if (rl_editing_mode == emacs_mode) + rl_mark = rl_point; + } + return 0; } @@ -348,7 +391,7 @@ rl_unix_word_rubout (count, key) doing. */ int rl_unix_line_discard (count, key) - int count __attribute__((unused)), key __attribute__((unused)); + int count, key; { if (rl_point == 0) rl_ding (); @@ -385,7 +428,7 @@ region_kill_internal (delete) /* Copy the text in the region to the kill ring. */ int rl_copy_region_to_kill (count, ignore) - int count __attribute__((unused)), ignore __attribute__((unused)); + int count, ignore; { return (region_kill_internal (0)); } @@ -393,7 +436,7 @@ rl_copy_region_to_kill (count, ignore) /* Kill the text between the point and mark. */ int rl_kill_region (count, ignore) - int count __attribute__((unused)), ignore __attribute__((unused)); + int count, ignore; { int r, npoint; @@ -458,7 +501,7 @@ rl_copy_backward_word (count, key) /* Yank back the last killed text. This ignores arguments. */ int rl_yank (count, ignore) - int count __attribute__((unused)), ignore __attribute__((unused)); + int count, ignore; { if (rl_kill_ring == 0) { @@ -477,7 +520,7 @@ rl_yank (count, ignore) yank back some other text. */ int rl_yank_pop (count, key) - int count __attribute__((unused)), key __attribute__((unused)); + int count, key; { int l, n; diff --git a/cmd-line-utils/readline/macro.c b/cmd-line-utils/readline/macro.c index 7f5c39f7d86..b73c3af9aa4 100644 --- a/cmd-line-utils/readline/macro.c +++ b/cmd-line-utils/readline/macro.c @@ -21,7 +21,9 @@ 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#include "config_readline.h" +#if defined (HAVE_CONFIG_H) +# include +#endif #include @@ -190,7 +192,7 @@ _rl_kill_kbd_macro () re-executing the existing macro. */ int rl_start_kbd_macro (ignore1, ignore2) - int ignore1 __attribute__((unused)), ignore2 __attribute__((unused)); + int ignore1, ignore2; { if (RL_ISSTATE (RL_STATE_MACRODEF)) { @@ -215,7 +217,7 @@ rl_start_kbd_macro (ignore1, ignore2) that many times, counting the definition as the first time. */ int rl_end_kbd_macro (count, ignore) - int count, ignore __attribute__((unused)); + int count, ignore; { if (RL_ISSTATE (RL_STATE_MACRODEF) == 0) { @@ -235,7 +237,7 @@ rl_end_kbd_macro (count, ignore) COUNT says how many times to execute it. */ int rl_call_last_kbd_macro (count, ignore) - int count, ignore __attribute__((unused)); + int count, ignore; { if (current_macro == 0) _rl_abort_internal (); diff --git a/cmd-line-utils/readline/mbutil.c b/cmd-line-utils/readline/mbutil.c index 3113b7b0538..9a8f17c0f74 100644 --- a/cmd-line-utils/readline/mbutil.c +++ b/cmd-line-utils/readline/mbutil.c @@ -1,6 +1,6 @@ /* mbutil.c -- readline multibyte character utility functions */ -/* Copyright (C) 2001 Free Software Foundation, Inc. +/* Copyright (C) 2001-2004 Free Software Foundation, Inc. This file is part of the GNU Readline Library, a library for reading lines of text with interactive input and history editing. @@ -21,7 +21,9 @@ 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#include "config_readline.h" +#if defined (HAVE_CONFIG_H) +# include +#endif #include #include @@ -90,12 +92,12 @@ _rl_find_next_mbchar_internal (string, seed, count, find_non_zero) /* if this is true, means that seed was not pointed character started byte. So correct the point and consume count */ if (seed < point) - count --; + count--; while (count > 0) { tmp = mbrtowc (&wc, string+point, strlen(string + point), &ps); - if ((size_t)(tmp) == (size_t)-1 || (size_t)(tmp) == (size_t)-2) + if (MB_INVALIDCH ((size_t)tmp)) { /* invalid bytes. asume a byte represents a character */ point++; @@ -103,9 +105,8 @@ _rl_find_next_mbchar_internal (string, seed, count, find_non_zero) /* reset states. */ memset(&ps, 0, sizeof(mbstate_t)); } - else if (tmp == (size_t)0) - /* found '\0' char */ - break; + else if (MB_NULLWCH (tmp)) + break; /* found wide '\0' */ else { /* valid bytes */ @@ -158,7 +159,7 @@ _rl_find_prev_mbchar_internal (string, seed, find_non_zero) while (point < seed) { tmp = mbrtowc (&wc, string + point, length - point, &ps); - if ((size_t)(tmp) == (size_t)-1 || (size_t)(tmp) == (size_t)-2) + if (MB_INVALIDCH ((size_t)tmp)) { /* in this case, bytes are invalid or shorted to compose multibyte char, so assume that the first byte represents @@ -167,8 +168,12 @@ _rl_find_prev_mbchar_internal (string, seed, find_non_zero) /* clear the state of the byte sequence, because in this case effect of mbstate is undefined */ memset(&ps, 0, sizeof (mbstate_t)); + + /* Since we're assuming that this byte represents a single + non-zero-width character, don't forget about it. */ + prev = point; } - else if (tmp == 0) + else if (MB_NULLWCH (tmp)) break; /* Found '\0' char. Can this happen? */ else { @@ -194,7 +199,7 @@ _rl_find_prev_mbchar_internal (string, seed, find_non_zero) if it couldn't parse a complete multibyte character. */ int _rl_get_char_len (src, ps) - const char *src; + char *src; mbstate_t *ps; { size_t tmp; @@ -203,14 +208,16 @@ _rl_get_char_len (src, ps) if (tmp == (size_t)(-2)) { /* shorted to compose multibyte char */ - memset (ps, 0, sizeof(mbstate_t)); + if (ps) + memset (ps, 0, sizeof(mbstate_t)); return -2; } else if (tmp == (size_t)(-1)) { /* invalid to compose multibyte char */ /* initialize the conversion state */ - memset (ps, 0, sizeof(mbstate_t)); + if (ps) + memset (ps, 0, sizeof(mbstate_t)); return -1; } else if (tmp == (size_t)0) @@ -223,9 +230,12 @@ _rl_get_char_len (src, ps) return 1. Otherwise return 0. */ int _rl_compare_chars (buf1, pos1, ps1, buf2, pos2, ps2) - char *buf1, *buf2; - mbstate_t *ps1, *ps2; - int pos1, pos2; + char *buf1; + int pos1; + mbstate_t *ps1; + char *buf2; + int pos2; + mbstate_t *ps2; { int i, w1, w2; @@ -249,7 +259,7 @@ _rl_compare_chars (buf1, pos1, ps1, buf2, pos2, ps2) it returns -1 */ int _rl_adjust_point(string, point, ps) - const char *string; + char *string; int point; mbstate_t *ps; { @@ -266,7 +276,7 @@ _rl_adjust_point(string, point, ps) while (pos < point) { tmp = mbrlen (string + pos, length - pos, ps); - if((size_t)(tmp) == (size_t)-1 || (size_t)(tmp) == (size_t)-2) + if (MB_INVALIDCH ((size_t)tmp)) { /* in this case, bytes are invalid or shorted to compose multibyte char, so assume that the first byte represents @@ -274,8 +284,11 @@ _rl_adjust_point(string, point, ps) pos++; /* clear the state of the byte sequence, because in this case effect of mbstate is undefined */ - memset (ps, 0, sizeof (mbstate_t)); + if (ps) + memset (ps, 0, sizeof (mbstate_t)); } + else if (MB_NULLWCH (tmp)) + pos++; else pos += tmp; } @@ -308,8 +321,8 @@ _rl_is_mbchar_matched (string, seed, end, mbchar, length) #undef _rl_find_next_mbchar int _rl_find_next_mbchar (string, seed, count, flags) - char *string __attribute__((unused)); - int seed, count, flags __attribute__((unused)); + char *string; + int seed, count, flags; { #if defined (HANDLE_MULTIBYTE) return _rl_find_next_mbchar_internal (string, seed, count, flags); @@ -324,8 +337,8 @@ _rl_find_next_mbchar (string, seed, count, flags) #undef _rl_find_prev_mbchar int _rl_find_prev_mbchar (string, seed, flags) - char *string __attribute__((unused)); - int seed, flags __attribute__((unused)); + char *string; + int seed, flags; { #if defined (HANDLE_MULTIBYTE) return _rl_find_prev_mbchar_internal (string, seed, flags); diff --git a/cmd-line-utils/readline/misc.c b/cmd-line-utils/readline/misc.c index 858d09dbe90..ab1e1337fd3 100644 --- a/cmd-line-utils/readline/misc.c +++ b/cmd-line-utils/readline/misc.c @@ -1,6 +1,6 @@ /* misc.c -- miscellaneous bindable readline functions. */ -/* Copyright (C) 1987-2002 Free Software Foundation, Inc. +/* Copyright (C) 1987-2004 Free Software Foundation, Inc. This file is part of the GNU Readline Library, a library for reading lines of text with interactive input and history editing. @@ -21,7 +21,9 @@ 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#include "config_readline.h" +#if defined (HAVE_CONFIG_H) +# include +#endif #if defined (HAVE_UNISTD_H) # include @@ -155,7 +157,7 @@ rl_digit_loop () /* Add the current digit to the argument in progress. */ int rl_digit_argument (ignore, key) - int ignore __attribute__((unused)), key; + int ignore, key; { rl_execute_next (key); return (rl_digit_loop ()); @@ -185,7 +187,7 @@ _rl_init_argument () dispatch on it. If the key is the abort character then abort. */ int rl_universal_argument (count, key) - int count __attribute__((unused)), key __attribute__((unused)); + int count, key; { rl_numeric_arg *= 4; return (rl_digit_loop ()); @@ -251,6 +253,8 @@ rl_maybe_unsave_line () { if (_rl_saved_line_for_history) { + /* Can't call with `1' because rl_undo_list might point to an undo + list from a history entry, as in rl_replace_from_history() below. */ rl_replace_line (_rl_saved_line_for_history->line, 0); rl_undo_list = (UNDO_LIST *)_rl_saved_line_for_history->data; _rl_free_history_entry (_rl_saved_line_for_history); @@ -272,6 +276,13 @@ rl_maybe_save_line () _rl_saved_line_for_history->line = savestring (rl_line_buffer); _rl_saved_line_for_history->data = (char *)rl_undo_list; } + else if (STREQ (rl_line_buffer, _rl_saved_line_for_history->line) == 0) + { + free (_rl_saved_line_for_history->line); + _rl_saved_line_for_history->line = savestring (rl_line_buffer); + _rl_saved_line_for_history->data = (char *)rl_undo_list; /* XXX possible memleak */ + } + return 0; } @@ -296,7 +307,7 @@ _rl_history_set_point () rl_point = rl_end; #if defined (VI_MODE) - if (rl_editing_mode == vi_mode) + if (rl_editing_mode == vi_mode && _rl_keymap != vi_insertion_keymap) rl_point = 0; #endif /* VI_MODE */ @@ -307,8 +318,10 @@ _rl_history_set_point () void rl_replace_from_history (entry, flags) HIST_ENTRY *entry; - int flags __attribute__((unused)); /* currently unused */ + int flags; /* currently unused */ { + /* Can't call with `1' because rl_undo_list might point to an undo list + from a history entry, just like we're setting up here. */ rl_replace_line (entry->line, 0); rl_undo_list = (UNDO_LIST *)entry->data; rl_point = rl_end; @@ -332,7 +345,7 @@ rl_replace_from_history (entry, flags) /* Meta-< goes to the start of the history. */ int rl_beginning_of_history (count, key) - int count __attribute__((unused)), key; + int count, key; { return (rl_get_previous_history (1 + where_history (), key)); } @@ -340,7 +353,7 @@ rl_beginning_of_history (count, key) /* Meta-> goes to the end of the history. (The current line). */ int rl_end_of_history (count, key) - int count __attribute__((unused)), key __attribute__((unused)); + int count, key; { rl_maybe_replace_line (); using_history (); @@ -433,6 +446,7 @@ rl_get_previous_history (count, key) rl_replace_from_history (temp, 0); _rl_history_set_point (); } + return 0; } @@ -444,7 +458,7 @@ rl_get_previous_history (count, key) /* How to toggle back and forth between editing modes. */ int rl_vi_editing_mode (count, key) - int count __attribute__((unused)), key; + int count, key; { #if defined (VI_MODE) _rl_set_insert_mode (RL_IM_INSERT, 1); /* vi mode ignores insert mode */ @@ -457,7 +471,7 @@ rl_vi_editing_mode (count, key) int rl_emacs_editing_mode (count, key) - int count __attribute__((unused)), key __attribute__((unused)); + int count, key; { rl_editing_mode = emacs_mode; _rl_set_insert_mode (RL_IM_INSERT, 1); /* emacs mode default is insert mode */ @@ -468,7 +482,7 @@ rl_emacs_editing_mode (count, key) /* Function for the rest of the library to use to set insert/overwrite mode. */ void _rl_set_insert_mode (im, force) - int im, force __attribute__((unused)); + int im, force; { #ifdef CURSOR_MODE _rl_set_cursor (im, force); @@ -481,7 +495,7 @@ _rl_set_insert_mode (im, force) mode. A negative or zero explicit argument selects insert mode. */ int rl_overwrite_mode (count, key) - int count, key __attribute__((unused)); + int count, key; { if (rl_explicit_arg == 0) _rl_set_insert_mode (rl_insert_mode ^ 1, 0); diff --git a/cmd-line-utils/readline/nls.c b/cmd-line-utils/readline/nls.c index 6555c50c22b..bcee87561aa 100644 --- a/cmd-line-utils/readline/nls.c +++ b/cmd-line-utils/readline/nls.c @@ -21,7 +21,9 @@ 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#include "config_readline.h" +#if defined (HAVE_CONFIG_H) +# include +#endif #include @@ -73,6 +75,23 @@ static char *normalize_codeset PARAMS((char *)); static char *find_codeset PARAMS((char *, size_t *)); #endif /* !HAVE_SETLOCALE */ +static char *_rl_get_locale_var PARAMS((const char *)); + +static char * +_rl_get_locale_var (v) + const char *v; +{ + char *lspec; + + lspec = sh_get_env_value ("LC_ALL"); + if (lspec == 0 || *lspec == 0) + lspec = sh_get_env_value (v); + if (lspec == 0 || *lspec == 0) + lspec = sh_get_env_value ("LANG"); + + return lspec; +} + /* Check for LC_ALL, LC_CTYPE, and LANG and use the first with a value to decide the defaults for 8-bit character input and output. Returns 1 if we set eight-bit mode. */ @@ -82,10 +101,21 @@ _rl_init_eightbit () /* If we have setlocale(3), just check the current LC_CTYPE category value, and go into eight-bit mode if it's not C or POSIX. */ #if defined (HAVE_SETLOCALE) - char *t; + char *lspec, *t; /* Set the LC_CTYPE locale category from environment variables. */ - t = setlocale (LC_CTYPE, ""); + lspec = _rl_get_locale_var ("LC_CTYPE"); + /* Since _rl_get_locale_var queries the right environment variables, + we query the current locale settings with setlocale(), and, if + that doesn't return anything, we set lspec to the empty string to + force the subsequent call to setlocale() to define the `native' + environment. */ + if (lspec == 0 || *lspec == 0) + lspec = setlocale (LC_CTYPE, (char *)NULL); + if (lspec == 0) + lspec = ""; + t = setlocale (LC_CTYPE, lspec); + if (t && *t && (t[0] != 'C' || t[1]) && (STREQ (t, "POSIX") == 0)) { _rl_meta_flag = 1; @@ -103,9 +133,8 @@ _rl_init_eightbit () /* We don't have setlocale. Finesse it. Check the environment for the appropriate variables and set eight-bit mode if they have the right values. */ - lspec = sh_get_env_value ("LC_ALL"); - if (lspec == 0) lspec = sh_get_env_value ("LC_CTYPE"); - if (lspec == 0) lspec = sh_get_env_value ("LANG"); + lspec = _rl_get_locale_var ("LC_CTYPE"); + if (lspec == 0 || (t = normalize_codeset (lspec)) == 0) return (0); for (i = 0; t && legal_lang_values[i]; i++) diff --git a/cmd-line-utils/readline/parens.c b/cmd-line-utils/readline/parens.c index 5d4a08a0ce8..737f7675e93 100644 --- a/cmd-line-utils/readline/parens.c +++ b/cmd-line-utils/readline/parens.c @@ -21,9 +21,15 @@ 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY +#if defined (__TANDEM) +# include +#endif + #include "rlconf.h" -#include "config_readline.h" +#if defined (HAVE_CONFIG_H) +# include +#endif #include #include diff --git a/cmd-line-utils/readline/posixdir.h b/cmd-line-utils/readline/posixdir.h index 505e27954f1..91f6d96111d 100644 --- a/cmd-line-utils/readline/posixdir.h +++ b/cmd-line-utils/readline/posixdir.h @@ -25,7 +25,11 @@ #if defined (HAVE_DIRENT_H) # include -# define D_NAMLEN(d) (strlen ((d)->d_name)) +# if defined (HAVE_STRUCT_DIRENT_D_NAMLEN) +# define D_NAMLEN(d) ((d)->d_namlen) +# else +# define D_NAMLEN(d) (strlen ((d)->d_name)) +# endif /* !HAVE_STRUCT_DIRENT_D_NAMLEN */ #else # if defined (HAVE_SYS_NDIR_H) # include @@ -42,11 +46,11 @@ # define D_NAMLEN(d) ((d)->d_namlen) #endif /* !HAVE_DIRENT_H */ -#if defined (STRUCT_DIRENT_HAS_D_INO) && !defined (STRUCT_DIRENT_HAS_D_FILENO) +#if defined (HAVE_STRUCT_DIRENT_D_INO) && !defined (HAVE_STRUCT_DIRENT_D_FILENO) # define d_fileno d_ino #endif -#if defined (_POSIX_SOURCE) && (!defined (STRUCT_DIRENT_HAS_D_INO) || defined (BROKEN_DIRENT_D_INO)) +#if defined (_POSIX_SOURCE) && (!defined (HAVE_STRUCT_DIRENT_D_INO) || defined (BROKEN_DIRENT_D_INO)) /* Posix does not require that the d_ino field be present, and some systems do not provide it. */ # define REAL_DIR_ENTRY(dp) 1 diff --git a/cmd-line-utils/readline/readline.c b/cmd-line-utils/readline/readline.c index 2c0bb499b7b..a70d80ff492 100644 --- a/cmd-line-utils/readline/readline.c +++ b/cmd-line-utils/readline/readline.c @@ -22,7 +22,9 @@ 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#include "config_readline.h" +#if defined (HAVE_CONFIG_H) +# include +#endif #include #include "posixstat.h" @@ -66,11 +68,11 @@ #include "xmalloc.h" #ifndef RL_LIBRARY_VERSION -# define RL_LIBRARY_VERSION "4.3" +# define RL_LIBRARY_VERSION "5.0" #endif #ifndef RL_READLINE_VERSION -# define RL_READLINE_VERSION 0x0403 +# define RL_READLINE_VERSION 0x0500 #endif extern void _rl_free_history_entry PARAMS((HIST_ENTRY *)); @@ -83,6 +85,7 @@ static void bind_arrow_keys_internal PARAMS((Keymap)); static void bind_arrow_keys PARAMS((void)); static void readline_default_bindings PARAMS((void)); +static void reset_default_bindings PARAMS((void)); /* **************************************************************** */ /* */ @@ -345,7 +348,7 @@ readline_internal_setup () #if defined (VI_MODE) if (rl_editing_mode == vi_mode) - rl_vi_insertion_mode (1, 0); + rl_vi_insertion_mode (1, 'i'); #endif /* VI_MODE */ if (rl_pre_input_hook) @@ -648,7 +651,21 @@ _rl_dispatch_subseq (key, map, got_subseq) the function. The recursive call to _rl_dispatch_subseq has already taken care of pushing any necessary input back onto the input queue with _rl_unget_char. */ - r = _rl_dispatch (ANYOTHERKEY, FUNCTION_TO_KEYMAP (map, key)); + { +#if 0 + r = _rl_dispatch (ANYOTHERKEY, FUNCTION_TO_KEYMAP (map, key)); +#else + /* XXX - experimental code -- might never be executed. Save + for later. */ + Keymap m = FUNCTION_TO_KEYMAP (map, key); + int type = m[ANYOTHERKEY].type; + func = m[ANYOTHERKEY].function; + if (type == ISFUNC && func == rl_do_lowercase_version) + r = _rl_dispatch (_rl_to_lower (key), map); + else + r = _rl_dispatch (ANYOTHERKEY, m); +#endif + } else if (r && map[ANYOTHERKEY].function) { /* We didn't match (r is probably -1), so return something to @@ -682,6 +699,7 @@ _rl_dispatch_subseq (key, map, got_subseq) } #if defined (VI_MODE) if (rl_editing_mode == vi_mode && _rl_keymap == vi_movement_keymap && + key != ANYOTHERKEY && _rl_vi_textmod_command (key)) _rl_vi_set_last (key, rl_numeric_arg, rl_arg_sign); #endif @@ -836,7 +854,7 @@ readline_initialize_everything () /* If the completion parser's default word break characters haven't been set yet, then do so now. */ if (rl_completer_word_break_characters == (char *)NULL) - rl_completer_word_break_characters = rl_basic_word_break_characters; + rl_completer_word_break_characters = (char *)rl_basic_word_break_characters; } /* If this system allows us to look at the values of the regular @@ -848,6 +866,15 @@ readline_default_bindings () rl_tty_set_default_bindings (_rl_keymap); } +/* Reset the default bindings for the terminal special characters we're + interested in back to rl_insert and read the new ones. */ +static void +reset_default_bindings () +{ + rl_tty_unset_default_bindings (_rl_keymap); + rl_tty_set_default_bindings (_rl_keymap); +} + /* Bind some common arrow key sequences in MAP. */ static void bind_arrow_keys_internal (map) @@ -859,25 +886,25 @@ bind_arrow_keys_internal (map) _rl_keymap = map; #if defined (__MSDOS__) - _rl_bind_if_unbound ("\033[0A", rl_get_previous_history); - _rl_bind_if_unbound ("\033[0B", rl_backward_char); - _rl_bind_if_unbound ("\033[0C", rl_forward_char); - _rl_bind_if_unbound ("\033[0D", rl_get_next_history); + rl_bind_keyseq_if_unbound ("\033[0A", rl_get_previous_history); + rl_bind_keyseq_if_unbound ("\033[0B", rl_backward_char); + rl_bind_keyseq_if_unbound ("\033[0C", rl_forward_char); + rl_bind_keyseq_if_unbound ("\033[0D", rl_get_next_history); #endif - _rl_bind_if_unbound ("\033[A", rl_get_previous_history); - _rl_bind_if_unbound ("\033[B", rl_get_next_history); - _rl_bind_if_unbound ("\033[C", rl_forward_char); - _rl_bind_if_unbound ("\033[D", rl_backward_char); - _rl_bind_if_unbound ("\033[H", rl_beg_of_line); - _rl_bind_if_unbound ("\033[F", rl_end_of_line); + rl_bind_keyseq_if_unbound ("\033[A", rl_get_previous_history); + rl_bind_keyseq_if_unbound ("\033[B", rl_get_next_history); + rl_bind_keyseq_if_unbound ("\033[C", rl_forward_char); + rl_bind_keyseq_if_unbound ("\033[D", rl_backward_char); + rl_bind_keyseq_if_unbound ("\033[H", rl_beg_of_line); + rl_bind_keyseq_if_unbound ("\033[F", rl_end_of_line); - _rl_bind_if_unbound ("\033OA", rl_get_previous_history); - _rl_bind_if_unbound ("\033OB", rl_get_next_history); - _rl_bind_if_unbound ("\033OC", rl_forward_char); - _rl_bind_if_unbound ("\033OD", rl_backward_char); - _rl_bind_if_unbound ("\033OH", rl_beg_of_line); - _rl_bind_if_unbound ("\033OF", rl_end_of_line); + rl_bind_keyseq_if_unbound ("\033OA", rl_get_previous_history); + rl_bind_keyseq_if_unbound ("\033OB", rl_get_next_history); + rl_bind_keyseq_if_unbound ("\033OC", rl_forward_char); + rl_bind_keyseq_if_unbound ("\033OD", rl_backward_char); + rl_bind_keyseq_if_unbound ("\033OH", rl_beg_of_line); + rl_bind_keyseq_if_unbound ("\033OF", rl_end_of_line); _rl_keymap = xkeymap; } diff --git a/cmd-line-utils/readline/readline.h b/cmd-line-utils/readline/readline.h index 9425de50aef..222b317c4a8 100644 --- a/cmd-line-utils/readline/readline.h +++ b/cmd-line-utils/readline/readline.h @@ -1,6 +1,6 @@ /* Readline.h -- the names of functions callable from within readline. */ -/* Copyright (C) 1987, 1989, 1992 Free Software Foundation, Inc. +/* Copyright (C) 1987-2004 Free Software Foundation, Inc. This file is part of the GNU Readline Library, a library for reading lines of text with interactive input and history editing. @@ -40,9 +40,9 @@ extern "C" { #endif /* Hex-encoded Readline version number. */ -#define RL_READLINE_VERSION 0x0403 /* Readline 4.3 */ -#define RL_VERSION_MAJOR 4 -#define RL_VERSION_MINOR 3 +#define RL_READLINE_VERSION 0x0500 /* Readline 5.0 */ +#define RL_VERSION_MAJOR 5 +#define RL_VERSION_MINOR 0 /* Readline data structures. */ @@ -160,6 +160,7 @@ extern int rl_kill_line PARAMS((int, int)); extern int rl_backward_kill_line PARAMS((int, int)); extern int rl_kill_full_line PARAMS((int, int)); extern int rl_unix_word_rubout PARAMS((int, int)); +extern int rl_unix_filename_rubout PARAMS((int, int)); extern int rl_unix_line_discard PARAMS((int, int)); extern int rl_copy_region_to_kill PARAMS((int, int)); extern int rl_kill_region PARAMS((int, int)); @@ -258,6 +259,8 @@ extern int rl_vi_check PARAMS((void)); extern int rl_vi_domove PARAMS((int, int *)); extern int rl_vi_bracktype PARAMS((int)); +extern void rl_vi_start_inserting PARAMS((int, int, int)); + /* VI-mode pseudo-bindable commands, used as utility functions. */ extern int rl_vi_fWord PARAMS((int, int)); extern int rl_vi_bWord PARAMS((int, int)); @@ -290,12 +293,20 @@ extern int rl_bind_key PARAMS((int, rl_command_func_t *)); extern int rl_bind_key_in_map PARAMS((int, rl_command_func_t *, Keymap)); extern int rl_unbind_key PARAMS((int)); extern int rl_unbind_key_in_map PARAMS((int, Keymap)); +extern int rl_bind_key_if_unbound PARAMS((int, rl_command_func_t *)); +extern int rl_bind_key_if_unbound_in_map PARAMS((int, rl_command_func_t *, Keymap)); extern int rl_unbind_function_in_map PARAMS((rl_command_func_t *, Keymap)); extern int rl_unbind_command_in_map PARAMS((const char *, Keymap)); -extern int rl_set_key PARAMS((const char *, rl_command_func_t *, Keymap)); +extern int rl_bind_keyseq PARAMS((const char *, rl_command_func_t *)); +extern int rl_bind_keyseq_in_map PARAMS((const char *, rl_command_func_t *, Keymap)); +extern int rl_bind_keyseq_if_unbound PARAMS((const char *, rl_command_func_t *)); +extern int rl_bind_keyseq_if_unbound_in_map PARAMS((const char *, rl_command_func_t *, Keymap)); extern int rl_generic_bind PARAMS((int, const char *, char *, Keymap)); extern int rl_variable_bind PARAMS((const char *, const char *)); +/* Backwards compatibility, use rl_bind_keyseq_in_map instead. */ +extern int rl_set_key PARAMS((const char *, rl_command_func_t *, Keymap)); + /* Backwards compatibility, use rl_generic_bind instead. */ extern int rl_macro_bind PARAMS((const char *, const char *, Keymap)); @@ -329,7 +340,7 @@ extern void rl_set_keymap PARAMS((Keymap)); extern Keymap rl_get_keymap PARAMS((void)); /* Undocumented; used internally only. */ extern void rl_set_keymap_from_edit_mode PARAMS((void)); -extern const char *rl_get_keymap_name_from_edit_mode PARAMS((void)); +extern char *rl_get_keymap_name_from_edit_mode PARAMS((void)); /* Functions for manipulating the funmap, which maps command names to functions. */ extern int rl_add_funmap_entry PARAMS((const char *, rl_command_func_t *)); @@ -358,7 +369,7 @@ extern int rl_clear_message PARAMS((void)); extern int rl_reset_line_state PARAMS((void)); extern int rl_crlf PARAMS((void)); -#if (defined (__STDC__) || defined (__cplusplus)) && defined (USE_VARARGS) && defined (PREFER_STDARG) +#if defined (USE_VARARGS) && defined (PREFER_STDARG) extern int rl_message (const char *, ...) __attribute__((__format__ (printf, 1, 2))); #else extern int rl_message (); @@ -384,13 +395,14 @@ extern char *rl_copy_text PARAMS((int, int)); extern void rl_prep_terminal PARAMS((int)); extern void rl_deprep_terminal PARAMS((void)); extern void rl_tty_set_default_bindings PARAMS((Keymap)); +extern void rl_tty_unset_default_bindings PARAMS((Keymap)); extern int rl_reset_terminal PARAMS((const char *)); extern void rl_resize_terminal PARAMS((void)); extern void rl_set_screen_size PARAMS((int, int)); extern void rl_get_screen_size PARAMS((int *, int *)); -extern const char *rl_get_termcap PARAMS((const char *)); +extern char *rl_get_termcap PARAMS((const char *)); /* Functions for character input. */ extern int rl_stuff_char PARAMS((int)); @@ -603,7 +615,12 @@ extern const char *rl_basic_word_break_characters; /* The list of characters that signal a break between words for rl_complete_internal. The default list is the contents of rl_basic_word_break_characters. */ -extern const char *rl_completer_word_break_characters; +extern /*const*/ char *rl_completer_word_break_characters; + +/* Hook function to allow an application to set the completion word + break characters before readline breaks up the line. Allows + position-dependent word break characters. */ +extern rl_cpvfunc_t *rl_completion_word_break_hook; /* List of characters which can be used to quote a substring of the line. Completion occurs on the entire substring, and within the substring @@ -687,6 +704,11 @@ extern int rl_attempted_completion_over; functions. */ extern int rl_completion_type; +/* Up to this many items will be displayed in response to a + possible-completions call. After that, we ask the user if she + is sure she wants to see them all. The default value is 100. */ +extern int rl_completion_query_items; + /* Character appended to completed words when at the end of the line. The default is a space. Nothing is added if this is '\0'. */ extern int rl_completion_append_character; @@ -695,10 +717,18 @@ extern int rl_completion_append_character; rl_completion_append_character will not be appended. */ extern int rl_completion_suppress_append; -/* Up to this many items will be displayed in response to a - possible-completions call. After that, we ask the user if she - is sure she wants to see them all. The default value is 100. */ -extern int rl_completion_query_items; +/* Set to any quote character readline thinks it finds before any application + completion function is called. */ +extern int rl_completion_quote_character; + +/* Set to a non-zero value if readline found quoting anywhere in the word to + be completed; set before any application completion function is called. */ +extern int rl_completion_found_quote; + +/* If non-zero, the completion functions don't append any closing quote. + This is set to 0 by rl_complete_internal and may be changed by an + application-specific completion function. */ +extern int rl_completion_suppress_quote; /* If non-zero, a slash will be appended to completed filenames that are symbolic links to directory names, subject to the value of the @@ -749,6 +779,7 @@ extern int rl_inhibit_completion; #define RL_STATE_SIGHANDLER 0x08000 /* in readline sighandler */ #define RL_STATE_UNDOING 0x10000 /* doing an undo */ #define RL_STATE_INPUTPENDING 0x20000 /* rl_execute_next called */ +#define RL_STATE_TTYCSAVED 0x40000 /* tty special chars saved */ #define RL_STATE_DONE 0x80000 /* done; accepted line */ @@ -785,6 +816,12 @@ struct readline_state { int catchsigs; int catchsigwinch; + /* search state */ + + /* completion state */ + + /* options state */ + /* reserved for future expansion, so the struct size doesn't change */ char reserved[64]; }; diff --git a/cmd-line-utils/readline/rldefs.h b/cmd-line-utils/readline/rldefs.h index 5cba9a5ac32..0d600407b5f 100644 --- a/cmd-line-utils/readline/rldefs.h +++ b/cmd-line-utils/readline/rldefs.h @@ -32,10 +32,6 @@ #include "rlstdc.h" -#if !defined(__attribute__) && (defined(__cplusplus) || !defined(__GNUC__) || __GNUC__ == 2 && __GNUC_MINOR__ <8) -#define __attribute__(A) -#endif - #if defined (_POSIX_VERSION) && !defined (TERMIOS_MISSING) # define TERMIOS_TTY_DRIVER #else @@ -81,7 +77,7 @@ extern int _rl_stricmp PARAMS((char *, char *)); extern int _rl_strnicmp PARAMS((char *, char *, int)); #endif -#if defined (HAVE_STRPBRK) +#if defined (HAVE_STRPBRK) && !defined (HAVE_MULTIBYTE) # define _rl_strpbrk(a,b) strpbrk((a),(b)) #else extern char *_rl_strpbrk PARAMS((const char *, const char *)); diff --git a/cmd-line-utils/readline/rlmbutil.h b/cmd-line-utils/readline/rlmbutil.h index 4660a72fce5..77cc026e3e8 100644 --- a/cmd-line-utils/readline/rlmbutil.h +++ b/cmd-line-utils/readline/rlmbutil.h @@ -35,11 +35,18 @@ #if defined (HAVE_WCTYPE_H) && defined (HAVE_WCHAR_H) # include # include -# if defined (HAVE_MBSRTOWCS) /* system is supposed to support XPG5 */ +# if defined (HAVE_MBSRTOWCS) && defined (HAVE_MBRTOWC) && defined (HAVE_MBRLEN) && defined (HAVE_WCWIDTH) + /* system is supposed to support XPG5 */ # define HANDLE_MULTIBYTE 1 # endif #endif +/* If we don't want multibyte chars even on a system that supports them, let + the configuring user turn multibyte support off. */ +#if defined (NO_MULTIBYTE_SUPPORT) +# undef HANDLE_MULTIBYTE +#endif + /* Some systems, like BeOS, have multibyte encodings but lack mbstate_t. */ #if HANDLE_MULTIBYTE && !defined (HAVE_MBSTATE_T) # define wcsrtombs(dest, src, len, ps) (wcsrtombs) (dest, src, len, 0) @@ -82,14 +89,17 @@ extern int _rl_find_next_mbchar PARAMS((char *, int, int, int)); #ifdef HANDLE_MULTIBYTE extern int _rl_compare_chars PARAMS((char *, int, mbstate_t *, char *, int, mbstate_t *)); -extern int _rl_get_char_len PARAMS((const char *, mbstate_t *)); -extern int _rl_adjust_point PARAMS((const char *, int, mbstate_t *)); +extern int _rl_get_char_len PARAMS((char *, mbstate_t *)); +extern int _rl_adjust_point PARAMS((char *, int, mbstate_t *)); extern int _rl_read_mbchar PARAMS((char *, int)); extern int _rl_read_mbstring PARAMS((int, char *, int)); extern int _rl_is_mbchar_matched PARAMS((char *, int, int, char *, int)); +#define MB_INVALIDCH(x) ((x) == (size_t)-1 || (x) == (size_t)-2) +#define MB_NULLWCH(x) ((x) == 0) + #else /* !HANDLE_MULTIBYTE */ #undef MB_LEN_MAX @@ -101,6 +111,9 @@ extern int _rl_is_mbchar_matched PARAMS((char *, int, int, char *, int)); #define _rl_find_prev_mbchar(b, i, f) (((i) == 0) ? (i) : ((i) - 1)) #define _rl_find_next_mbchar(b, i1, i2, f) ((i1) + (i2)) +#define MB_INVALIDCH(x) (0) +#define MB_NULLWCH(x) (0) + #endif /* !HANDLE_MULTIBYTE */ extern int rl_byte_oriented; diff --git a/cmd-line-utils/readline/rlprivate.h b/cmd-line-utils/readline/rlprivate.h index 36645fb4a65..c3cee917b76 100644 --- a/cmd-line-utils/readline/rlprivate.h +++ b/cmd-line-utils/readline/rlprivate.h @@ -1,7 +1,7 @@ /* rlprivate.h -- functions and variables global to the readline library, but not intended for use by applications. */ -/* Copyright (C) 1999 Free Software Foundation, Inc. +/* Copyright (C) 1999-2004 Free Software Foundation, Inc. This file is part of the GNU Readline Library, a library for reading lines of text with interactive input and history editing. @@ -73,7 +73,7 @@ extern int rl_set_retained_kills PARAMS((int)); extern void _rl_set_screen_size PARAMS((int, int)); /* undo.c */ -extern int _rl_fix_last_undo_of_type PARAMS((unsigned int, int, int)); +extern int _rl_fix_last_undo_of_type PARAMS((int, int, int)); /* util.c */ extern char *_rl_savestring PARAMS((const char *)); @@ -103,7 +103,6 @@ extern int readline_internal_char PARAMS((void)); #endif /* READLINE_CALLBACKS */ /* bind.c */ -extern void _rl_bind_if_unbound PARAMS((const char *, rl_command_func_t *)); /* complete.c */ extern char _rl_find_completion_word PARAMS((int *, int *)); @@ -131,6 +130,7 @@ extern int _rl_input_available PARAMS((void)); extern int _rl_input_queued PARAMS((int)); extern void _rl_insert_typein PARAMS((int)); extern int _rl_unget_char PARAMS((int)); +extern int _rl_pushed_input_available PARAMS((void)); /* macro.c */ extern void _rl_with_macro_input PARAMS((char *)); @@ -219,6 +219,7 @@ extern const char *_rl_possible_meta_prefixes[]; /* complete.c */ extern int _rl_complete_show_all; +extern int _rl_complete_show_unmodified; extern int _rl_complete_mark_directories; extern int _rl_complete_mark_symlink_dirs; extern int _rl_print_completions_horizontally; @@ -230,7 +231,7 @@ extern int _rl_page_completions; extern int _rl_vis_botlin; extern int _rl_last_c_pos; extern int _rl_suppress_redisplay; -extern const char *rl_display_prompt; +extern char *rl_display_prompt; /* isearch.c */ extern char *_rl_isearch_terminators; @@ -261,16 +262,16 @@ extern procenv_t readline_top_level; /* terminal.c */ extern int _rl_enable_keypad; extern int _rl_enable_meta; -extern const char *_rl_term_clreol; -extern const char *_rl_term_clrpag; -extern const char *_rl_term_im; -extern const char *_rl_term_ic; -extern const char *_rl_term_ei; -extern const char *_rl_term_DC; -extern const char *_rl_term_up; -extern const char *_rl_term_dc; -extern const char *_rl_term_cr; -extern const char *_rl_term_IC; +extern char *_rl_term_clreol; +extern char *_rl_term_clrpag; +extern char *_rl_term_im; +extern char *_rl_term_ic; +extern char *_rl_term_ei; +extern char *_rl_term_DC; +extern char *_rl_term_up; +extern char *_rl_term_dc; +extern char *_rl_term_cr; +extern char *_rl_term_IC; extern int _rl_screenheight; extern int _rl_screenwidth; extern int _rl_screenchars; @@ -281,4 +282,7 @@ extern int _rl_term_autowrap; extern int _rl_doing_an_undo; extern int _rl_undo_group_level; +/* vi_mode.c */ +extern int _rl_vi_last_command; + #endif /* _RL_PRIVATE_H_ */ diff --git a/cmd-line-utils/readline/rlstdc.h b/cmd-line-utils/readline/rlstdc.h index d6a22b3742c..847fa9c26f4 100644 --- a/cmd-line-utils/readline/rlstdc.h +++ b/cmd-line-utils/readline/rlstdc.h @@ -37,7 +37,7 @@ #endif #ifndef __attribute__ -# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__ +# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) # define __attribute__(x) # endif #endif diff --git a/cmd-line-utils/readline/rltty.c b/cmd-line-utils/readline/rltty.c index 9a2cef4b279..1a31f35952e 100644 --- a/cmd-line-utils/readline/rltty.c +++ b/cmd-line-utils/readline/rltty.c @@ -22,7 +22,9 @@ 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#include "config_readline.h" +#if defined (HAVE_CONFIG_H) +# include +#endif #include #include @@ -184,6 +186,8 @@ static int set_tty_settings PARAMS((int, TIOTYPE *)); static void prepare_terminal_settings PARAMS((int, TIOTYPE, TIOTYPE *)); +static void set_special_char PARAMS((Keymap, TIOTYPE *, int, rl_command_func_t)); + static void save_tty_chars (tiop) TIOTYPE *tiop; @@ -398,6 +402,9 @@ static int set_tty_settings PARAMS((int, TIOTYPE *)); static void prepare_terminal_settings PARAMS((int, TIOTYPE, TIOTYPE *)); +static void set_special_char PARAMS((Keymap, TIOTYPE *, int, rl_command_func_t)); +static void _rl_bind_tty_special_chars PARAMS((Keymap, TIOTYPE)); + #if defined (FLUSHO) # define OUTPUT_BEING_FLUSHED(tp) (tp->c_lflag & FLUSHO) #else @@ -650,7 +657,10 @@ rl_prep_terminal (meta_flag) otio = tio; + rl_tty_unset_default_bindings (_rl_keymap); save_tty_chars (&otio); + RL_SETSTATE(RL_STATE_TTYCSAVED); + _rl_bind_tty_special_chars (_rl_keymap, tio); prepare_terminal_settings (meta_flag, otio, &tio); @@ -709,7 +719,7 @@ rl_deprep_terminal () int rl_restart_output (count, key) - int count __attribute__((unused)), key __attribute__((unused)); + int count, key; { int fildes = fileno (rl_outstream); #if defined (TIOCSTART) @@ -742,7 +752,7 @@ rl_restart_output (count, key) int rl_stop_output (count, key) - int count __attribute__((unused)), key __attribute__((unused)); + int count, key; { int fildes = fileno (rl_instream); @@ -774,6 +784,83 @@ rl_stop_output (count, key) /* */ /* **************************************************************** */ +#define SET_SPECIAL(sc, func) set_special_char(kmap, &ttybuff, sc, func) + +#if defined (NEW_TTY_DRIVER) +static void +set_special_char (kmap, tiop, sc, func) + Keymap kmap; + TIOTYPE *tiop; + int sc; + rl_command_func_t *func; +{ + if (sc != -1 && kmap[(unsigned char)sc].type == ISFUNC) + kmap[(unsigned char)sc].function = func; +} + +#define RESET_SPECIAL(c) \ + if (c != -1 && kmap[(unsigned char)c].type == ISFUNC) + kmap[(unsigned char)c].function = rl_insert; + +static void +_rl_bind_tty_special_chars (kmap, ttybuff) + Keymap kmap; + TIOTYPE ttybuff; +{ + if (ttybuff.flags & SGTTY_SET) + { + SET_SPECIAL (ttybuff.sgttyb.sg_erase, rl_rubout); + SET_SPECIAL (ttybuff.sgttyb.sg_kill, rl_unix_line_discard); + } + +# if defined (TIOCGLTC) + if (ttybuff.flags & LTCHARS_SET) + { + SET_SPECIAL (ttybuff.ltchars.t_werasc, rl_unix_word_rubout); + SET_SPECIAL (ttybuff.ltchars.t_lnextc, rl_quoted_insert); + } +# endif /* TIOCGLTC */ +} + +#else /* !NEW_TTY_DRIVER */ +static void +set_special_char (kmap, tiop, sc, func) + Keymap kmap; + TIOTYPE *tiop; + int sc; + rl_command_func_t *func; +{ + unsigned char uc; + + uc = tiop->c_cc[sc]; + if (uc != (unsigned char)_POSIX_VDISABLE && kmap[uc].type == ISFUNC) + kmap[uc].function = func; +} + +/* used later */ +#define RESET_SPECIAL(uc) \ + if (uc != (unsigned char)_POSIX_VDISABLE && kmap[uc].type == ISFUNC) \ + kmap[uc].function = rl_insert; + +static void +_rl_bind_tty_special_chars (kmap, ttybuff) + Keymap kmap; + TIOTYPE ttybuff; +{ + SET_SPECIAL (VERASE, rl_rubout); + SET_SPECIAL (VKILL, rl_unix_line_discard); + +# if defined (VLNEXT) && defined (TERMIOS_TTY_DRIVER) + SET_SPECIAL (VLNEXT, rl_quoted_insert); +# endif /* VLNEXT && TERMIOS_TTY_DRIVER */ + +# if defined (VWERASE) && defined (TERMIOS_TTY_DRIVER) + SET_SPECIAL (VWERASE, rl_unix_word_rubout); +# endif /* VWERASE && TERMIOS_TTY_DRIVER */ +} + +#endif /* !NEW_TTY_DRIVER */ + /* Set the system's default editing characters to their readline equivalents in KMAP. Should be static, now that we have rl_tty_set_default_bindings. */ void @@ -781,63 +868,13 @@ rltty_set_default_bindings (kmap) Keymap kmap; { TIOTYPE ttybuff; - int tty = fileno (rl_instream); + int tty; + static int called = 0; -#if defined (NEW_TTY_DRIVER) - -#define SET_SPECIAL(sc, func) \ - do \ - { \ - int ic; \ - ic = sc; \ - if (ic != -1 && kmap[(unsigned char)ic].type == ISFUNC) \ - kmap[(unsigned char)ic].function = func; \ - } \ - while (0) + tty = fileno (rl_instream); if (get_tty_settings (tty, &ttybuff) == 0) - { - if (ttybuff.flags & SGTTY_SET) - { - SET_SPECIAL (ttybuff.sgttyb.sg_erase, rl_rubout); - SET_SPECIAL (ttybuff.sgttyb.sg_kill, rl_unix_line_discard); - } - -# if defined (TIOCGLTC) - if (ttybuff.flags & LTCHARS_SET) - { - SET_SPECIAL (ttybuff.ltchars.t_werasc, rl_unix_word_rubout); - SET_SPECIAL (ttybuff.ltchars.t_lnextc, rl_quoted_insert); - } -# endif /* TIOCGLTC */ - } - -#else /* !NEW_TTY_DRIVER */ - -#define SET_SPECIAL(sc, func) \ - do \ - { \ - unsigned char uc; \ - uc = ttybuff.c_cc[sc]; \ - if (uc != (unsigned char)_POSIX_VDISABLE && kmap[uc].type == ISFUNC) \ - kmap[uc].function = func; \ - } \ - while (0) - - if (get_tty_settings (tty, &ttybuff) == 0) - { - SET_SPECIAL (VERASE, rl_rubout); - SET_SPECIAL (VKILL, rl_unix_line_discard); - -# if defined (VLNEXT) && defined (TERMIOS_TTY_DRIVER) - SET_SPECIAL (VLNEXT, rl_quoted_insert); -# endif /* VLNEXT && TERMIOS_TTY_DRIVER */ - -# if defined (VWERASE) && defined (TERMIOS_TTY_DRIVER) - SET_SPECIAL (VWERASE, rl_unix_word_rubout); -# endif /* VWERASE && TERMIOS_TTY_DRIVER */ - } -#endif /* !NEW_TTY_DRIVER */ + _rl_bind_tty_special_chars (kmap, ttybuff); } /* New public way to set the system default editing chars to their readline @@ -849,6 +886,30 @@ rl_tty_set_default_bindings (kmap) rltty_set_default_bindings (kmap); } +/* Rebind all of the tty special chars that readline worries about back + to self-insert. Call this before saving the current terminal special + chars with save_tty_chars(). This only works on POSIX termios or termio + systems. */ +void +rl_tty_unset_default_bindings (kmap) + Keymap kmap; +{ + /* Don't bother before we've saved the tty special chars at least once. */ + if (RL_ISSTATE(RL_STATE_TTYCSAVED) == 0) + return; + + RESET_SPECIAL (_rl_tty_chars.t_erase); + RESET_SPECIAL (_rl_tty_chars.t_kill); + +# if defined (VLNEXT) && defined (TERMIOS_TTY_DRIVER) + RESET_SPECIAL (_rl_tty_chars.t_lnext); +# endif /* VLNEXT && TERMIOS_TTY_DRIVER */ + +# if defined (VWERASE) && defined (TERMIOS_TTY_DRIVER) + RESET_SPECIAL (_rl_tty_chars.t_werase); +# endif /* VWERASE && TERMIOS_TTY_DRIVER */ +} + #if defined (HANDLE_SIGNALS) #if defined (NEW_TTY_DRIVER) diff --git a/cmd-line-utils/readline/rltty.h b/cmd-line-utils/readline/rltty.h index 029a3fbc0e1..142e96b6a64 100644 --- a/cmd-line-utils/readline/rltty.h +++ b/cmd-line-utils/readline/rltty.h @@ -61,22 +61,22 @@ #endif /* !NEW_TTY_DRIVER && !_POSIX_VDISABLE */ typedef struct _rl_tty_chars { - char t_eof; - char t_eol; - char t_eol2; - char t_erase; - char t_werase; - char t_kill; - char t_reprint; - char t_intr; - char t_quit; - char t_susp; - char t_dsusp; - char t_start; - char t_stop; - char t_lnext; - char t_flush; - char t_status; + unsigned char t_eof; + unsigned char t_eol; + unsigned char t_eol2; + unsigned char t_erase; + unsigned char t_werase; + unsigned char t_kill; + unsigned char t_reprint; + unsigned char t_intr; + unsigned char t_quit; + unsigned char t_susp; + unsigned char t_dsusp; + unsigned char t_start; + unsigned char t_stop; + unsigned char t_lnext; + unsigned char t_flush; + unsigned char t_status; } _RL_TTY_CHARS; #endif /* _RLTTY_H_ */ diff --git a/cmd-line-utils/readline/rltypedefs.h b/cmd-line-utils/readline/rltypedefs.h index f3280e9fce0..862bdb8e4d9 100644 --- a/cmd-line-utils/readline/rltypedefs.h +++ b/cmd-line-utils/readline/rltypedefs.h @@ -1,6 +1,6 @@ /* rltypedefs.h -- Type declarations for readline functions. */ -/* Copyright (C) 2000 Free Software Foundation, Inc. +/* Copyright (C) 2000-2004 Free Software Foundation, Inc. This file is part of the GNU Readline Library, a library for reading lines of text with interactive input and history editing. @@ -79,6 +79,12 @@ typedef void rl_voidfunc_t PARAMS((void)); typedef void rl_vintfunc_t PARAMS((int)); typedef void rl_vcpfunc_t PARAMS((char *)); typedef void rl_vcppfunc_t PARAMS((char **)); + +typedef char *rl_cpvfunc_t PARAMS((void)); +typedef char *rl_cpifunc_t PARAMS((int)); +typedef char *rl_cpcpfunc_t PARAMS((char *)); +typedef char *rl_cpcppfunc_t PARAMS((char **)); + #endif /* _RL_FUNCTION_TYPEDEF */ #ifdef __cplusplus diff --git a/cmd-line-utils/readline/savestring.c b/cmd-line-utils/readline/savestring.c new file mode 100644 index 00000000000..820428d8881 --- /dev/null +++ b/cmd-line-utils/readline/savestring.c @@ -0,0 +1,37 @@ +/* savestring.c */ + +/* Copyright (C) 1998,2003 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library, a library for + reading lines of text with interactive input and history editing. + + The GNU Readline Library 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, or + (at your option) any later version. + + The GNU Readline Library 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. + + The GNU General Public License is often shipped with GNU software, and + is generally kept in a file called COPYING or LICENSE. If you do not + have a copy of the license, write to the Free Software Foundation, + 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ +#define READLINE_LIBRARY + +#include +#ifdef HAVE_STRING_H +# include +#endif +#include "xmalloc.h" + +/* Backwards compatibility, now that savestring has been removed from + all `public' readline header files. */ +char * +savestring (s) + const char *s; +{ + return ((char *)strcpy ((char *)xmalloc (1 + strlen (s)), (s))); +} diff --git a/cmd-line-utils/readline/search.c b/cmd-line-utils/readline/search.c index 637534924f1..ce7d54eb799 100644 --- a/cmd-line-utils/readline/search.c +++ b/cmd-line-utils/readline/search.c @@ -22,7 +22,9 @@ 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#include "config_readline.h" +#if defined (HAVE_CONFIG_H) +# include +#endif #include #include @@ -80,8 +82,13 @@ static void make_history_line_current (entry) HIST_ENTRY *entry; { - rl_replace_line (entry->line, 0); +#if 0 + rl_replace_line (entry->line, 1); rl_undo_list = (UNDO_LIST *)entry->data; +#else + _rl_replace_text (entry->line, 0, rl_end); + _rl_fix_point (1); +#endif if (_rl_saved_line_for_history) _rl_free_history_entry (_rl_saved_line_for_history); @@ -187,6 +194,11 @@ noninc_search (dir, pchar) saved_point = rl_point; saved_mark = rl_mark; + /* Clear the undo list, since reading the search string should create its + own undo list, and the whole list will end up being freed when we + finish reading the search string. */ + rl_undo_list = 0; + /* Use the line buffer to read the search string. */ rl_line_buffer[0] = 0; rl_end = rl_point = 0; @@ -294,7 +306,7 @@ noninc_search (dir, pchar) code calls this, KEY will be `?'. */ int rl_noninc_forward_search (count, key) - int count __attribute__((unused)), key __attribute__((unused)); + int count, key; { noninc_search (1, (key == '?') ? '?' : 0); return 0; @@ -304,7 +316,7 @@ rl_noninc_forward_search (count, key) calls this, KEY will be `/'. */ int rl_noninc_reverse_search (count, key) - int count __attribute__((unused)), key __attribute__((unused)); + int count, key; { noninc_search (-1, (key == '/') ? '/' : 0); return 0; @@ -314,7 +326,7 @@ rl_noninc_reverse_search (count, key) for. If there is no saved search string, abort. */ int rl_noninc_forward_search_again (count, key) - int count __attribute__((unused)), key __attribute__((unused)); + int count, key; { if (!noninc_search_string) { @@ -329,7 +341,7 @@ rl_noninc_forward_search_again (count, key) for. If there is no saved search string, abort. */ int rl_noninc_reverse_search_again (count, key) - int count __attribute__((unused)), key __attribute__((unused)); + int count, key; { if (!noninc_search_string) { diff --git a/cmd-line-utils/readline/shell.c b/cmd-line-utils/readline/shell.c index fd6a2816309..a07e2b96fc1 100644 --- a/cmd-line-utils/readline/shell.c +++ b/cmd-line-utils/readline/shell.c @@ -22,7 +22,9 @@ 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#include "config_readline.h" +#if defined (HAVE_CONFIG_H) +# include +#endif #include @@ -124,6 +126,7 @@ sh_set_lines_and_columns (lines, cols) b = (char *)xmalloc (INT_STRLEN_BOUND (int) + sizeof ("LINES=") + 1); sprintf (b, "LINES=%d", lines); putenv (b); + b = (char *)xmalloc (INT_STRLEN_BOUND (int) + sizeof ("COLUMNS=") + 1); sprintf (b, "COLUMNS=%d", cols); putenv (b); @@ -132,9 +135,12 @@ sh_set_lines_and_columns (lines, cols) b = (char *)xmalloc (INT_STRLEN_BOUND (int) + 1); sprintf (b, "%d", lines); setenv ("LINES", b, 1); + free (b); + b = (char *)xmalloc (INT_STRLEN_BOUND (int) + 1); sprintf (b, "%d", cols); setenv ("COLUMNS", b, 1); + free (b); # endif /* HAVE_SETENV */ #endif /* !HAVE_PUTENV */ } diff --git a/cmd-line-utils/readline/signals.c b/cmd-line-utils/readline/signals.c index 4609598ff98..8e9c0ae60e5 100644 --- a/cmd-line-utils/readline/signals.c +++ b/cmd-line-utils/readline/signals.c @@ -21,7 +21,9 @@ 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#include "config_readline.h" +#if defined (HAVE_CONFIG_H) +# include +#endif #include /* Just for NULL. Yuck. */ #include @@ -71,6 +73,10 @@ typedef struct { SigHandler *sa_handler; int sa_mask, sa_flags; } sighandler_cxt # define sigemptyset(m) #endif /* !HAVE_POSIX_SIGNALS */ +#ifndef SA_RESTART +# define SA_RESTART 0 +#endif + static SigHandler *rl_set_sighandler PARAMS((int, SigHandler *, sighandler_cxt *)); static void rl_maybe_set_sighandler PARAMS((int, SigHandler *, sighandler_cxt *)); @@ -83,6 +89,8 @@ int rl_catch_signals = 1; /* If non-zero, readline will install a signal handler for SIGWINCH. */ #ifdef SIGWINCH int rl_catch_sigwinch = 1; +#else +int rl_catch_sigwinch = 0; /* for the readline state struct in readline.c */ #endif static int signals_set_flag; @@ -231,7 +239,7 @@ rl_set_sighandler (sig, handler, ohandler) struct sigaction act; act.sa_handler = handler; - act.sa_flags = 0; /* XXX - should we set SA_RESTART for SIGWINCH? */ + act.sa_flags = (sig == SIGWINCH) ? SA_RESTART : 0; sigemptyset (&act.sa_mask); sigemptyset (&ohandler->sa_mask); sigaction (sig, &act, &old_handler); diff --git a/cmd-line-utils/readline/terminal.c b/cmd-line-utils/readline/terminal.c index b2bcf5f146c..b95aea89b3d 100644 --- a/cmd-line-utils/readline/terminal.c +++ b/cmd-line-utils/readline/terminal.c @@ -21,7 +21,9 @@ 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#include "config_readline.h" +#if defined (HAVE_CONFIG_H) +# include +#endif #include #include "posixstat.h" @@ -82,41 +84,40 @@ static int tcap_initialized; # if defined (__EMX__) || defined (NEED_EXTERN_PC) extern # endif /* __EMX__ || NEED_EXTERN_PC */ -char PC; -char *BC, *UP; +char PC, *BC, *UP; #endif /* __linux__ */ /* Some strings to control terminal actions. These are output by tputs (). */ -const char *_rl_term_clreol; -const char *_rl_term_clrpag; -const char *_rl_term_cr; -const char *_rl_term_backspace; -const char *_rl_term_goto; -const char *_rl_term_pc; +char *_rl_term_clreol; +char *_rl_term_clrpag; +char *_rl_term_cr; +char *_rl_term_backspace; +char *_rl_term_goto; +char *_rl_term_pc; /* Non-zero if we determine that the terminal can do character insertion. */ int _rl_terminal_can_insert = 0; /* How to insert characters. */ -const char *_rl_term_im; -const char *_rl_term_ei; -const char *_rl_term_ic; -const char *_rl_term_ip; -const char *_rl_term_IC; +char *_rl_term_im; +char *_rl_term_ei; +char *_rl_term_ic; +char *_rl_term_ip; +char *_rl_term_IC; /* How to delete characters. */ -const char *_rl_term_dc; -const char *_rl_term_DC; +char *_rl_term_dc; +char *_rl_term_DC; #if defined (HACK_TERMCAP_MOTION) char *_rl_term_forward_char; #endif /* HACK_TERMCAP_MOTION */ /* How to go up a line. */ -const char *_rl_term_up; +char *_rl_term_up; /* A visible bell; char if the terminal can be made to flash the screen. */ -static const char *_rl_visible_bell; +static char *_rl_visible_bell; /* Non-zero means the terminal can auto-wrap lines. */ int _rl_term_autowrap; @@ -126,30 +127,30 @@ static int term_has_meta; /* The sequences to write to turn on and off the meta key, if this terminal has one. */ -static const char *_rl_term_mm; -static const char *_rl_term_mo; +static char *_rl_term_mm; +static char *_rl_term_mo; /* The key sequences output by the arrow keys, if this terminal has any. */ -static const char *_rl_term_ku; -static const char *_rl_term_kd; -static const char *_rl_term_kr; -static const char *_rl_term_kl; +static char *_rl_term_ku; +static char *_rl_term_kd; +static char *_rl_term_kr; +static char *_rl_term_kl; /* How to initialize and reset the arrow keys, if this terminal has any. */ -static const char *_rl_term_ks; -static const char *_rl_term_ke; +static char *_rl_term_ks; +static char *_rl_term_ke; /* The key sequences sent by the Home and End keys, if any. */ -static const char *_rl_term_kh; -static const char *_rl_term_kH; -static const char *_rl_term_at7; /* @7 */ +static char *_rl_term_kh; +static char *_rl_term_kH; +static char *_rl_term_at7; /* @7 */ /* Insert key */ -static const char *_rl_term_kI; +static char *_rl_term_kI; /* Cursor control */ -static const char *_rl_term_vs; /* very visible */ -static const char *_rl_term_ve; /* normal */ +static char *_rl_term_vs; /* very visible */ +static char *_rl_term_ve; /* normal */ static void bind_termcap_arrow_keys PARAMS((Keymap)); @@ -295,7 +296,7 @@ rl_resize_terminal () struct _tc_string { const char *tc_var; - const char **tc_value; + char **tc_value; }; /* This should be kept sorted, just in case we decide to change the @@ -343,14 +344,10 @@ get_term_capabilities (bp) char **bp; { #if !defined (__DJGPP__) /* XXX - doesn't DJGPP have a termcap library? */ - register unsigned int i; + register int i; for (i = 0; i < NUM_TC_STRINGS; i++) -# if defined(__LCC__) || defined(__MWERKS__) *(tc_strings[i].tc_value) = tgetstr ((char *)tc_strings[i].tc_var, bp); -# else - *(tc_strings[i].tc_value) = tgetstr (tc_strings[i].tc_var, bp); -# endif #endif tcap_initialized = 1; } @@ -432,8 +429,8 @@ _rl_init_terminal_io (terminal_name) tgoto if _rl_term_IC or _rl_term_DC is defined, but just in case we change that later... */ PC = '\0'; - BC = (char*)(_rl_term_backspace = "\b"); - UP = (char*)_rl_term_up; + BC = _rl_term_backspace = "\b"; + UP = _rl_term_up; return 0; } @@ -443,8 +440,8 @@ _rl_init_terminal_io (terminal_name) /* Set up the variables that the termcap library expects the application to provide. */ PC = _rl_term_pc ? *_rl_term_pc : 0; - BC = (char*)_rl_term_backspace; - UP = (char*)_rl_term_up; + BC = _rl_term_backspace; + UP = _rl_term_up; if (!_rl_term_cr) _rl_term_cr = "\r"; @@ -488,22 +485,22 @@ bind_termcap_arrow_keys (map) xkeymap = _rl_keymap; _rl_keymap = map; - _rl_bind_if_unbound (_rl_term_ku, rl_get_previous_history); - _rl_bind_if_unbound (_rl_term_kd, rl_get_next_history); - _rl_bind_if_unbound (_rl_term_kr, rl_forward); - _rl_bind_if_unbound (_rl_term_kl, rl_backward); + rl_bind_keyseq_if_unbound (_rl_term_ku, rl_get_previous_history); + rl_bind_keyseq_if_unbound (_rl_term_kd, rl_get_next_history); + rl_bind_keyseq_if_unbound (_rl_term_kr, rl_forward_char); + rl_bind_keyseq_if_unbound (_rl_term_kl, rl_backward_char); - _rl_bind_if_unbound (_rl_term_kh, rl_beg_of_line); /* Home */ - _rl_bind_if_unbound (_rl_term_at7, rl_end_of_line); /* End */ + rl_bind_keyseq_if_unbound (_rl_term_kh, rl_beg_of_line); /* Home */ + rl_bind_keyseq_if_unbound (_rl_term_at7, rl_end_of_line); /* End */ _rl_keymap = xkeymap; } -const char * +char * rl_get_termcap (cap) const char *cap; { - register unsigned int i; + register int i; if (tcap_initialized == 0) return ((char *)NULL); diff --git a/cmd-line-utils/readline/text.c b/cmd-line-utils/readline/text.c index d98b266edfe..d917e99b9bd 100644 --- a/cmd-line-utils/readline/text.c +++ b/cmd-line-utils/readline/text.c @@ -1,6 +1,6 @@ /* text.c -- text handling commands for readline. */ -/* Copyright (C) 1987-2002 Free Software Foundation, Inc. +/* Copyright (C) 1987-2004 Free Software Foundation, Inc. This file is part of the GNU Readline Library, a library for reading lines of text with interactive input and history editing. @@ -21,7 +21,9 @@ 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#include "config_readline.h" +#if defined (HAVE_CONFIG_H) +# include +#endif #if defined (HAVE_UNISTD_H) # include @@ -168,6 +170,9 @@ _rl_fix_point (fix_mark_too) } #undef _RL_FIX_POINT +/* Replace the contents of the line buffer between START and END with + TEXT. The operation is undoable. To replace the entire line in an + undoable mode, use _rl_replace_text(text, 0, rl_end); */ int _rl_replace_text (text, start, end) const char *text; @@ -400,7 +405,7 @@ rl_backward (count, key) /* Move to the beginning of the line. */ int rl_beg_of_line (count, key) - int count __attribute__((unused)), key __attribute__((unused)); + int count, key; { rl_point = 0; return 0; @@ -409,7 +414,7 @@ rl_beg_of_line (count, key) /* Move to the end of the line. */ int rl_end_of_line (count, key) - int count __attribute__((unused)), key __attribute__((unused)); + int count, key; { rl_point = rl_end; return 0; @@ -506,7 +511,7 @@ rl_backward_word (count, key) /* Clear the current line. Numeric argument to C-l does this. */ int rl_refresh_line (ignore1, ignore2) - int ignore1 __attribute__((unused)), ignore2 __attribute__((unused)); + int ignore1, ignore2; { int curr_line; @@ -545,7 +550,7 @@ rl_clear_screen (count, key) int rl_arrow_keys (count, c) - int count, c __attribute__((unused)); + int count, c; { int ch; @@ -799,13 +804,10 @@ _rl_overwrite_char (count, c) k = _rl_read_mbstring (c, mbkey, MB_LEN_MAX); #endif + rl_begin_undo_group (); + for (i = 0; i < count; i++) { - rl_begin_undo_group (); - - if (rl_point < rl_end) - rl_delete (1, c); - #if defined (HANDLE_MULTIBYTE) if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) rl_insert_text (mbkey); @@ -813,9 +815,12 @@ _rl_overwrite_char (count, c) #endif _rl_insert_char (1, c); - rl_end_undo_group (); + if (rl_point < rl_end) + rl_delete (1, c); } + rl_end_undo_group (); + return 0; } @@ -830,7 +835,7 @@ rl_insert (count, c) /* Insert the next typed character verbatim. */ int rl_quoted_insert (count, key) - int count, key __attribute__((unused)); + int count, key; { int c; @@ -852,7 +857,7 @@ rl_quoted_insert (count, key) /* Insert a tab character. */ int rl_tab_insert (count, key) - int count, key __attribute__((unused)); + int count, key; { return (_rl_insert_char (count, '\t')); } @@ -862,7 +867,7 @@ rl_tab_insert (count, key) meaning in the future. */ int rl_newline (count, key) - int count __attribute__((unused)), key __attribute__((unused)); + int count, key; { rl_done = 1; @@ -875,7 +880,8 @@ rl_newline (count, key) if (rl_editing_mode == vi_mode) { _rl_vi_done_inserting (); - _rl_vi_reset_last (); + if (_rl_vi_textmod_command (_rl_vi_last_command) == 0) /* XXX */ + _rl_vi_reset_last (); } #endif /* VI_MODE */ @@ -895,7 +901,7 @@ rl_newline (count, key) is special cased. */ int rl_do_lowercase_version (ignore1, ignore2) - int ignore1 __attribute__((unused)), ignore2 __attribute__((unused)); + int ignore1, ignore2; { return 0; } @@ -933,9 +939,12 @@ _rl_overwrite_rubout (count, key) rl_delete_text (opoint, rl_point); /* Emacs puts point at the beginning of the sequence of spaces. */ - opoint = rl_point; - _rl_insert_char (l, ' '); - rl_point = opoint; + if (rl_point < rl_end) + { + opoint = rl_point; + _rl_insert_char (l, ' '); + rl_point = opoint; + } rl_end_undo_group (); @@ -1087,7 +1096,7 @@ rl_rubout_or_delete (count, key) /* Delete all spaces and tabs around point. */ int rl_delete_horizontal_space (count, ignore) - int count __attribute__((unused)), ignore __attribute__((unused)); + int count, ignore; { int start = rl_point; @@ -1128,9 +1137,9 @@ rl_delete_or_show_completions (count, key) A K*rn shell style function. */ int rl_insert_comment (count, key) - int count __attribute__((unused)), key; + int count, key; { - const char *rl_comment_text; + char *rl_comment_text; int rl_comment_len; rl_beg_of_line (1, key); @@ -1167,7 +1176,7 @@ rl_insert_comment (count, key) /* Uppercase the word at point. */ int rl_upcase_word (count, key) - int count, key __attribute__((unused)); + int count, key; { return (rl_change_case (count, UpCase)); } @@ -1175,7 +1184,7 @@ rl_upcase_word (count, key) /* Lowercase the word at point. */ int rl_downcase_word (count, key) - int count, key __attribute__((unused)); + int count, key; { return (rl_change_case (count, DownCase)); } @@ -1183,7 +1192,7 @@ rl_downcase_word (count, key) /* Upcase the first letter, downcase the rest. */ int rl_capitalize_word (count, key) - int count, key __attribute__((unused)); + int count, key; { return (rl_change_case (count, CapCase)); } @@ -1308,7 +1317,7 @@ rl_transpose_words (count, key) then transpose the characters before point. */ int rl_transpose_chars (count, key) - int count, key __attribute__((unused)); + int count, key; { #if defined (HANDLE_MULTIBYTE) char *dummy; @@ -1480,14 +1489,14 @@ _rl_char_search (count, fdir, bdir) int rl_char_search (count, key) - int count, key __attribute__((unused)); + int count, key; { return (_rl_char_search (count, FFIND, BFIND)); } int rl_backward_char_search (count, key) - int count, key __attribute__((unused)); + int count, key; { return (_rl_char_search (count, BFIND, FFIND)); } @@ -1513,7 +1522,7 @@ _rl_set_mark_at_pos (position) /* A bindable command to set the mark. */ int rl_set_mark (count, key) - int count, key __attribute__((unused)); + int count, key; { return (_rl_set_mark_at_pos (rl_explicit_arg ? count : rl_point)); } @@ -1521,7 +1530,7 @@ rl_set_mark (count, key) /* Exchange the position of mark and point. */ int rl_exchange_point_and_mark (count, key) - int count __attribute__((unused)), key __attribute__((unused)); + int count, key; { if (rl_mark > rl_end) rl_mark = -1; diff --git a/cmd-line-utils/readline/tilde.c b/cmd-line-utils/readline/tilde.c index 456a6bcb357..07fa374e1d1 100644 --- a/cmd-line-utils/readline/tilde.c +++ b/cmd-line-utils/readline/tilde.c @@ -19,7 +19,11 @@ along with Readline; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ -#include "config_readline.h" +#define READLINE_LIBRARY + +#if defined (HAVE_CONFIG_H) +# include +#endif #if defined (HAVE_UNISTD_H) # ifdef _MINIX @@ -188,7 +192,7 @@ tilde_expand (string) int result_size, result_index; result_index = result_size = 0; - if ((result = strchr(string, '~'))) + if (result = strchr (string, '~')) result = (char *)xmalloc (result_size = (strlen (string) + 16)); else result = (char *)xmalloc (result_size = (strlen (string) + 1)); diff --git a/cmd-line-utils/readline/undo.c b/cmd-line-utils/readline/undo.c index 947da3d00d0..25c287b5a2d 100644 --- a/cmd-line-utils/readline/undo.c +++ b/cmd-line-utils/readline/undo.c @@ -22,7 +22,9 @@ 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#include "config_readline.h" +#if defined (HAVE_CONFIG_H) +# include +#endif #include @@ -169,8 +171,7 @@ rl_do_undo () int _rl_fix_last_undo_of_type (type, start, end) - unsigned int type; - int start, end; + int type, start, end; { UNDO_LIST *rl; @@ -228,7 +229,7 @@ rl_modifying (start, end) /* Revert the current line to its previous state. */ int rl_revert_line (count, key) - int count __attribute__((unused)), key __attribute__((unused)); + int count, key; { if (!rl_undo_list) rl_ding (); @@ -243,7 +244,7 @@ rl_revert_line (count, key) /* Do some undoing of things that were done. */ int rl_undo_command (count, key) - int count, key __attribute__((unused)); + int count, key; { if (count < 0) return 0; /* Nothing to do. */ diff --git a/cmd-line-utils/readline/util.c b/cmd-line-utils/readline/util.c index 403b3d544d9..a632d81129c 100644 --- a/cmd-line-utils/readline/util.c +++ b/cmd-line-utils/readline/util.c @@ -21,7 +21,9 @@ 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#include "config_readline.h" +#if defined (HAVE_CONFIG_H) +# include +#endif #include #include @@ -96,14 +98,14 @@ _rl_abort_internal () int rl_abort (count, key) - int count __attribute__((unused)), key __attribute__((unused)); + int count, key; { return (_rl_abort_internal ()); } int rl_tty_status (count, key) - int count __attribute__((unused)), key __attribute__((unused)); + int count, key; { #if defined (TIOCSTAT) ioctl (1, TIOCSTAT, (char *)0); @@ -153,7 +155,7 @@ rl_extend_line_buffer (len) /* A function for simple tilde expansion. */ int rl_tilde_expand (ignore, key) - int ignore __attribute__((unused)), key __attribute__((unused)); + int ignore, key; { register int start, end; char *homedir, *temp; @@ -248,7 +250,7 @@ _rl_strpbrk (string1, string2) { v = _rl_get_char_len (string1, &ps); if (v > 1) - string += v - 1; /* -1 to account for auto-increment in loop */ + string1 += v - 1; /* -1 to account for auto-increment in loop */ } #endif } diff --git a/cmd-line-utils/readline/vi_mode.c b/cmd-line-utils/readline/vi_mode.c index e8ad05d866f..74d8acbbc05 100644 --- a/cmd-line-utils/readline/vi_mode.c +++ b/cmd-line-utils/readline/vi_mode.c @@ -1,7 +1,7 @@ /* vi_mode.c -- A vi emulation mode for Bash. Derived from code written by Jeff Sparkes (jsparkes@bnr.ca). */ -/* Copyright (C) 1987, 1989, 1992 Free Software Foundation, Inc. +/* Copyright (C) 1987-2004 Free Software Foundation, Inc. This file is part of the GNU Readline Library, a library for reading lines of text with interactive input and history editing. @@ -31,7 +31,9 @@ #if defined (VI_MODE) -#include "config_readline.h" +#if defined (HAVE_CONFIG_H) +# include +#endif #include @@ -61,6 +63,8 @@ #define member(c, s) ((c) ? (char *)strchr ((s), (c)) != (char *)NULL : 0) #endif +int _rl_vi_last_command = 'i'; /* default `.' puts you in insert mode */ + /* Non-zero means enter insertion mode. */ static int _rl_vi_doing_insert; @@ -81,7 +85,6 @@ static int vi_continued_command; static char *vi_insert_buffer; static int vi_insert_buffer_size; -static int _rl_vi_last_command = 'i'; /* default `.' puts you in insert mode */ static int _rl_vi_last_repeat = 1; static int _rl_vi_last_arg_sign = 1; static int _rl_vi_last_motion; @@ -109,7 +112,7 @@ static int rl_digit_loop1 PARAMS((void)); void _rl_vi_initialize_line () { - register unsigned int i; + register int i; for (i = 0; i < sizeof (vi_mark_chars) / sizeof (int); i++) vi_mark_chars[i] = -1; @@ -133,6 +136,16 @@ _rl_vi_set_last (key, repeat, sign) _rl_vi_last_arg_sign = sign; } +/* A convenience function that calls _rl_vi_set_last to save the last command + information and enters insertion mode. */ +void +rl_vi_start_inserting (key, repeat, sign) + int key, repeat, sign; +{ + _rl_vi_set_last (key, repeat, sign); + rl_vi_insertion_mode (1, key); +} + /* Is the command C a VI mode text modification command? */ int _rl_vi_textmod_command (c) @@ -156,7 +169,7 @@ _rl_vi_stuff_insert (count) puts you back into insert mode. */ int rl_vi_redo (count, c) - int count, c __attribute__((unused)); + int count, c; { int r; @@ -195,7 +208,7 @@ rl_vi_undo (count, key) /* Yank the nth arg from the previous line into this line at point. */ int rl_vi_yank_arg (count, key) - int count, key __attribute__((unused)); + int count, key; { /* Readline thinks that the first word on a line is the 0th, while vi thinks the first word on a line is the 1st. Compensate. */ @@ -276,7 +289,7 @@ rl_vi_search (count, key) /* Completion, from vi's point of view. */ int rl_vi_complete (ignore, key) - int ignore __attribute__((unused)), key; + int ignore, key; { if ((rl_point < rl_end) && (!whitespace (rl_line_buffer[rl_point]))) { @@ -295,21 +308,18 @@ rl_vi_complete (ignore, key) rl_complete (0, key); if (key == '*' || key == '\\') - { - _rl_vi_set_last (key, 1, rl_arg_sign); - rl_vi_insertion_mode (1, key); - } + rl_vi_start_inserting (key, 1, rl_arg_sign); + return (0); } /* Tilde expansion for vi mode. */ int rl_vi_tilde_expand (ignore, key) - int ignore __attribute__((unused)), key; + int ignore, key; { rl_tilde_expand (0, key); - _rl_vi_set_last (key, 1, rl_arg_sign); /* XXX */ - rl_vi_insertion_mode (1, key); + rl_vi_start_inserting (key, 1, rl_arg_sign); return (0); } @@ -377,7 +387,7 @@ rl_vi_end_word (count, key) /* Move forward a word the way that 'W' does. */ int rl_vi_fWord (count, ignore) - int count, ignore __attribute__((unused)); + int count, ignore; { while (count-- && rl_point < (rl_end - 1)) { @@ -394,7 +404,7 @@ rl_vi_fWord (count, ignore) int rl_vi_bWord (count, ignore) - int count, ignore __attribute__((unused)); + int count, ignore; { while (count-- && rl_point > 0) { @@ -418,7 +428,7 @@ rl_vi_bWord (count, ignore) int rl_vi_eWord (count, ignore) - int count, ignore __attribute__((unused)); + int count, ignore; { while (count-- && rl_point < (rl_end - 1)) { @@ -427,7 +437,8 @@ rl_vi_eWord (count, ignore) /* Move to the next non-whitespace character (to the start of the next word). */ - while (++rl_point < rl_end && whitespace (rl_line_buffer[rl_point])); + while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point])) + rl_point++; if (rl_point && rl_point < rl_end) { @@ -448,7 +459,7 @@ rl_vi_eWord (count, ignore) int rl_vi_fword (count, ignore) - int count, ignore __attribute__((unused)); + int count, ignore; { while (count-- && rl_point < (rl_end - 1)) { @@ -474,7 +485,7 @@ rl_vi_fword (count, ignore) int rl_vi_bword (count, ignore) - int count, ignore __attribute__((unused)); + int count, ignore; { while (count-- && rl_point > 0) { @@ -513,7 +524,7 @@ rl_vi_bword (count, ignore) int rl_vi_eword (count, ignore) - int count, ignore __attribute__((unused)); + int count, ignore; { while (count-- && rl_point < rl_end - 1) { @@ -538,7 +549,7 @@ rl_vi_eword (count, ignore) int rl_vi_insert_beg (count, key) - int count __attribute__((unused)), key; + int count, key; { rl_beg_of_line (1, key); rl_vi_insertion_mode (1, key); @@ -547,7 +558,7 @@ rl_vi_insert_beg (count, key) int rl_vi_append_mode (count, key) - int count __attribute__((unused)), key; + int count, key; { if (rl_point < rl_end) { @@ -567,7 +578,7 @@ rl_vi_append_mode (count, key) int rl_vi_append_eol (count, key) - int count __attribute__((unused)), key; + int count, key; { rl_end_of_line (1, key); rl_vi_append_mode (1, key); @@ -577,7 +588,7 @@ rl_vi_append_eol (count, key) /* What to do in the case of C-d. */ int rl_vi_eof_maybe (count, c) - int count __attribute__((unused)), c __attribute__((unused)); + int count, c; { return (rl_newline (1, '\n')); } @@ -588,7 +599,7 @@ rl_vi_eof_maybe (count, c) switching keymaps. */ int rl_vi_insertion_mode (count, key) - int count __attribute__((unused)), key; + int count, key; { _rl_keymap = vi_insertion_keymap; _rl_vi_last_key_before_insert = key; @@ -638,7 +649,7 @@ _rl_vi_done_inserting () } else { - if (_rl_vi_last_key_before_insert == 'i' && rl_undo_list) + if ((_rl_vi_last_key_before_insert == 'i' || _rl_vi_last_key_before_insert == 'a') && rl_undo_list) _rl_vi_save_insert (rl_undo_list); /* XXX - Other keys probably need to be checked. */ else if (_rl_vi_last_key_before_insert == 'C') @@ -651,7 +662,7 @@ _rl_vi_done_inserting () int rl_vi_movement_mode (count, key) - int count __attribute__((unused)), key; + int count, key; { if (rl_point > 0) rl_backward_char (1, key); @@ -678,7 +689,8 @@ _rl_vi_change_mbchar_case (count) int count; { wchar_t wc; - char mb[MB_LEN_MAX]; + char mb[MB_LEN_MAX+1]; + int mblen; mbstate_t ps; memset (&ps, 0, sizeof (mbstate_t)); @@ -701,7 +713,9 @@ _rl_vi_change_mbchar_case (count) /* Vi is kind of strange here. */ if (wc) { - wctomb (mb, wc); + mblen = wcrtomb (mb, wc, &ps); + if (mblen >= 0) + mb[mblen] = '\0'; rl_begin_undo_group (); rl_delete (1, 0); rl_insert_text (mb); @@ -718,14 +732,15 @@ _rl_vi_change_mbchar_case (count) int rl_vi_change_case (count, ignore) - int count, ignore __attribute__((unused)); + int count, ignore; { - char c = 0; + int c, p; /* Don't try this on an empty line. */ if (rl_point >= rl_end) return (0); + c = 0; #if defined (HANDLE_MULTIBYTE) if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) return (_rl_vi_change_mbchar_case (count)); @@ -747,8 +762,11 @@ rl_vi_change_case (count, ignore) /* Vi is kind of strange here. */ if (c) { + p = rl_point; rl_begin_undo_group (); - rl_delete (1, c); + rl_vi_delete (1, c); + if (rl_point < p) /* Did we retreat at EOL? */ + rl_point++; _rl_insert_char (1, c); rl_end_undo_group (); rl_vi_check (); @@ -761,12 +779,14 @@ rl_vi_change_case (count, ignore) int rl_vi_put (count, key) - int count __attribute__((unused)), key; + int count, key; { if (!_rl_uppercase_p (key) && (rl_point + 1 <= rl_end)) rl_point = _rl_find_next_mbchar (rl_line_buffer, rl_point, 1, MB_FIND_NONZERO); - rl_yank (1, key); + while (count--) + rl_yank (1, key); + rl_backward_char (1, key); return (0); } @@ -814,6 +834,7 @@ rl_vi_domove (key, nextkey) { save = rl_numeric_arg; rl_numeric_arg = _rl_digit_value (c); + rl_explicit_arg = 1; rl_digit_loop1 (); rl_numeric_arg *= save; RL_SETSTATE(RL_STATE_MOREINPUT); @@ -941,7 +962,7 @@ rl_digit_loop1 () int rl_vi_delete_to (count, key) - int count __attribute__((unused)), key; + int count, key; { int c; @@ -1012,8 +1033,7 @@ rl_vi_change_to (count, key) /* `C' does not save the text inserted for undoing or redoing. */ if (_rl_uppercase_p (key) == 0) _rl_vi_doing_insert = 1; - _rl_vi_set_last (key, count, rl_arg_sign); - rl_vi_insertion_mode (1, key); + rl_vi_start_inserting (key, rl_numeric_arg, rl_arg_sign); } return (0); @@ -1021,7 +1041,7 @@ rl_vi_change_to (count, key) int rl_vi_yank_to (count, key) - int count __attribute__((unused)), key; + int count, key; { int c, save = rl_point; @@ -1077,7 +1097,7 @@ rl_vi_delete (count, key) int rl_vi_back_to_indent (count, key) - int count __attribute__((unused)), key; + int count, key; { rl_beg_of_line (1, key); while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point])) @@ -1087,7 +1107,7 @@ rl_vi_back_to_indent (count, key) int rl_vi_first_print (count, key) - int count __attribute__((unused)), key; + int count, key; { return (rl_vi_back_to_indent (1, key)); } @@ -1156,7 +1176,7 @@ rl_vi_char_search (count, key) /* Match brackets */ int rl_vi_match (ignore, key) - int ignore __attribute__((unused)), key; + int ignore, key; { int count = 1, brack, pos, tmp, pre; @@ -1262,14 +1282,14 @@ rl_vi_bracktype (c) /* XXX - think about reading an entire mbchar with _rl_read_mbchar and inserting it in one bunch instead of the loop below (like in - rl_vi_char_search or _rl_vi_change_mbchar_case. Set c to mbchar[0] + rl_vi_char_search or _rl_vi_change_mbchar_case). Set c to mbchar[0] for test against 033 or ^C. Make sure that _rl_read_mbchar does this right. */ int rl_vi_change_char (count, key) - int count, key __attribute__((unused)); + int count, key; { - int c; + int c, p; if (vi_redoing) c = _rl_vi_last_replacement; @@ -1283,11 +1303,11 @@ rl_vi_change_char (count, key) if (c == '\033' || c == CTRL ('C')) return -1; + rl_begin_undo_group (); while (count-- && rl_point < rl_end) { - rl_begin_undo_group (); - - rl_delete (1, c); + p = rl_point; + rl_vi_delete (1, c); #if defined (HANDLE_MULTIBYTE) if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) while (_rl_insert_char (1, c)) @@ -1298,12 +1318,14 @@ rl_vi_change_char (count, key) } else #endif - _rl_insert_char (1, c); - if (count == 0) - rl_backward_char (1, c); - - rl_end_undo_group (); + { + if (rl_point < p) /* Did we retreat at EOL? */ + rl_point++; + _rl_insert_char (1, c); + } } + rl_end_undo_group (); + return (0); } @@ -1313,7 +1335,7 @@ rl_vi_subst (count, key) { /* If we are redoing, rl_vi_change_to will stuff the last motion char */ if (vi_redoing == 0) - rl_stuff_char ((key == 'S') ? 'c' : ' '); /* `S' == `cc', `s' == `c ' */ + rl_stuff_char ((key == 'S') ? 'c' : 'l'); /* `S' == `cc', `s' == `cl' */ return (rl_vi_change_to (count, 'c')); } @@ -1370,7 +1392,7 @@ rl_vi_overstrike_delete (count, key) int rl_vi_replace (count, key) - int count __attribute__((unused)), key __attribute__((unused)); + int count, key; { int i; @@ -1431,7 +1453,7 @@ rl_vi_possible_completions() /* Functions to save and restore marks. */ int rl_vi_set_mark (count, key) - int count __attribute__((unused)), key __attribute__((unused)); + int count, key; { int ch; @@ -1451,7 +1473,7 @@ rl_vi_set_mark (count, key) int rl_vi_goto_mark (count, key) - int count __attribute__((unused)), key __attribute__((unused)); + int count, key; { int ch; diff --git a/cmd-line-utils/readline/xmalloc.c b/cmd-line-utils/readline/xmalloc.c index 698807addf9..8985d340d39 100644 --- a/cmd-line-utils/readline/xmalloc.c +++ b/cmd-line-utils/readline/xmalloc.c @@ -20,7 +20,9 @@ Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#include "config_readline.h" +#if defined (HAVE_CONFIG_H) +#include +#endif #include From 7d7a4626fc627d086391c2e817ae2d479c359301 Mon Sep 17 00:00:00 2001 From: "jimw@mysql.com" <> Date: Mon, 18 Jul 2005 18:00:19 -0700 Subject: [PATCH 39/48] Reimplement use of "config_readline.h" to get readline configuration. --- cmd-line-utils/readline/bind.c | 4 +--- cmd-line-utils/readline/callback.c | 4 +--- cmd-line-utils/readline/compat.c | 4 +--- cmd-line-utils/readline/complete.c | 4 +--- cmd-line-utils/readline/display.c | 4 +--- cmd-line-utils/readline/funmap.c | 4 +--- cmd-line-utils/readline/histexpand.c | 4 +--- cmd-line-utils/readline/histfile.c | 4 +--- cmd-line-utils/readline/history.c | 4 +--- cmd-line-utils/readline/histsearch.c | 4 +--- cmd-line-utils/readline/input.c | 4 +--- cmd-line-utils/readline/isearch.c | 4 +--- cmd-line-utils/readline/keymaps.c | 4 +--- cmd-line-utils/readline/kill.c | 4 +--- cmd-line-utils/readline/macro.c | 4 +--- cmd-line-utils/readline/mbutil.c | 4 +--- cmd-line-utils/readline/misc.c | 4 +--- cmd-line-utils/readline/nls.c | 4 +--- cmd-line-utils/readline/parens.c | 4 +--- cmd-line-utils/readline/readline.c | 4 +--- cmd-line-utils/readline/rltty.c | 4 +--- cmd-line-utils/readline/savestring.c | 3 ++- cmd-line-utils/readline/search.c | 4 +--- cmd-line-utils/readline/shell.c | 4 +--- cmd-line-utils/readline/signals.c | 4 +--- cmd-line-utils/readline/terminal.c | 4 +--- cmd-line-utils/readline/text.c | 4 +--- cmd-line-utils/readline/tilde.c | 4 +--- cmd-line-utils/readline/undo.c | 4 +--- cmd-line-utils/readline/util.c | 4 +--- cmd-line-utils/readline/vi_mode.c | 4 +--- cmd-line-utils/readline/xmalloc.c | 4 +--- 32 files changed, 33 insertions(+), 94 deletions(-) diff --git a/cmd-line-utils/readline/bind.c b/cmd-line-utils/readline/bind.c index 15d0e4e3a65..17f61d1df08 100644 --- a/cmd-line-utils/readline/bind.c +++ b/cmd-line-utils/readline/bind.c @@ -26,9 +26,7 @@ # include #endif -#if defined (HAVE_CONFIG_H) -# include -#endif +#include "config_readline.h" #include #include diff --git a/cmd-line-utils/readline/callback.c b/cmd-line-utils/readline/callback.c index 5b05ea5c5b7..0807f137b92 100644 --- a/cmd-line-utils/readline/callback.c +++ b/cmd-line-utils/readline/callback.c @@ -21,9 +21,7 @@ 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#if defined (HAVE_CONFIG_H) -# include -#endif +#include "config_readline.h" #include "rlconf.h" diff --git a/cmd-line-utils/readline/compat.c b/cmd-line-utils/readline/compat.c index a66d210fd2e..e4fbc322cee 100644 --- a/cmd-line-utils/readline/compat.c +++ b/cmd-line-utils/readline/compat.c @@ -21,9 +21,7 @@ 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#if defined (HAVE_CONFIG_H) -# include -#endif +#include "config_readline.h" #include diff --git a/cmd-line-utils/readline/complete.c b/cmd-line-utils/readline/complete.c index d212f61840e..2196c66a54c 100644 --- a/cmd-line-utils/readline/complete.c +++ b/cmd-line-utils/readline/complete.c @@ -21,9 +21,7 @@ 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#if defined (HAVE_CONFIG_H) -# include -#endif +#include "config_readline.h" #include #include diff --git a/cmd-line-utils/readline/display.c b/cmd-line-utils/readline/display.c index 0ff428e44e9..cab76c0da81 100644 --- a/cmd-line-utils/readline/display.c +++ b/cmd-line-utils/readline/display.c @@ -21,9 +21,7 @@ 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#if defined (HAVE_CONFIG_H) -# include -#endif +#include "config_readline.h" #include diff --git a/cmd-line-utils/readline/funmap.c b/cmd-line-utils/readline/funmap.c index 28c8cc67c9f..d56ffb9fadc 100644 --- a/cmd-line-utils/readline/funmap.c +++ b/cmd-line-utils/readline/funmap.c @@ -21,9 +21,7 @@ 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#if defined (HAVE_CONFIG_H) -# include -#endif +#include "config_readline.h" #if !defined (BUFSIZ) #include diff --git a/cmd-line-utils/readline/histexpand.c b/cmd-line-utils/readline/histexpand.c index 2ab34cba269..47f97e9a6f7 100644 --- a/cmd-line-utils/readline/histexpand.c +++ b/cmd-line-utils/readline/histexpand.c @@ -22,9 +22,7 @@ #define READLINE_LIBRARY -#if defined (HAVE_CONFIG_H) -# include -#endif +#include "config_readline.h" #include diff --git a/cmd-line-utils/readline/histfile.c b/cmd-line-utils/readline/histfile.c index 717bbee6fd7..7d340b346d4 100644 --- a/cmd-line-utils/readline/histfile.c +++ b/cmd-line-utils/readline/histfile.c @@ -30,9 +30,7 @@ # include #endif -#if defined (HAVE_CONFIG_H) -# include -#endif +#include "config_readline.h" #include diff --git a/cmd-line-utils/readline/history.c b/cmd-line-utils/readline/history.c index d99b76e8744..bb1960d8d99 100644 --- a/cmd-line-utils/readline/history.c +++ b/cmd-line-utils/readline/history.c @@ -25,9 +25,7 @@ you can call. I think I have done that. */ #define READLINE_LIBRARY -#if defined (HAVE_CONFIG_H) -# include -#endif +#include "config_readline.h" #include diff --git a/cmd-line-utils/readline/histsearch.c b/cmd-line-utils/readline/histsearch.c index 1cc5875a4b4..778b323afdc 100644 --- a/cmd-line-utils/readline/histsearch.c +++ b/cmd-line-utils/readline/histsearch.c @@ -22,9 +22,7 @@ #define READLINE_LIBRARY -#if defined (HAVE_CONFIG_H) -# include -#endif +#include "config_readline.h" #include #if defined (HAVE_STDLIB_H) diff --git a/cmd-line-utils/readline/input.c b/cmd-line-utils/readline/input.c index 044338e879b..1981061eac6 100644 --- a/cmd-line-utils/readline/input.c +++ b/cmd-line-utils/readline/input.c @@ -25,9 +25,7 @@ # include #endif -#if defined (HAVE_CONFIG_H) -# include -#endif +#include "config_readline.h" #include #include diff --git a/cmd-line-utils/readline/isearch.c b/cmd-line-utils/readline/isearch.c index c1ea5b30852..f7b0f1404e9 100644 --- a/cmd-line-utils/readline/isearch.c +++ b/cmd-line-utils/readline/isearch.c @@ -26,9 +26,7 @@ 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#if defined (HAVE_CONFIG_H) -# include -#endif +#include "config_readline.h" #include diff --git a/cmd-line-utils/readline/keymaps.c b/cmd-line-utils/readline/keymaps.c index 70d0cc08d3f..2be03f7086f 100644 --- a/cmd-line-utils/readline/keymaps.c +++ b/cmd-line-utils/readline/keymaps.c @@ -20,9 +20,7 @@ Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#if defined (HAVE_CONFIG_H) -# include -#endif +#include "config_readline.h" #if defined (HAVE_STDLIB_H) # include diff --git a/cmd-line-utils/readline/kill.c b/cmd-line-utils/readline/kill.c index 1d3254c3275..061bdafcf9a 100644 --- a/cmd-line-utils/readline/kill.c +++ b/cmd-line-utils/readline/kill.c @@ -21,9 +21,7 @@ 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#if defined (HAVE_CONFIG_H) -# include -#endif +#include "config_readline.h" #include diff --git a/cmd-line-utils/readline/macro.c b/cmd-line-utils/readline/macro.c index b73c3af9aa4..f7b77a831b8 100644 --- a/cmd-line-utils/readline/macro.c +++ b/cmd-line-utils/readline/macro.c @@ -21,9 +21,7 @@ 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#if defined (HAVE_CONFIG_H) -# include -#endif +#include "config_readline.h" #include diff --git a/cmd-line-utils/readline/mbutil.c b/cmd-line-utils/readline/mbutil.c index 9a8f17c0f74..c88e9485f39 100644 --- a/cmd-line-utils/readline/mbutil.c +++ b/cmd-line-utils/readline/mbutil.c @@ -21,9 +21,7 @@ 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#if defined (HAVE_CONFIG_H) -# include -#endif +#include "config_readline.h" #include #include diff --git a/cmd-line-utils/readline/misc.c b/cmd-line-utils/readline/misc.c index ab1e1337fd3..810b940edab 100644 --- a/cmd-line-utils/readline/misc.c +++ b/cmd-line-utils/readline/misc.c @@ -21,9 +21,7 @@ 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#if defined (HAVE_CONFIG_H) -# include -#endif +#include "config_readline.h" #if defined (HAVE_UNISTD_H) # include diff --git a/cmd-line-utils/readline/nls.c b/cmd-line-utils/readline/nls.c index bcee87561aa..4f28152f316 100644 --- a/cmd-line-utils/readline/nls.c +++ b/cmd-line-utils/readline/nls.c @@ -21,9 +21,7 @@ 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#if defined (HAVE_CONFIG_H) -# include -#endif +#include "config_readline.h" #include diff --git a/cmd-line-utils/readline/parens.c b/cmd-line-utils/readline/parens.c index 737f7675e93..bb893ac1bfb 100644 --- a/cmd-line-utils/readline/parens.c +++ b/cmd-line-utils/readline/parens.c @@ -27,9 +27,7 @@ #include "rlconf.h" -#if defined (HAVE_CONFIG_H) -# include -#endif +#include "config_readline.h" #include #include diff --git a/cmd-line-utils/readline/readline.c b/cmd-line-utils/readline/readline.c index a70d80ff492..e82db84c9dc 100644 --- a/cmd-line-utils/readline/readline.c +++ b/cmd-line-utils/readline/readline.c @@ -22,9 +22,7 @@ 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#if defined (HAVE_CONFIG_H) -# include -#endif +#include "config_readline.h" #include #include "posixstat.h" diff --git a/cmd-line-utils/readline/rltty.c b/cmd-line-utils/readline/rltty.c index 1a31f35952e..3e9c71c8df1 100644 --- a/cmd-line-utils/readline/rltty.c +++ b/cmd-line-utils/readline/rltty.c @@ -22,9 +22,7 @@ 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#if defined (HAVE_CONFIG_H) -# include -#endif +#include "config_readline.h" #include #include diff --git a/cmd-line-utils/readline/savestring.c b/cmd-line-utils/readline/savestring.c index 820428d8881..ae605374d13 100644 --- a/cmd-line-utils/readline/savestring.c +++ b/cmd-line-utils/readline/savestring.c @@ -21,7 +21,8 @@ 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#include +#include "config_readline.h" + #ifdef HAVE_STRING_H # include #endif diff --git a/cmd-line-utils/readline/search.c b/cmd-line-utils/readline/search.c index ce7d54eb799..1878d2bf031 100644 --- a/cmd-line-utils/readline/search.c +++ b/cmd-line-utils/readline/search.c @@ -22,9 +22,7 @@ 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#if defined (HAVE_CONFIG_H) -# include -#endif +#include "config_readline.h" #include #include diff --git a/cmd-line-utils/readline/shell.c b/cmd-line-utils/readline/shell.c index a07e2b96fc1..41668d70ab5 100644 --- a/cmd-line-utils/readline/shell.c +++ b/cmd-line-utils/readline/shell.c @@ -22,9 +22,7 @@ 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#if defined (HAVE_CONFIG_H) -# include -#endif +#include "config_readline.h" #include diff --git a/cmd-line-utils/readline/signals.c b/cmd-line-utils/readline/signals.c index 8e9c0ae60e5..be1150f6c54 100644 --- a/cmd-line-utils/readline/signals.c +++ b/cmd-line-utils/readline/signals.c @@ -21,9 +21,7 @@ 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#if defined (HAVE_CONFIG_H) -# include -#endif +#include "config_readline.h" #include /* Just for NULL. Yuck. */ #include diff --git a/cmd-line-utils/readline/terminal.c b/cmd-line-utils/readline/terminal.c index b95aea89b3d..3545fce5b85 100644 --- a/cmd-line-utils/readline/terminal.c +++ b/cmd-line-utils/readline/terminal.c @@ -21,9 +21,7 @@ 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#if defined (HAVE_CONFIG_H) -# include -#endif +#include "config_readline.h" #include #include "posixstat.h" diff --git a/cmd-line-utils/readline/text.c b/cmd-line-utils/readline/text.c index d917e99b9bd..ad7b53ec422 100644 --- a/cmd-line-utils/readline/text.c +++ b/cmd-line-utils/readline/text.c @@ -21,9 +21,7 @@ 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#if defined (HAVE_CONFIG_H) -# include -#endif +#include "config_readline.h" #if defined (HAVE_UNISTD_H) # include diff --git a/cmd-line-utils/readline/tilde.c b/cmd-line-utils/readline/tilde.c index 07fa374e1d1..c44357ffbea 100644 --- a/cmd-line-utils/readline/tilde.c +++ b/cmd-line-utils/readline/tilde.c @@ -21,9 +21,7 @@ #define READLINE_LIBRARY -#if defined (HAVE_CONFIG_H) -# include -#endif +#include "config_readline.h" #if defined (HAVE_UNISTD_H) # ifdef _MINIX diff --git a/cmd-line-utils/readline/undo.c b/cmd-line-utils/readline/undo.c index 25c287b5a2d..48baded332a 100644 --- a/cmd-line-utils/readline/undo.c +++ b/cmd-line-utils/readline/undo.c @@ -22,9 +22,7 @@ 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#if defined (HAVE_CONFIG_H) -# include -#endif +#include "config_readline.h" #include diff --git a/cmd-line-utils/readline/util.c b/cmd-line-utils/readline/util.c index a632d81129c..43478aaf1ac 100644 --- a/cmd-line-utils/readline/util.c +++ b/cmd-line-utils/readline/util.c @@ -21,9 +21,7 @@ 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#if defined (HAVE_CONFIG_H) -# include -#endif +#include "config_readline.h" #include #include diff --git a/cmd-line-utils/readline/vi_mode.c b/cmd-line-utils/readline/vi_mode.c index 74d8acbbc05..9a8cfdd7200 100644 --- a/cmd-line-utils/readline/vi_mode.c +++ b/cmd-line-utils/readline/vi_mode.c @@ -31,9 +31,7 @@ #if defined (VI_MODE) -#if defined (HAVE_CONFIG_H) -# include -#endif +#include "config_readline.h" #include diff --git a/cmd-line-utils/readline/xmalloc.c b/cmd-line-utils/readline/xmalloc.c index 8985d340d39..698807addf9 100644 --- a/cmd-line-utils/readline/xmalloc.c +++ b/cmd-line-utils/readline/xmalloc.c @@ -20,9 +20,7 @@ Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY -#if defined (HAVE_CONFIG_H) -#include -#endif +#include "config_readline.h" #include From e2d864405303d20af4e8f39efac7ff590a2f4100 Mon Sep 17 00:00:00 2001 From: "patg@radha.local" <> Date: Tue, 19 Jul 2005 03:04:51 +0200 Subject: [PATCH 40/48] Eric's implentation of OPTIMIZE TABLE and REPAIR TABLE, as well as changes to the test. --- mysql-test/r/federated.result | 319 ++-------------------------------- mysql-test/t/federated.test | 141 +-------------- sql/ha_federated.cc | 58 ++++++- sql/ha_federated.h | 54 +++++- 4 files changed, 129 insertions(+), 443 deletions(-) diff --git a/mysql-test/r/federated.result b/mysql-test/r/federated.result index 1cf4b4707cb..7692991c112 100644 --- a/mysql-test/r/federated.result +++ b/mysql-test/r/federated.result @@ -150,9 +150,6 @@ INSERT INTO federated.t1 (name, other) VALUES ('Seventh Name', 77777); INSERT INTO federated.t1 (name, other) VALUES ('Eigth Name', 88888); INSERT INTO federated.t1 (name, other) VALUES ('Ninth Name', 99999); INSERT INTO federated.t1 (name, other) VALUES ('Tenth Name', 101010); -EXPLAIN SELECT * FROM federated.t1; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 10 SELECT * FROM federated.t1; id name other created 1 First Name 11111 2004-04-04 04:04:04 @@ -165,32 +162,17 @@ id name other created 8 Eigth Name 88888 2004-04-04 04:04:04 9 Ninth Name 99999 2004-04-04 04:04:04 10 Tenth Name 101010 2004-04-04 04:04:04 -EXPLAIN SELECT * FROM federated.t1 WHERE id = 5; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 const PRIMARY PRIMARY 4 const 1 SELECT * FROM federated.t1 WHERE id = 5; id name other created 5 Fifth Name 55555 2004-04-04 04:04:04 -EXPLAIN SELECT * FROM federated.t1 WHERE name = 'Sixth Name'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 10 Using where SELECT * FROM federated.t1 WHERE name = 'Sixth Name'; id name other created 6 Sixth Name 66666 2004-04-04 04:04:04 -EXPLAIN SELECT * FROM federated.t1 WHERE id = 6 and name = 'Sixth Name'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 const PRIMARY PRIMARY 4 const 1 SELECT * FROM federated.t1 WHERE id = 6 and name = 'Sixth Name'; id name other created 6 Sixth Name 66666 2004-04-04 04:04:04 -EXPLAIN SELECT * FROM federated.t1 WHERE name = 'Sixth Name' AND other = 44444; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 10 Using where SELECT * FROM federated.t1 WHERE name = 'Sixth Name' AND other = 44444; id name other created -EXPLAIN SELECT * FROM federated.t1 WHERE name like '%th%'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 10 Using where SELECT * FROM federated.t1 WHERE name like '%th%'; id name other created 3 Third Name 33333 2004-04-04 04:04:04 @@ -209,9 +191,6 @@ UPDATE federated.t1 SET name = 'Third name' WHERE name = '3rd name'; SELECT * FROM federated.t1 WHERE name = 'Third name'; id name other created 3 Third name 33333 2004-04-04 04:04:04 -EXPLAIN SELECT * FROM federated.t1 ORDER BY id DESC; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 10 Using filesort SELECT * FROM federated.t1 ORDER BY id DESC; id name other created 10 Tenth Name 101010 2004-04-04 04:04:04 @@ -224,9 +203,6 @@ id name other created 3 Third name 33333 2004-04-04 04:04:04 2 Second Name 22222 2004-04-04 04:04:04 1 First Name 11111 2004-04-04 04:04:04 -EXPLAIN SELECT * FROM federated.t1 ORDER BY name; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 10 Using filesort SELECT * FROM federated.t1 ORDER BY name; id name other created 8 Eigth Name 88888 2004-04-04 04:04:04 @@ -239,9 +215,6 @@ id name other created 6 Sixth Name 66666 2004-04-04 04:04:04 10 Tenth Name 101010 2004-04-04 04:04:04 3 Third name 33333 2004-04-04 04:04:04 -EXPLAIN SELECT * FROM federated.t1 ORDER BY name DESC; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 10 Using filesort SELECT * FROM federated.t1 ORDER BY name DESC; id name other created 3 Third name 33333 2004-04-04 04:04:04 @@ -254,9 +227,6 @@ id name other created 1 First Name 11111 2004-04-04 04:04:04 5 Fifth Name 55555 2004-04-04 04:04:04 8 Eigth Name 88888 2004-04-04 04:04:04 -EXPLAIN SELECT * FROM federated.t1 ORDER BY name ASC; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 10 Using filesort SELECT * FROM federated.t1 ORDER BY name ASC; id name other created 8 Eigth Name 88888 2004-04-04 04:04:04 @@ -269,9 +239,6 @@ id name other created 6 Sixth Name 66666 2004-04-04 04:04:04 10 Tenth Name 101010 2004-04-04 04:04:04 3 Third name 33333 2004-04-04 04:04:04 -EXPLAIN SELECT * FROM federated.t1 GROUP BY other; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 10 Using temporary; Using filesort SELECT * FROM federated.t1 GROUP BY other; id name other created 1 First Name 11111 2004-04-04 04:04:04 @@ -333,9 +300,6 @@ INSERT INTO federated.t1 (name, other, created) VALUES ('Ninth Name', 99999, '2005-03-12 11:00:01'); INSERT INTO federated.t1 (name, other, created) VALUES ('Tenth Name', 101010, '2005-03-12 12:00:01'); -EXPLAIN SELECT * FROM federated.t1; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 10 SELECT * FROM federated.t1; id name other created 1 First Name 11111 2004-01-01 01:01:01 @@ -348,33 +312,15 @@ id name other created 8 Eigth Name 88888 2005-03-12 11:00:00 9 Ninth Name 99999 2005-03-12 11:00:01 10 Tenth Name 101010 2005-03-12 12:00:01 -EXPLAIN SELECT * FROM federated.t1 WHERE id = 5; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 const PRIMARY PRIMARY 4 const 1 SELECT * FROM federated.t1 WHERE id = 5; id name other created 5 Fifth Name 55555 2001-02-02 02:02:02 -EXPLAIN SELECT * FROM federated.t1 WHERE name = 'Sixth Name'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ref name name 34 const 2 Using where -SELECT * FROM federated.t1 WHERE name = 'Sixth Name'; -id name other created -6 Sixth Name 66666 2005-06-06 15:30:00 -EXPLAIN SELECT * FROM federated.t1 WHERE id = 6 and name = 'Sixth Name'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 const PRIMARY,name PRIMARY 4 const 1 SELECT * FROM federated.t1 WHERE id = 6 and name = 'Sixth Name'; id name other created 6 Sixth Name 66666 2005-06-06 15:30:00 -EXPLAIN SELECT * FROM federated.t1 WHERE other = 44444; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ref other other 4 const 2 SELECT * FROM federated.t1 WHERE other = 44444; id name other created 4 Fourth Name 44444 2003-04-05 00:00:00 -EXPLAIN SELECT * FROM federated.t1 WHERE name like '%th%'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 10 Using where SELECT * FROM federated.t1 WHERE name like '%th%'; id name other created 3 Third Name 33333 2004-02-14 02:14:00 @@ -664,113 +610,49 @@ INSERT INTO federated.t1 (col1, col2, col3, col4) VALUES (9, 'nine Nine', 999999, 999999); INSERT INTO federated.t1 (col1, col2, col3, col4) VALUES (10, 'Tenth ten TEN', 1010101, 1010); -EXPLAIN SELECT * FROM federated.t1 WHERE col2 = 'two two'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ref col2 col2 66 const 2 Using where SELECT * FROM federated.t1 WHERE col2 = 'two two'; id col1 col2 col3 col4 2 2 Two two 22 2222 -EXPLAIN SELECT * FROM federated.t1 WHERE col2 = 'two Two'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ref col2 col2 66 const 2 Using where SELECT * FROM federated.t1 WHERE col2 = 'two Two'; id col1 col2 col3 col4 2 2 Two two 22 2222 SELECT * FROM federated.t1 WHERE id = 3; id col1 col2 col3 col4 3 3 three Three 33 33333 -EXPLAIN SELECT * FROM federated.t1 WHERE id = 3; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ref PRIMARY PRIMARY 4 const 2 SELECT * FROM federated.t1 WHERE id = 3 AND col1 = 3; id col1 col2 col3 col4 3 3 three Three 33 33333 -EXPLAIN SELECT * FROM federated.t1 WHERE id = 3 AND col1 = 3; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ref PRIMARY,col1 PRIMARY 8 const,const 2 SELECT * FROM federated.t1 WHERE id = 4 AND col1 = 4 AND col2 = 'Two two'; id col1 col2 col3 col4 -EXPLAIN SELECT * FROM federated.t1 WHERE id = 4 AND col1 = 4 AND col2 = 'Two two'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ref PRIMARY,col1,col2 PRIMARY 74 const,const,const 2 Using where SELECT * FROM federated.t1 WHERE id = 4 AND col1 = 4 AND col2 = 'fourfourfour'; id col1 col2 col3 col4 4 4 fourfourfour 444 4444444 -EXPLAIN SELECT * FROM federated.t1 WHERE id = 4 AND col1 = 4 AND col2 = 'fourfourfour'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ref PRIMARY,col1,col2 PRIMARY 74 const,const,const 2 Using where SELECT * FROM federated.t1 WHERE id = 5 AND col2 = 'five 5 five five 5' AND col3 = 5; id col1 col2 col3 col4 5 5 five 5 five five 5 5 55555 -EXPLAIN SELECT * FROM federated.t1 WHERE id = 5 AND col2 = 'five 5 five five 5' -AND col3 = 5; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ref PRIMARY,col2,col3 PRIMARY 4 const 2 Using where -EXPLAIN SELECT * FROM federated.t1 WHERE id = 5 AND col2 = 'five 5 five five 5' -AND col3 = 5 -AND col4 = 55555; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ref PRIMARY,col2,col3,col4 PRIMARY 4 const 2 Using where SELECT * FROM federated.t1 WHERE id = 5 AND col2 = 'five 5 five five 5' AND col3 = 5 AND col4 = 55555; id col1 col2 col3 col4 5 5 five 5 five five 5 5 55555 -EXPLAIN SELECT * FROM federated.t1 WHERE id = 5 -AND col2 = 'Two two' AND col3 = 22 -AND col4 = 33; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ref PRIMARY,col2,col3,col4 PRIMARY 4 const 2 Using where SELECT * FROM federated.t1 WHERE id = 5 AND col2 = 'Two two' AND col3 = 22 AND col4 = 33; id col1 col2 col3 col4 -EXPLAIN SELECT * FROM federated.t1 WHERE id = 5 -AND col2 = 'five 5 five five 5' AND col3 = 5 -AND col4 = 55555; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ref PRIMARY,col2,col3,col4 PRIMARY 4 const 2 Using where SELECT * FROM federated.t1 WHERE id = 5 AND col2 = 'five 5 five five 5' AND col3 = 5 AND col4 = 55555; id col1 col2 col3 col4 5 5 five 5 five five 5 5 55555 -EXPLAIN SELECT * FROM federated.t1 WHERE (id = 5 AND col2 = 'five 5 five five 5') -OR (col2 = 'three Three' AND col3 = 33); -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range PRIMARY,col2,col3 col2 66 NULL 4 Using where SELECT * FROM federated.t1 WHERE (id = 5 AND col2 = 'five 5 five five 5') OR (col2 = 'three Three' AND col3 = 33); id col1 col2 col3 col4 5 5 five 5 five five 5 5 55555 3 3 three Three 33 33333 -EXPLAIN SELECT * FROM federated.t1 WHERE (id = 5 AND col2 = 'five 5 five five 5') -OR (col2 = 'three Three' AND col3 = 33) -OR col4 = 1010; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL PRIMARY,col2,col3,col4 NULL NULL NULL 10 Using where -SELECT * FROM federated.t1 WHERE (id = 5 AND col2 = 'five 5 five five 5') -OR (col2 = 'three Three' AND col3 = 33) -OR col4 = 1010; -id col1 col2 col3 col4 -3 3 three Three 33 33333 -5 5 five 5 five five 5 5 55555 -10 10 Tenth ten TEN 1010101 1010 -EXPLAIN SELECT * FROM federated.t1 WHERE (id = 5 AND col2 = 'Two two') -OR (col2 = 444 AND col3 = 4444444); -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL PRIMARY,col2,col3 NULL NULL NULL 10 Using where SELECT * FROM federated.t1 WHERE (id = 5 AND col2 = 'Two two') OR (col2 = 444 AND col3 = 4444444); id col1 col2 col3 col4 -EXPLAIN SELECT * FROM federated.t1 WHERE id = 1 -OR col1 = 10 -OR col2 = 'Two two' -OR col3 = 33 -OR col4 = 4444444; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL PRIMARY,col1,col2,col3,col4 NULL NULL NULL 10 Using where SELECT * FROM federated.t1 WHERE id = 1 OR col1 = 10 OR col2 = 'Two two' @@ -782,9 +664,6 @@ id col1 col2 col3 col4 3 3 three Three 33 33333 4 4 fourfourfour 444 4444444 10 10 Tenth ten TEN 1010101 1010 -EXPLAIN SELECT * FROM federated.t1 WHERE id > 5; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 2 Using where SELECT * FROM federated.t1 WHERE id > 5; id col1 col2 col3 col4 6 6 six six Sixsix 6666 6 @@ -792,9 +671,6 @@ id col1 col2 col3 col4 8 8 eight eight eight 88888 88 9 9 nine Nine 999999 999999 10 10 Tenth ten TEN 1010101 1010 -EXPLAIN SELECT * FROM federated.t1 WHERE id >= 5; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 2 Using where SELECT * FROM federated.t1 WHERE id >= 5; id col1 col2 col3 col4 5 5 five 5 five five 5 5 55555 @@ -803,18 +679,12 @@ id col1 col2 col3 col4 8 8 eight eight eight 88888 88 9 9 nine Nine 999999 999999 10 10 Tenth ten TEN 1010101 1010 -EXPLAIN SELECT * FROM federated.t1 WHERE id < 5; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 2 Using where SELECT * FROM federated.t1 WHERE id < 5; id col1 col2 col3 col4 1 1 one One 11 1111 2 2 Two two 22 2222 3 3 three Three 33 33333 4 4 fourfourfour 444 4444444 -EXPLAIN SELECT * FROM federated.t1 WHERE id <= 5; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 2 Using where SELECT * FROM federated.t1 WHERE id <= 5; id col1 col2 col3 col4 1 1 one One 11 1111 @@ -822,9 +692,6 @@ id col1 col2 col3 col4 3 3 three Three 33 33333 4 4 fourfourfour 444 4444444 5 5 five 5 five five 5 5 55555 -EXPLAIN SELECT * FROM federated.t1 WHERE id != 5; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 4 Using where SELECT * FROM federated.t1 WHERE id != 5; id col1 col2 col3 col4 1 1 one One 11 1111 @@ -836,26 +703,17 @@ id col1 col2 col3 col4 8 8 eight eight eight 88888 88 9 9 nine Nine 999999 999999 10 10 Tenth ten TEN 1010101 1010 -EXPLAIN SELECT * FROM federated.t1 WHERE id > 3 AND id < 7; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 2 Using where SELECT * FROM federated.t1 WHERE id > 3 AND id < 7; id col1 col2 col3 col4 4 4 fourfourfour 444 4444444 5 5 five 5 five five 5 5 55555 6 6 six six Sixsix 6666 6 -EXPLAIN SELECT * FROM federated.t1 WHERE id > 3 AND id <= 7; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 2 Using where SELECT * FROM federated.t1 WHERE id > 3 AND id <= 7; id col1 col2 col3 col4 4 4 fourfourfour 444 4444444 5 5 five 5 five five 5 5 55555 6 6 six six Sixsix 6666 6 7 7 seven Sevenseven 77777 7777 -EXPLAIN SELECT * FROM federated.t1 WHERE id >= 3 AND id <= 7; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 2 Using where SELECT * FROM federated.t1 WHERE id >= 3 AND id <= 7; id col1 col2 col3 col4 3 3 three Three 33 33333 @@ -863,21 +721,12 @@ id col1 col2 col3 col4 5 5 five 5 five five 5 5 55555 6 6 six six Sixsix 6666 6 7 7 seven Sevenseven 77777 7777 -EXPLAIN SELECT * FROM federated.t1 WHERE id < 3 AND id <= 7; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 2 Using where SELECT * FROM federated.t1 WHERE id < 3 AND id <= 7; id col1 col2 col3 col4 1 1 one One 11 1111 2 2 Two two 22 2222 -EXPLAIN SELECT * FROM federated.t1 WHERE id < 3 AND id > 7; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables SELECT * FROM federated.t1 WHERE id < 3 AND id > 7; id col1 col2 col3 col4 -EXPLAIN SELECT * FROM federated.t1 WHERE id < 3 OR id > 7; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 4 Using where SELECT * FROM federated.t1 WHERE id < 3 OR id > 7; id col1 col2 col3 col4 1 1 one One 11 1111 @@ -885,15 +734,9 @@ id col1 col2 col3 col4 8 8 eight eight eight 88888 88 9 9 nine Nine 999999 999999 10 10 Tenth ten TEN 1010101 1010 -EXPLAIN SELECT * FROM federated.t1 WHERE col2 = 'three Three'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ref col2 col2 66 const 2 Using where SELECT * FROM federated.t1 WHERE col2 = 'three Three'; id col1 col2 col3 col4 3 3 three Three 33 33333 -EXPLAIN SELECT * FROM federated.t1 WHERE col2 > 'one'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range col2 col2 66 NULL 2 Using where SELECT * FROM federated.t1 WHERE col2 > 'one'; id col1 col2 col3 col4 1 1 one One 11 1111 @@ -902,28 +745,16 @@ id col1 col2 col3 col4 6 6 six six Sixsix 6666 6 7 7 seven Sevenseven 77777 7777 10 10 Tenth ten TEN 1010101 1010 -EXPLAIN SELECT * FROM federated.t1 WHERE col2 LIKE 's%'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range col2 col2 66 NULL 2 Using where SELECT * FROM federated.t1 WHERE col2 LIKE 's%'; id col1 col2 col3 col4 7 7 seven Sevenseven 77777 7777 6 6 six six Sixsix 6666 6 -EXPLAIN SELECT * FROM federated.t1 WHERE col2 LIKE 'si%'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range col2 col2 66 NULL 2 Using where SELECT * FROM federated.t1 WHERE col2 LIKE 'si%'; id col1 col2 col3 col4 6 6 six six Sixsix 6666 6 -EXPLAIN SELECT * FROM federated.t1 WHERE col2 LIKE 'se%'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range col2 col2 66 NULL 2 Using where SELECT * FROM federated.t1 WHERE col2 LIKE 'se%'; id col1 col2 col3 col4 7 7 seven Sevenseven 77777 7777 -EXPLAIN SELECT * FROM federated.t1 WHERE col2 NOT LIKE 'e%'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 10 Using where SELECT * FROM federated.t1 WHERE col2 NOT LIKE 'e%'; id col1 col2 col3 col4 1 1 one One 11 1111 @@ -935,9 +766,6 @@ id col1 col2 col3 col4 7 7 seven Sevenseven 77777 7777 9 9 nine Nine 999999 999999 10 10 Tenth ten TEN 1010101 1010 -EXPLAIN SELECT * FROM federated.t1 WHERE col2 <> 'one One'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range col2 col2 66 NULL 4 Using where SELECT * FROM federated.t1 WHERE col2 <> 'one One'; id col1 col2 col3 col4 4 4 fourfourfour 444 4444444 @@ -987,45 +815,24 @@ INSERT INTO federated.t1 (col1, col2, col3, col4) VALUES ('gggg', 'ggggggggggggggggggg', 'gagagagaga', 'gcgcgcgcgcgcgcgc'); INSERT INTO federated.t1 (col1, col2, col3, col4) VALUES ('hhhh', 'hhhhhhhhhhhhhhhhhhh', 'hahahahaha', 'hchchchchchchchc'); -EXPLAIN SELECT * FROM federated.t1 WHERE col1 = 'cccc'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ref PRIMARY PRIMARY 10 const 2 Using where SELECT * FROM federated.t1 WHERE col1 = 'cccc'; col1 col2 col3 col4 cccc ccccccccccccccccccc cacacacaca cbcbcbcbcbcbcbcb -EXPLAIN SELECT * FROM federated.t1 WHERE col2 = 'eeeeeeeeeeeeeeeeeee'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ref 3key 3key 130 const 2 Using where SELECT * FROM federated.t1 WHERE col2 = 'eeeeeeeeeeeeeeeeeee'; col1 col2 col3 col4 eeee eeeeeeeeeeeeeeeeeee eaeaeaeaea ecececececececec -EXPLAIN SELECT * FROM federated.t1 WHERE col3 = 'bababababa'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ref 2key 2key 22 const 2 Using where SELECT * FROM federated.t1 WHERE col3 = 'bababababa'; col1 col2 col3 col4 bbbb bbbbbbbbbbbbbbbbbbb bababababa bcbcbcbcbcbcbcbc -EXPLAIN SELECT * FROM federated.t1 WHERE col1 = 'gggg' AND col2 = 'ggggggggggggggggggg'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ref PRIMARY,3key PRIMARY 140 const,const 2 Using where SELECT * FROM federated.t1 WHERE col1 = 'gggg' AND col2 = 'ggggggggggggggggggg'; col1 col2 col3 col4 gggg ggggggggggggggggggg gagagagaga gcgcgcgcgcgcgcgc -EXPLAIN SELECT * FROM federated.t1 WHERE col1 = 'gggg' AND col3 = 'gagagagaga'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ref PRIMARY,2key PRIMARY 10 const 2 Using where SELECT * FROM federated.t1 WHERE col1 = 'gggg' AND col3 = 'gagagagaga'; col1 col2 col3 col4 gggg ggggggggggggggggggg gagagagaga gcgcgcgcgcgcgcgc -EXPLAIN SELECT * FROM federated.t1 WHERE col1 = 'ffff' AND col4 = 'fcfcfcfcfcfcfcfc'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ref PRIMARY,col4 PRIMARY 10 const 2 Using where SELECT * FROM federated.t1 WHERE col1 = 'ffff' AND col4 = 'fcfcfcfcfcfcfcfc'; col1 col2 col3 col4 ffff fffffffffffffffffff fafafafafa fcfcfcfcfcfcfcfc -EXPLAIN SELECT * FROM federated.t1 WHERE col1 > 'bbbb'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range PRIMARY PRIMARY 10 NULL 2 Using where SELECT * FROM federated.t1 WHERE col1 > 'bbbb'; col1 col2 col3 col4 cccc ccccccccccccccccccc cacacacaca cbcbcbcbcbcbcbcb @@ -1034,9 +841,6 @@ eeee eeeeeeeeeeeeeeeeeee eaeaeaeaea ecececececececec ffff fffffffffffffffffff fafafafafa fcfcfcfcfcfcfcfc gggg ggggggggggggggggggg gagagagaga gcgcgcgcgcgcgcgc hhhh hhhhhhhhhhhhhhhhhhh hahahahaha hchchchchchchchc -EXPLAIN SELECT * FROM federated.t1 WHERE col1 >= 'bbbb'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range PRIMARY PRIMARY 10 NULL 2 Using where SELECT * FROM federated.t1 WHERE col1 >= 'bbbb'; col1 col2 col3 col4 bbbb bbbbbbbbbbbbbbbbbbb bababababa bcbcbcbcbcbcbcbc @@ -1046,22 +850,13 @@ eeee eeeeeeeeeeeeeeeeeee eaeaeaeaea ecececececececec ffff fffffffffffffffffff fafafafafa fcfcfcfcfcfcfcfc gggg ggggggggggggggggggg gagagagaga gcgcgcgcgcgcgcgc hhhh hhhhhhhhhhhhhhhhhhh hahahahaha hchchchchchchchc -EXPLAIN SELECT * FROM federated.t1 WHERE col1 < 'bbbb'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range PRIMARY PRIMARY 10 NULL 2 Using where SELECT * FROM federated.t1 WHERE col1 < 'bbbb'; col1 col2 col3 col4 aaaa aaaaaaaaaaaaaaaaaaa ababababab acacacacacacacac -EXPLAIN SELECT * FROM federated.t1 WHERE col1 <= 'bbbb'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range PRIMARY PRIMARY 10 NULL 2 Using where SELECT * FROM federated.t1 WHERE col1 <= 'bbbb'; col1 col2 col3 col4 aaaa aaaaaaaaaaaaaaaaaaa ababababab acacacacacacacac bbbb bbbbbbbbbbbbbbbbbbb bababababa bcbcbcbcbcbcbcbc -EXPLAIN SELECT * FROM federated.t1 WHERE col1 <> 'bbbb'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range PRIMARY PRIMARY 10 NULL 4 Using where SELECT * FROM federated.t1 WHERE col1 <> 'bbbb'; col1 col2 col3 col4 aaaa aaaaaaaaaaaaaaaaaaa ababababab acacacacacacacac @@ -1071,22 +866,13 @@ eeee eeeeeeeeeeeeeeeeeee eaeaeaeaea ecececececececec ffff fffffffffffffffffff fafafafafa fcfcfcfcfcfcfcfc gggg ggggggggggggggggggg gagagagaga gcgcgcgcgcgcgcgc hhhh hhhhhhhhhhhhhhhhhhh hahahahaha hchchchchchchchc -EXPLAIN SELECT * FROM federated.t1 WHERE col1 LIKE 'b%'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range PRIMARY PRIMARY 10 NULL 2 Using where SELECT * FROM federated.t1 WHERE col1 LIKE 'b%'; col1 col2 col3 col4 bbbb bbbbbbbbbbbbbbbbbbb bababababa bcbcbcbcbcbcbcbc -EXPLAIN SELECT * FROM federated.t1 WHERE col4 LIKE '%b%'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 8 Using where SELECT * FROM federated.t1 WHERE col4 LIKE '%b%'; col1 col2 col3 col4 bbbb bbbbbbbbbbbbbbbbbbb bababababa bcbcbcbcbcbcbcbc cccc ccccccccccccccccccc cacacacaca cbcbcbcbcbcbcbcb -EXPLAIN SELECT * FROM federated.t1 WHERE col1 NOT LIKE 'c%'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 8 Using where SELECT * FROM federated.t1 WHERE col1 NOT LIKE 'c%'; col1 col2 col3 col4 aaaa aaaaaaaaaaaaaaaaaaa ababababab acacacacacacacac @@ -1096,9 +882,6 @@ eeee eeeeeeeeeeeeeeeeeee eaeaeaeaea ecececececececec ffff fffffffffffffffffff fafafafafa fcfcfcfcfcfcfcfc gggg ggggggggggggggggggg gagagagaga gcgcgcgcgcgcgcgc hhhh hhhhhhhhhhhhhhhhhhh hahahahaha hchchchchchchchc -EXPLAIN SELECT * FROM federated.t1 WHERE col4 NOT LIKE '%c%'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 8 Using where SELECT * FROM federated.t1 WHERE col4 NOT LIKE '%c%'; col1 col2 col3 col4 DROP TABLE IF EXISTS federated.t1; @@ -1121,11 +904,6 @@ INSERT INTO federated.t1 VALUES ('aaa', '111', 'ccc'); INSERT INTO federated.t1 VALUES ('bbb', '222', 'yyy'); INSERT INTO federated.t1 VALUES ('ccc', '111', 'zzz'); INSERT INTO federated.t1 VALUES ('ccd', '112', 'zzzz'); -EXPLAIN SELECT col3 FROM federated.t1 WHERE ( -(col1 = 'aaa' AND col2 >= '111') OR col1 > 'aaa') AND -(col1 < 'ccc' OR ( col1 = 'ccc' AND col2 <= '111')); -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range PRIMARY PRIMARY 14 NULL 3 Using where; Using index SELECT col3 FROM federated.t1 WHERE ( (col1 = 'aaa' AND col2 >= '111') OR col1 > 'aaa') AND (col1 < 'ccc' OR ( col1 = 'ccc' AND col2 <= '111')); @@ -1133,11 +911,6 @@ col3 ccc yyy zzz -EXPLAIN SELECT col3 FROM federated.t1 WHERE ( -(col1 = 'aaa' AND col2 >= '111') OR col1 > 'aaa') AND -(col1 < 'ccc' OR ( col1 = 'ccc' AND col2 <= '111')); -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range PRIMARY PRIMARY 20 NULL 6 Using where SELECT col3 FROM federated.t1 WHERE ( (col1 = 'aaa' AND col2 >= '111') OR col1 > 'aaa') AND (col1 < 'ccc' OR ( col1 = 'ccc' AND col2 <= '111')); @@ -1182,13 +955,6 @@ AND floatval IS NULL AND other IS NULL; count(*) 2 -EXPLAIN SELECT count(*) FROM federated.t1 -WHERE id IS NULL -AND name IS NULL -AND floatval IS NULL -AND other IS NULL; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 5 Using where DROP TABLE IF EXISTS federated.t1; CREATE TABLE federated.t1 ( `blurb_id` int NOT NULL DEFAULT 0, @@ -1230,30 +996,6 @@ ENGINE="FEDERATED" DEFAULT CHARSET=latin1 COMMENT='mysql://root@127.0.0.1:SLAVE_PORT/federated/t1'; INSERT INTO federated.t1 VALUES (3,3,3),(1,1,1),(2,2,2),(4,4,4); -EXPLAIN SELECT * FROM federated.t1 ORDER BY a; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 4 Using filesort -EXPLAIN SELECT * FROM federated.t1 ORDER BY b; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 4 Using filesort -EXPLAIN SELECT * FROM federated.t1 ORDER BY c; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 4 Using filesort -EXPLAIN SELECT a FROM federated.t1 ORDER BY a; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 4 Using filesort -EXPLAIN SELECT b FROM federated.t1 ORDER BY b; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 4 Using filesort -EXPLAIN SELECT a,b FROM federated.t1 ORDER BY b; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 4 Using filesort -EXPLAIN SELECT a,b FROM federated.t1; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 4 -EXPLAIN SELECT a,b,c FROM federated.t1; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 4 DROP TABLE IF EXISTS federated.t1; CREATE TABLE federated.t1 (i1 int, i2 int, i3 int, i4 int, i5 int, i6 int, i7 int, i8 int, i9 int, i10 int, i11 int, i12 int, i13 int, i14 int, i15 int, i16 int, i17 @@ -1553,9 +1295,6 @@ values (1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, "PatrickG"); UPDATE federated.t1 SET b=repeat('a',256); UPDATE federated.t1 SET i1=0, i2=0, i3=0, i4=0, i5=0, i6=0, i7=0, i8=0, i9=0, i10=0; -EXPLAIN SELECT * FROM federated.t1 WHERE i9=0 and i10=0; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 1 Using where SELECT * FROM federated.t1 WHERE i9=0 and i10=0; i1 i2 i3 i4 i5 i6 i7 i8 i9 i10 i11 i12 i13 i14 i15 i16 i17 i18 i19 i20 i21 i22 i23 i24 i25 i26 i27 i28 i29 i30 i31 i32 i33 i34 i35 i36 i37 i38 i39 i40 i41 i42 i43 i44 i45 i46 i47 i48 i49 i50 i51 i52 i53 i54 i55 i56 i57 i58 i59 i60 i61 i62 i63 i64 i65 i66 i67 i68 i69 i70 i71 i72 i73 i74 i75 i76 i77 i78 i79 i80 i81 i82 i83 i84 i85 i86 i87 i88 i89 i90 i91 i92 i93 i94 i95 i96 i97 i98 i99 i100 i101 i102 i103 i104 i105 i106 i107 i108 i109 i110 i111 i112 i113 i114 i115 i116 i117 i118 i119 i120 i121 i122 i123 i124 i125 i126 i127 i128 i129 i130 i131 i132 i133 i134 i135 i136 i137 i138 i139 i140 i141 i142 i143 i144 i145 i146 i147 i148 i149 i150 i151 i152 i153 i154 i155 i156 i157 i158 i159 i160 i161 i162 i163 i164 i165 i166 i167 i168 i169 i170 i171 i172 i173 i174 i175 i176 i177 i178 i179 i180 i181 i182 i183 i184 i185 i186 i187 i188 i189 i190 i191 i192 i193 i194 i195 i196 i197 i198 i199 i200 i201 i202 i203 i204 i205 i206 i207 i208 i209 i210 i211 i212 i213 i214 i215 i216 i217 i218 i219 i220 i221 i222 i223 i224 i225 i226 i227 i228 i229 i230 i231 i232 i233 i234 i235 i236 i237 i238 i239 i240 i241 i242 i243 i244 i245 i246 i247 i248 i249 i250 i251 i252 i253 i254 i255 i256 i257 i258 i259 i260 i261 i262 i263 i264 i265 i266 i267 i268 i269 i270 i271 i272 i273 i274 i275 i276 i277 i278 i279 i280 i281 i282 i283 i284 i285 i286 i287 i288 i289 i290 i291 i292 i293 i294 i295 i296 i297 i298 i299 i300 i301 i302 i303 i304 i305 i306 i307 i308 i309 i310 i311 i312 i313 i314 i315 i316 i317 i318 i319 i320 i321 i322 i323 i324 i325 i326 i327 i328 i329 i330 i331 i332 i333 i334 i335 i336 i337 i338 i339 i340 i341 i342 i343 i344 i345 i346 i347 i348 i349 i350 i351 i352 i353 i354 i355 i356 i357 i358 i359 i360 i361 i362 i363 i364 i365 i366 i367 i368 i369 i370 i371 i372 i373 i374 i375 i376 i377 i378 i379 i380 i381 i382 i383 i384 i385 i386 i387 i388 i389 i390 i391 i392 i393 i394 i395 i396 i397 i398 i399 i400 i401 i402 i403 i404 i405 i406 i407 i408 i409 i410 i411 i412 i413 i414 i415 i416 i417 i418 i419 i420 i421 i422 i423 i424 i425 i426 i427 i428 i429 i430 i431 i432 i433 i434 i435 i436 i437 i438 i439 i440 i441 i442 i443 i444 i445 i446 i447 i448 i449 i450 i451 i452 i453 i454 i455 i456 i457 i458 i459 i460 i461 i462 i463 i464 i465 i466 i467 i468 i469 i470 i471 i472 i473 i474 i475 i476 i477 i478 i479 i480 i481 i482 i483 i484 i485 i486 i487 i488 i489 i490 i491 i492 i493 i494 i495 i496 i497 i498 i499 i500 i501 i502 i503 i504 i505 i506 i507 i508 i509 i510 i511 i512 i513 i514 i515 i516 i517 i518 i519 i520 i521 i522 i523 i524 i525 i526 i527 i528 i529 i530 i531 i532 i533 i534 i535 i536 i537 i538 i539 i540 i541 i542 i543 i544 i545 i546 i547 i548 i549 i550 i551 i552 i553 i554 i555 i556 i557 i558 i559 i560 i561 i562 i563 i564 i565 i566 i567 i568 i569 i570 i571 i572 i573 i574 i575 i576 i577 i578 i579 i580 i581 i582 i583 i584 i585 i586 i587 i588 i589 i590 i591 i592 i593 i594 i595 i596 i597 i598 i599 i600 i601 i602 i603 i604 i605 i606 i607 i608 i609 i610 i611 i612 i613 i614 i615 i616 i617 i618 i619 i620 i621 i622 i623 i624 i625 i626 i627 i628 i629 i630 i631 i632 i633 i634 i635 i636 i637 i638 i639 i640 i641 i642 i643 i644 i645 i646 i647 i648 i649 i650 i651 i652 i653 i654 i655 i656 i657 i658 i659 i660 i661 i662 i663 i664 i665 i666 i667 i668 i669 i670 i671 i672 i673 i674 i675 i676 i677 i678 i679 i680 i681 i682 i683 i684 i685 i686 i687 i688 i689 i690 i691 i692 i693 i694 i695 i696 i697 i698 i699 i700 i701 i702 i703 i704 i705 i706 i707 i708 i709 i710 i711 i712 i713 i714 i715 i716 i717 i718 i719 i720 i721 i722 i723 i724 i725 i726 i727 i728 i729 i730 i731 i732 i733 i734 i735 i736 i737 i738 i739 i740 i741 i742 i743 i744 i745 i746 i747 i748 i749 i750 i751 i752 i753 i754 i755 i756 i757 i758 i759 i760 i761 i762 i763 i764 i765 i766 i767 i768 i769 i770 i771 i772 i773 i774 i775 i776 i777 i778 i779 i780 i781 i782 i783 i784 i785 i786 i787 i788 i789 i790 i791 i792 i793 i794 i795 i796 i797 i798 i799 i800 i801 i802 i803 i804 i805 i806 i807 i808 i809 i810 i811 i812 i813 i814 i815 i816 i817 i818 i819 i820 i821 i822 i823 i824 i825 i826 i827 i828 i829 i830 i831 i832 i833 i834 i835 i836 i837 i838 i839 i840 i841 i842 i843 i844 i845 i846 i847 i848 i849 i850 i851 i852 i853 i854 i855 i856 i857 i858 i859 i860 i861 i862 i863 i864 i865 i866 i867 i868 i869 i870 i871 i872 i873 i874 i875 i876 i877 i878 i879 i880 i881 i882 i883 i884 i885 i886 i887 i888 i889 i890 i891 i892 i893 i894 i895 i896 i897 i898 i899 i900 i901 i902 i903 i904 i905 i906 i907 i908 i909 i910 i911 i912 i913 i914 i915 i916 i917 i918 i919 i920 i921 i922 i923 i924 i925 i926 i927 i928 i929 i930 i931 i932 i933 i934 i935 i936 i937 i938 i939 i940 i941 i942 i943 i944 i945 i946 i947 i948 i949 i950 i951 i952 i953 i954 i955 i956 i957 i958 i959 i960 i961 i962 i963 i964 i965 i966 i967 i968 i969 i970 i971 i972 i973 i974 i975 i976 i977 i978 i979 i980 i981 i982 i983 i984 i985 i986 i987 i988 i989 i990 i991 i992 i993 i994 i995 i996 i997 i998 i999 i1000 b 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa @@ -1645,13 +1384,6 @@ INSERT INTO federated.t1 (name, country_id, other) VALUES ('Lenz', 2, 22222); INSERT INTO federated.t1 (name, country_id, other) VALUES ('Marizio', 3, 33333); INSERT INTO federated.t1 (name, country_id, other) VALUES ('Monty', 4, 33333); INSERT INTO federated.t1 (name, country_id, other) VALUES ('Sanja', 5, 33333); -EXPLAIN SELECT federated.t1.name AS name, federated.t1.country_id AS country_id, -federated.t1.other AS other, federated.countries.country AS country -FROM federated.t1, federated.countries WHERE -federated.t1.country_id = federated.countries.id; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE countries ALL PRIMARY NULL NULL NULL 5 -1 SIMPLE t1 ref country_id country_id 4 federated.countries.id 2 SELECT federated.t1.name AS name, federated.t1.country_id AS country_id, federated.t1.other AS other, federated.countries.country AS country FROM federated.t1, federated.countries WHERE @@ -1662,13 +1394,6 @@ Lenz 2 22222 Germany Marizio 3 33333 Italy Monty 4 33333 Finland Sanja 5 33333 Ukraine -EXPLAIN SELECT federated.t1.name AS name, federated.t1.country_id AS country_id, -federated.t1.other AS other, federated.countries.country AS country -FROM federated.t1 INNER JOIN federated.countries ON -federated.t1.country_id = federated.countries.id; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE countries ALL PRIMARY NULL NULL NULL 5 -1 SIMPLE t1 ref country_id country_id 4 federated.countries.id 2 SELECT federated.t1.name AS name, federated.t1.country_id AS country_id, federated.t1.other AS other, federated.countries.country AS country FROM federated.t1 INNER JOIN federated.countries ON @@ -1679,14 +1404,6 @@ Lenz 2 22222 Germany Marizio 3 33333 Italy Monty 4 33333 Finland Sanja 5 33333 Ukraine -EXPLAIN SELECT federated.t1.name AS name, federated.t1.country_id AS country_id, -federated.t1.other AS other, federated.countries.country AS country -FROM federated.t1 INNER JOIN federated.countries ON -federated.t1.country_id = federated.countries.id -WHERE federated.t1.name = 'Monty'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE countries ALL PRIMARY NULL NULL NULL 5 -1 SIMPLE t1 ref country_id country_id 4 federated.countries.id 2 Using where SELECT federated.t1.name AS name, federated.t1.country_id AS country_id, federated.t1.other AS other, federated.countries.country AS country FROM federated.t1 INNER JOIN federated.countries ON @@ -1694,13 +1411,6 @@ federated.t1.country_id = federated.countries.id WHERE federated.t1.name = 'Monty'; name country_id other country Monty 4 33333 Finland -EXPLAIN SELECT federated.t1.*, federated.countries.country -FROM federated.t1 LEFT JOIN federated.countries -ON federated.t1.country_id = federated.countries.id -ORDER BY federated.countries.id; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 5 Using temporary; Using filesort -1 SIMPLE countries eq_ref PRIMARY PRIMARY 4 federated.t1.country_id 1 SELECT federated.t1.*, federated.countries.country FROM federated.t1 LEFT JOIN federated.countries ON federated.t1.country_id = federated.countries.id @@ -1711,13 +1421,6 @@ id country_id name other country 3 3 Marizio 33333 Italy 4 4 Monty 33333 Finland 5 5 Sanja 33333 Ukraine -EXPLAIN SELECT federated.t1.*, federated.countries.country -FROM federated.t1 LEFT JOIN federated.countries -ON federated.t1.country_id = federated.countries.id -ORDER BY federated.countries.country; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 5 Using temporary; Using filesort -1 SIMPLE countries eq_ref PRIMARY PRIMARY 4 federated.t1.country_id 1 SELECT federated.t1.*, federated.countries.country FROM federated.t1 LEFT JOIN federated.countries ON federated.t1.country_id = federated.countries.id @@ -1728,13 +1431,6 @@ id country_id name other country 1 1 Kumar 11111 India 3 3 Marizio 33333 Italy 5 5 Sanja 33333 Ukraine -EXPLAIN SELECT federated.t1.*, federated.countries.country -FROM federated.t1 RIGHT JOIN federated.countries -ON federated.t1.country_id = federated.countries.id -ORDER BY federated.t1.country_id; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE countries ALL NULL NULL NULL NULL 5 Using temporary; Using filesort -1 SIMPLE t1 ref country_id country_id 4 federated.countries.id 2 SELECT federated.t1.*, federated.countries.country FROM federated.t1 RIGHT JOIN federated.countries ON federated.t1.country_id = federated.countries.id @@ -1746,6 +1442,21 @@ id country_id name other country 4 4 Monty 33333 Finland 5 5 Sanja 33333 Ukraine DROP TABLE federated.countries; +OPTIMIZE TABLE federated.t1; +Table Op Msg_type Msg_text +federated.t1 optimize status OK +REPAIR TABLE federated.t1; +Table Op Msg_type Msg_text +federated.t1 repair status OK +REPAIR TABLE federated.t1 QUICK; +Table Op Msg_type Msg_text +federated.t1 repair status OK +REPAIR TABLE federated.t1 EXTENDED; +Table Op Msg_type Msg_text +federated.t1 repair status OK +REPAIR TABLE federated.t1 USE_FRM; +Table Op Msg_type Msg_text +federated.t1 repair status OK DROP TABLE IF EXISTS federated.t1; DROP DATABASE IF EXISTS federated; DROP TABLE IF EXISTS federated.t1; diff --git a/mysql-test/t/federated.test b/mysql-test/t/federated.test index e0cc4a9945d..255b9dc22d7 100644 --- a/mysql-test/t/federated.test +++ b/mysql-test/t/federated.test @@ -160,33 +160,22 @@ INSERT INTO federated.t1 (name, other) VALUES ('Ninth Name', 99999); INSERT INTO federated.t1 (name, other) VALUES ('Tenth Name', 101010); # basic select -EXPLAIN SELECT * FROM federated.t1; SELECT * FROM federated.t1; # with PRIMARY KEY index_read_idx -EXPLAIN SELECT * FROM federated.t1 WHERE id = 5; SELECT * FROM federated.t1 WHERE id = 5; -EXPLAIN SELECT * FROM federated.t1 WHERE name = 'Sixth Name'; SELECT * FROM federated.t1 WHERE name = 'Sixth Name'; -EXPLAIN SELECT * FROM federated.t1 WHERE id = 6 and name = 'Sixth Name'; SELECT * FROM federated.t1 WHERE id = 6 and name = 'Sixth Name'; -EXPLAIN SELECT * FROM federated.t1 WHERE name = 'Sixth Name' AND other = 44444; SELECT * FROM federated.t1 WHERE name = 'Sixth Name' AND other = 44444; -EXPLAIN SELECT * FROM federated.t1 WHERE name like '%th%'; SELECT * FROM federated.t1 WHERE name like '%th%'; UPDATE federated.t1 SET name = '3rd name' WHERE id = 3; SELECT * FROM federated.t1 WHERE name = '3rd name'; UPDATE federated.t1 SET name = 'Third name' WHERE name = '3rd name'; SELECT * FROM federated.t1 WHERE name = 'Third name'; # rnd_post, ::position -EXPLAIN SELECT * FROM federated.t1 ORDER BY id DESC; SELECT * FROM federated.t1 ORDER BY id DESC; -EXPLAIN SELECT * FROM federated.t1 ORDER BY name; SELECT * FROM federated.t1 ORDER BY name; -EXPLAIN SELECT * FROM federated.t1 ORDER BY name DESC; SELECT * FROM federated.t1 ORDER BY name DESC; -EXPLAIN SELECT * FROM federated.t1 ORDER BY name ASC; SELECT * FROM federated.t1 ORDER BY name ASC; -EXPLAIN SELECT * FROM federated.t1 GROUP BY other; SELECT * FROM federated.t1 GROUP BY other; # ::delete_row @@ -248,21 +237,14 @@ INSERT INTO federated.t1 (name, other, created) VALUES ('Tenth Name', 101010, '2005-03-12 12:00:01'); # basic select -EXPLAIN SELECT * FROM federated.t1; SELECT * FROM federated.t1; # with PRIMARY KEY index_read_idx -EXPLAIN SELECT * FROM federated.t1 WHERE id = 5; SELECT * FROM federated.t1 WHERE id = 5; # with regular key index_read -> index_read_idx -EXPLAIN SELECT * FROM federated.t1 WHERE name = 'Sixth Name'; -SELECT * FROM federated.t1 WHERE name = 'Sixth Name'; # regular and PRIMARY KEY index_read_idx -EXPLAIN SELECT * FROM federated.t1 WHERE id = 6 and name = 'Sixth Name'; SELECT * FROM federated.t1 WHERE id = 6 and name = 'Sixth Name'; # with regular key index_read -> index_read_idx -EXPLAIN SELECT * FROM federated.t1 WHERE other = 44444; SELECT * FROM federated.t1 WHERE other = 44444; -EXPLAIN SELECT * FROM federated.t1 WHERE name like '%th%'; SELECT * FROM federated.t1 WHERE name like '%th%'; # update - update_row, index_read_idx UPDATE federated.t1 SET name = '3rd name' WHERE id = 3; @@ -462,99 +444,49 @@ INSERT INTO federated.t1 (col1, col2, col3, col4) INSERT INTO federated.t1 (col1, col2, col3, col4) VALUES (10, 'Tenth ten TEN', 1010101, 1010); -EXPLAIN SELECT * FROM federated.t1 WHERE col2 = 'two two'; SELECT * FROM federated.t1 WHERE col2 = 'two two'; -EXPLAIN SELECT * FROM federated.t1 WHERE col2 = 'two Two'; SELECT * FROM federated.t1 WHERE col2 = 'two Two'; SELECT * FROM federated.t1 WHERE id = 3; -EXPLAIN SELECT * FROM federated.t1 WHERE id = 3; SELECT * FROM federated.t1 WHERE id = 3 AND col1 = 3; -EXPLAIN SELECT * FROM federated.t1 WHERE id = 3 AND col1 = 3; SELECT * FROM federated.t1 WHERE id = 4 AND col1 = 4 AND col2 = 'Two two'; -EXPLAIN SELECT * FROM federated.t1 WHERE id = 4 AND col1 = 4 AND col2 = 'Two two'; SELECT * FROM federated.t1 WHERE id = 4 AND col1 = 4 AND col2 = 'fourfourfour'; -EXPLAIN SELECT * FROM federated.t1 WHERE id = 4 AND col1 = 4 AND col2 = 'fourfourfour'; SELECT * FROM federated.t1 WHERE id = 5 AND col2 = 'five 5 five five 5' AND col3 = 5; -EXPLAIN SELECT * FROM federated.t1 WHERE id = 5 AND col2 = 'five 5 five five 5' - AND col3 = 5; -EXPLAIN SELECT * FROM federated.t1 WHERE id = 5 AND col2 = 'five 5 five five 5' - AND col3 = 5 - AND col4 = 55555; SELECT * FROM federated.t1 WHERE id = 5 AND col2 = 'five 5 five five 5' AND col3 = 5 AND col4 = 55555; -EXPLAIN SELECT * FROM federated.t1 WHERE id = 5 - AND col2 = 'Two two' AND col3 = 22 - AND col4 = 33; SELECT * FROM federated.t1 WHERE id = 5 AND col2 = 'Two two' AND col3 = 22 AND col4 = 33; -EXPLAIN SELECT * FROM federated.t1 WHERE id = 5 - AND col2 = 'five 5 five five 5' AND col3 = 5 - AND col4 = 55555; SELECT * FROM federated.t1 WHERE id = 5 AND col2 = 'five 5 five five 5' AND col3 = 5 AND col4 = 55555; -EXPLAIN SELECT * FROM federated.t1 WHERE (id = 5 AND col2 = 'five 5 five five 5') - OR (col2 = 'three Three' AND col3 = 33); SELECT * FROM federated.t1 WHERE (id = 5 AND col2 = 'five 5 five five 5') OR (col2 = 'three Three' AND col3 = 33); -EXPLAIN SELECT * FROM federated.t1 WHERE (id = 5 AND col2 = 'five 5 five five 5') - OR (col2 = 'three Three' AND col3 = 33) - OR col4 = 1010; -SELECT * FROM federated.t1 WHERE (id = 5 AND col2 = 'five 5 five five 5') - OR (col2 = 'three Three' AND col3 = 33) - OR col4 = 1010; -EXPLAIN SELECT * FROM federated.t1 WHERE (id = 5 AND col2 = 'Two two') - OR (col2 = 444 AND col3 = 4444444); SELECT * FROM federated.t1 WHERE (id = 5 AND col2 = 'Two two') OR (col2 = 444 AND col3 = 4444444); -EXPLAIN SELECT * FROM federated.t1 WHERE id = 1 - OR col1 = 10 - OR col2 = 'Two two' - OR col3 = 33 - OR col4 = 4444444; SELECT * FROM federated.t1 WHERE id = 1 OR col1 = 10 OR col2 = 'Two two' OR col3 = 33 OR col4 = 4444444; -EXPLAIN SELECT * FROM federated.t1 WHERE id > 5; SELECT * FROM federated.t1 WHERE id > 5; -EXPLAIN SELECT * FROM federated.t1 WHERE id >= 5; SELECT * FROM federated.t1 WHERE id >= 5; -EXPLAIN SELECT * FROM federated.t1 WHERE id < 5; SELECT * FROM federated.t1 WHERE id < 5; -EXPLAIN SELECT * FROM federated.t1 WHERE id <= 5; SELECT * FROM federated.t1 WHERE id <= 5; -EXPLAIN SELECT * FROM federated.t1 WHERE id != 5; SELECT * FROM federated.t1 WHERE id != 5; -EXPLAIN SELECT * FROM federated.t1 WHERE id > 3 AND id < 7; SELECT * FROM federated.t1 WHERE id > 3 AND id < 7; -EXPLAIN SELECT * FROM federated.t1 WHERE id > 3 AND id <= 7; SELECT * FROM federated.t1 WHERE id > 3 AND id <= 7; -EXPLAIN SELECT * FROM federated.t1 WHERE id >= 3 AND id <= 7; SELECT * FROM federated.t1 WHERE id >= 3 AND id <= 7; -EXPLAIN SELECT * FROM federated.t1 WHERE id < 3 AND id <= 7; SELECT * FROM federated.t1 WHERE id < 3 AND id <= 7; -EXPLAIN SELECT * FROM federated.t1 WHERE id < 3 AND id > 7; SELECT * FROM federated.t1 WHERE id < 3 AND id > 7; -EXPLAIN SELECT * FROM federated.t1 WHERE id < 3 OR id > 7; SELECT * FROM federated.t1 WHERE id < 3 OR id > 7; -EXPLAIN SELECT * FROM federated.t1 WHERE col2 = 'three Three'; SELECT * FROM federated.t1 WHERE col2 = 'three Three'; -EXPLAIN SELECT * FROM federated.t1 WHERE col2 > 'one'; SELECT * FROM federated.t1 WHERE col2 > 'one'; -EXPLAIN SELECT * FROM federated.t1 WHERE col2 LIKE 's%'; SELECT * FROM federated.t1 WHERE col2 LIKE 's%'; -EXPLAIN SELECT * FROM federated.t1 WHERE col2 LIKE 'si%'; SELECT * FROM federated.t1 WHERE col2 LIKE 'si%'; -EXPLAIN SELECT * FROM federated.t1 WHERE col2 LIKE 'se%'; SELECT * FROM federated.t1 WHERE col2 LIKE 'se%'; -EXPLAIN SELECT * FROM federated.t1 WHERE col2 NOT LIKE 'e%'; SELECT * FROM federated.t1 WHERE col2 NOT LIKE 'e%'; -EXPLAIN SELECT * FROM federated.t1 WHERE col2 <> 'one One'; SELECT * FROM federated.t1 WHERE col2 <> 'one One'; # more multi-column indexes, in the primary key @@ -602,35 +534,20 @@ INSERT INTO federated.t1 (col1, col2, col3, col4) INSERT INTO federated.t1 (col1, col2, col3, col4) VALUES ('hhhh', 'hhhhhhhhhhhhhhhhhhh', 'hahahahaha', 'hchchchchchchchc'); -EXPLAIN SELECT * FROM federated.t1 WHERE col1 = 'cccc'; SELECT * FROM federated.t1 WHERE col1 = 'cccc'; -EXPLAIN SELECT * FROM federated.t1 WHERE col2 = 'eeeeeeeeeeeeeeeeeee'; SELECT * FROM federated.t1 WHERE col2 = 'eeeeeeeeeeeeeeeeeee'; -EXPLAIN SELECT * FROM federated.t1 WHERE col3 = 'bababababa'; SELECT * FROM federated.t1 WHERE col3 = 'bababababa'; -EXPLAIN SELECT * FROM federated.t1 WHERE col1 = 'gggg' AND col2 = 'ggggggggggggggggggg'; SELECT * FROM federated.t1 WHERE col1 = 'gggg' AND col2 = 'ggggggggggggggggggg'; -EXPLAIN SELECT * FROM federated.t1 WHERE col1 = 'gggg' AND col3 = 'gagagagaga'; SELECT * FROM federated.t1 WHERE col1 = 'gggg' AND col3 = 'gagagagaga'; -EXPLAIN SELECT * FROM federated.t1 WHERE col1 = 'ffff' AND col4 = 'fcfcfcfcfcfcfcfc'; SELECT * FROM federated.t1 WHERE col1 = 'ffff' AND col4 = 'fcfcfcfcfcfcfcfc'; -EXPLAIN SELECT * FROM federated.t1 WHERE col1 > 'bbbb'; SELECT * FROM federated.t1 WHERE col1 > 'bbbb'; -EXPLAIN SELECT * FROM federated.t1 WHERE col1 >= 'bbbb'; SELECT * FROM federated.t1 WHERE col1 >= 'bbbb'; -EXPLAIN SELECT * FROM federated.t1 WHERE col1 < 'bbbb'; SELECT * FROM federated.t1 WHERE col1 < 'bbbb'; -EXPLAIN SELECT * FROM federated.t1 WHERE col1 <= 'bbbb'; SELECT * FROM federated.t1 WHERE col1 <= 'bbbb'; -EXPLAIN SELECT * FROM federated.t1 WHERE col1 <> 'bbbb'; SELECT * FROM federated.t1 WHERE col1 <> 'bbbb'; -EXPLAIN SELECT * FROM federated.t1 WHERE col1 LIKE 'b%'; SELECT * FROM federated.t1 WHERE col1 LIKE 'b%'; -EXPLAIN SELECT * FROM federated.t1 WHERE col4 LIKE '%b%'; SELECT * FROM federated.t1 WHERE col4 LIKE '%b%'; -EXPLAIN SELECT * FROM federated.t1 WHERE col1 NOT LIKE 'c%'; SELECT * FROM federated.t1 WHERE col1 NOT LIKE 'c%'; -EXPLAIN SELECT * FROM federated.t1 WHERE col4 NOT LIKE '%c%'; SELECT * FROM federated.t1 WHERE col4 NOT LIKE '%c%'; connection slave; DROP TABLE IF EXISTS federated.t1; @@ -660,17 +577,11 @@ INSERT INTO federated.t1 VALUES ('ccd', '112', 'zzzz'); # let's see what the foreign database says connection slave; -EXPLAIN SELECT col3 FROM federated.t1 WHERE ( -(col1 = 'aaa' AND col2 >= '111') OR col1 > 'aaa') AND -(col1 < 'ccc' OR ( col1 = 'ccc' AND col2 <= '111')); SELECT col3 FROM federated.t1 WHERE ( (col1 = 'aaa' AND col2 >= '111') OR col1 > 'aaa') AND (col1 < 'ccc' OR ( col1 = 'ccc' AND col2 <= '111')); connection master; -EXPLAIN SELECT col3 FROM federated.t1 WHERE ( -(col1 = 'aaa' AND col2 >= '111') OR col1 > 'aaa') AND -(col1 < 'ccc' OR ( col1 = 'ccc' AND col2 <= '111')); SELECT col3 FROM federated.t1 WHERE ( (col1 = 'aaa' AND col2 >= '111') OR col1 > 'aaa') AND (col1 < 'ccc' OR ( col1 = 'ccc' AND col2 <= '111')); @@ -711,11 +622,6 @@ WHERE id IS NULL AND name IS NULL AND floatval IS NULL AND other IS NULL; -EXPLAIN SELECT count(*) FROM federated.t1 -WHERE id IS NULL -AND name IS NULL -AND floatval IS NULL -AND other IS NULL; connection slave; DROP TABLE IF EXISTS federated.t1; @@ -764,14 +670,6 @@ eval CREATE TABLE federated.t1 ( COMMENT='mysql://root@127.0.0.1:$SLAVE_MYPORT/federated/t1'; INSERT INTO federated.t1 VALUES (3,3,3),(1,1,1),(2,2,2),(4,4,4); -EXPLAIN SELECT * FROM federated.t1 ORDER BY a; -EXPLAIN SELECT * FROM federated.t1 ORDER BY b; -EXPLAIN SELECT * FROM federated.t1 ORDER BY c; -EXPLAIN SELECT a FROM federated.t1 ORDER BY a; -EXPLAIN SELECT b FROM federated.t1 ORDER BY b; -EXPLAIN SELECT a,b FROM federated.t1 ORDER BY b; -EXPLAIN SELECT a,b FROM federated.t1; -EXPLAIN SELECT a,b,c FROM federated.t1; connection slave; DROP TABLE IF EXISTS federated.t1; @@ -1077,7 +975,6 @@ values (1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, "PatrickG"); UPDATE federated.t1 SET b=repeat('a',256); UPDATE federated.t1 SET i1=0, i2=0, i3=0, i4=0, i5=0, i6=0, i7=0, i8=0, i9=0, i10=0; -EXPLAIN SELECT * FROM federated.t1 WHERE i9=0 and i10=0; SELECT * FROM federated.t1 WHERE i9=0 and i10=0; UPDATE federated.t1 SET i50=20; SELECT * FROM federated.t1; @@ -1205,32 +1102,16 @@ INSERT INTO federated.t1 (name, country_id, other) VALUES ('Monty', 4, 33333); INSERT INTO federated.t1 (name, country_id, other) VALUES ('Sanja', 5, 33333); #inner join -EXPLAIN SELECT federated.t1.name AS name, federated.t1.country_id AS country_id, -federated.t1.other AS other, federated.countries.country AS country -FROM federated.t1, federated.countries WHERE -federated.t1.country_id = federated.countries.id; - SELECT federated.t1.name AS name, federated.t1.country_id AS country_id, federated.t1.other AS other, federated.countries.country AS country FROM federated.t1, federated.countries WHERE federated.t1.country_id = federated.countries.id; -EXPLAIN SELECT federated.t1.name AS name, federated.t1.country_id AS country_id, -federated.t1.other AS other, federated.countries.country AS country -FROM federated.t1 INNER JOIN federated.countries ON -federated.t1.country_id = federated.countries.id; - SELECT federated.t1.name AS name, federated.t1.country_id AS country_id, federated.t1.other AS other, federated.countries.country AS country FROM federated.t1 INNER JOIN federated.countries ON federated.t1.country_id = federated.countries.id; -EXPLAIN SELECT federated.t1.name AS name, federated.t1.country_id AS country_id, -federated.t1.other AS other, federated.countries.country AS country -FROM federated.t1 INNER JOIN federated.countries ON -federated.t1.country_id = federated.countries.id -WHERE federated.t1.name = 'Monty'; - SELECT federated.t1.name AS name, federated.t1.country_id AS country_id, federated.t1.other AS other, federated.countries.country AS country FROM federated.t1 INNER JOIN federated.countries ON @@ -1238,32 +1119,17 @@ federated.t1.country_id = federated.countries.id WHERE federated.t1.name = 'Monty'; #left join -EXPLAIN SELECT federated.t1.*, federated.countries.country -FROM federated.t1 LEFT JOIN federated.countries -ON federated.t1.country_id = federated.countries.id -ORDER BY federated.countries.id; - SELECT federated.t1.*, federated.countries.country FROM federated.t1 LEFT JOIN federated.countries ON federated.t1.country_id = federated.countries.id ORDER BY federated.countries.id; -EXPLAIN SELECT federated.t1.*, federated.countries.country -FROM federated.t1 LEFT JOIN federated.countries -ON federated.t1.country_id = federated.countries.id -ORDER BY federated.countries.country; - SELECT federated.t1.*, federated.countries.country FROM federated.t1 LEFT JOIN federated.countries ON federated.t1.country_id = federated.countries.id ORDER BY federated.countries.country; #right join -EXPLAIN SELECT federated.t1.*, federated.countries.country -FROM federated.t1 RIGHT JOIN federated.countries -ON federated.t1.country_id = federated.countries.id -ORDER BY federated.t1.country_id; - SELECT federated.t1.*, federated.countries.country FROM federated.t1 RIGHT JOIN federated.countries ON federated.t1.country_id = federated.countries.id @@ -1271,4 +1137,11 @@ ORDER BY federated.t1.country_id; DROP TABLE federated.countries; +# optimize and repair tests +OPTIMIZE TABLE federated.t1; +REPAIR TABLE federated.t1; +REPAIR TABLE federated.t1 QUICK; +REPAIR TABLE federated.t1 EXTENDED; +REPAIR TABLE federated.t1 USE_FRM; + source include/federated_cleanup.inc; diff --git a/sql/ha_federated.cc b/sql/ha_federated.cc index c953ff1d7ed..e0e35c6b866 100644 --- a/sql/ha_federated.cc +++ b/sql/ha_federated.cc @@ -495,7 +495,7 @@ static int check_foreign_data_source( query.append(escaped_table_name); query.append(FEDERATED_BTICK); query.append(FEDERATED_WHERE); - query.append(FEDERATED_1EQ0); + query.append(FEDERATED_FALSE); DBUG_PRINT("info", ("check_foreign_data_source query %s", query.c_ptr_quick())); if (mysql_real_query(mysql, query.ptr(), query.length())) @@ -1661,6 +1661,62 @@ int ha_federated::write_row(byte *buf) } +int ha_federated::optimize(THD* thd, HA_CHECK_OPT* check_opt) +{ + char query_buffer[STRING_BUFFER_USUAL_SIZE]; + String query(query_buffer, sizeof(query_buffer), &my_charset_bin); + + DBUG_ENTER("ha_federated::optimize"); + + query.length(0); + + query.set_charset(system_charset_info); + query.append(FEDERATED_OPTIMIZE); + query.append(FEDERATED_BTICK); + query.append(share->table_name, share->table_name_length); + query.append(FEDERATED_BTICK); + + if (mysql_real_query(mysql, query.ptr(), query.length())) + { + my_error(-1, MYF(0), mysql_error(mysql)); + DBUG_RETURN(-1); + } + + DBUG_RETURN(0); +} + + +int ha_federated::repair(THD* thd, HA_CHECK_OPT* check_opt) +{ + char query_buffer[STRING_BUFFER_USUAL_SIZE]; + String query(query_buffer, sizeof(query_buffer), &my_charset_bin); + + DBUG_ENTER("ha_federated::repair"); + + query.length(0); + + query.set_charset(system_charset_info); + query.append(FEDERATED_REPAIR); + query.append(FEDERATED_BTICK); + query.append(share->table_name, share->table_name_length); + query.append(FEDERATED_BTICK); + if (check_opt->flags & T_QUICK) + query.append(FEDERATED_QUICK); + if (check_opt->flags & T_EXTEND) + query.append(FEDERATED_EXTENDED); + if (check_opt->sql_flags & TT_USEFRM) + query.append(FEDERATED_USE_FRM); + + if (mysql_real_query(mysql, query.ptr(), query.length())) + { + my_error(-1, MYF(0), mysql_error(mysql)); + DBUG_RETURN(-1); + } + + DBUG_RETURN(0); +} + + /* 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 diff --git a/sql/ha_federated.h b/sql/ha_federated.h index a12cf14531f..3e55419f266 100644 --- a/sql/ha_federated.h +++ b/sql/ha_federated.h @@ -31,40 +31,83 @@ #define FEDERATED_RECORDS_IN_RANGE 2 #define FEDERATED_INFO " SHOW TABLE STATUS LIKE " +#define FEDERATED_INFO_LEN sizeof(FEDERATED_INFO) #define FEDERATED_SELECT "SELECT " +#define FEDERATED_SELECT_LEN sizeof(FEDERATED_SELECT) #define FEDERATED_WHERE " WHERE " +#define FEDERATED_WHERE_LEN sizeof(FEDERATED_WHERE) #define FEDERATED_FROM " FROM " +#define FEDERATED_FROM_LEN sizeof(FEDERATED_FROM) #define FEDERATED_PERCENT "%" +#define FEDERATED_PERCENT_LEN sizeof(FEDERATED_PERCENT) #define FEDERATED_IS " IS " +#define FEDERATED_IS_LEN sizeof(FEDERATED_IS) #define FEDERATED_NULL " NULL " +#define FEDERATED_NULL_LEN sizeof(FEDERATED_NULL) #define FEDERATED_ISNULL " IS NULL " +#define FEDERATED_ISNULL_LEN sizeof(FEDERATED_ISNULL) #define FEDERATED_LIKE " LIKE " +#define FEDERATED_LIKE_LEN sizeof(FEDERATED_LIKE) #define FEDERATED_TRUNCATE "TRUNCATE " +#define FEDERATED_TRUNCATE_LEN sizeof(FEDERATED_TRUNCATE) #define FEDERATED_DELETE "DELETE " +#define FEDERATED_DELETE_LEN sizeof(FEDERATED_DELETE) #define FEDERATED_INSERT "INSERT INTO " +#define FEDERATED_INSERT_LEN sizeof(FEDERATED_INSERT) +#define FEDERATED_OPTIMIZE "OPTIMIZE TABLE " +#define FEDERATED_OPTIMIZE_LEN sizeof(FEDERATED_OPTIMIZE) +#define FEDERATED_REPAIR "REPAIR TABLE " +#define FEDERATED_REPAIR_LEN sizeof(FEDERATED_REPAIR) +#define FEDERATED_QUICK " QUICK" +#define FEDERATED_QUICK_LEN sizeof(FEDERATED_QUICK) +#define FEDERATED_EXTENDED " EXTENDED" +#define FEDERATED_EXTENDED_LEN sizeof(FEDERATED_EXTENDED) +#define FEDERATED_USE_FRM " USE_FRM" +#define FEDERATED_USE_FRM_LEN sizeof(FEDERATED_USE_FRM) #define FEDERATED_LIMIT1 " LIMIT 1" +#define FEDERATED_LIMIT1_LEN sizeof(FEDERATED_LIMIT1) #define FEDERATED_VALUES "VALUES " +#define FEDERATED_VALUES_LEN sizeof(FEDERATED_VALUES) #define FEDERATED_UPDATE "UPDATE " +#define FEDERATED_UPDATE_LEN sizeof(FEDERATED_UPDATE) #define FEDERATED_SET "SET " +#define FEDERATED_SET_LEN sizeof(FEDERATED_SET) #define FEDERATED_AND " AND " +#define FEDERATED_AND_LEN sizeof(FEDERATED_AND) #define FEDERATED_CONJUNCTION ") AND (" +#define FEDERATED_CONJUNCTION_LEN sizeof(FEDERATED_CONJUNCTION) #define FEDERATED_OR " OR " +#define FEDERATED_OR_LEN sizeof(FEDERATED_OR) #define FEDERATED_NOT " NOT " +#define FEDERATED_NOT_LEN sizeof(FEDERATED_NOT) #define FEDERATED_STAR "* " +#define FEDERATED_STAR_LEN sizeof(FEDERATED_STAR) #define FEDERATED_SPACE " " +#define FEDERATED_SPACE_LEN sizeof(FEDERATED_SPACE) #define FEDERATED_SQUOTE "'" +#define FEDERATED_SQUOTE_LEN sizeof(FEDERATED_SQUOTE) #define FEDERATED_COMMA ", " -#define FEDERATED_DQOUTE '"' -#define FEDERATED_BTICK "`" +#define FEDERATED_COMMA_LEN sizeof(FEDERATED_COMMA) +#define FEDERATED_BTICK "`" +#define FEDERATED_BTICK_LEN sizeof(FEDERATED_BTICK) #define FEDERATED_OPENPAREN " (" +#define FEDERATED_OPENPAREN_LEN sizeof(FEDERATED_OPENPAREN) #define FEDERATED_CLOSEPAREN ") " +#define FEDERATED_CLOSEPAREN_LEN sizeof(FEDERATED_CLOSEPAREN) #define FEDERATED_NE " != " +#define FEDERATED_NE_LEN sizeof(FEDERATED_NE) #define FEDERATED_GT " > " +#define FEDERATED_GT_LEN sizeof(FEDERATED_GT) #define FEDERATED_LT " < " +#define FEDERATED_LT_LEN sizeof(FEDERATED_LT) #define FEDERATED_LE " <= " +#define FEDERATED_LE_LEN sizeof(FEDERATED_LE) #define FEDERATED_GE " >= " +#define FEDERATED_GE_LEN sizeof(FEDERATED_GE) #define FEDERATED_EQ " = " -#define FEDERATED_1EQ0 " 1=0" +#define FEDERATED_EQ_LEN sizeof(FEDERATED_EQ) +#define FEDERATED_FALSE " 1=0" +#define FEDERATED_FALSE_LEN sizeof(FEDERATED_FALSE) /* FEDERATED_SHARE is a structure that will be shared amoung all open handlers @@ -88,7 +131,7 @@ typedef struct st_federated_share { char *socket; char *sport; ushort port; - uint table_name_length,use_count; + uint table_name_length, use_count; pthread_mutex_t mutex; THR_LOCK lock; } FEDERATED_SHARE; @@ -235,6 +278,9 @@ public: void position(const byte *record); //required void info(uint); //required + int repair(THD* thd, HA_CHECK_OPT* check_opt); + int optimize(THD* thd, HA_CHECK_OPT* check_opt); + int delete_all_rows(void); int create(const char *name, TABLE *form, HA_CREATE_INFO *create_info); //required From d2814fb1b509076561643d0c8ae6b847343588d5 Mon Sep 17 00:00:00 2001 From: "konstantin@mysql.com" <> Date: Tue, 19 Jul 2005 11:35:16 +0400 Subject: [PATCH 41/48] Added mkinstalldirs (an auxiliary autotools symlink) to the ignore list. --- .bzrignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.bzrignore b/.bzrignore index 9c6cf2c965c..ca4cade7f09 100644 --- a/.bzrignore +++ b/.bzrignore @@ -1117,3 +1117,5 @@ vio/test-sslclient vio/test-sslserver vio/viotest-ssl ndb/src/dummy.cpp +innobase/mkinstalldirs +mkinstalldirs From 2da0bc8bde04e5d885ad2bde1a07ef79e494fd98 Mon Sep 17 00:00:00 2001 From: "konstantin@mysql.com" <> Date: Tue, 19 Jul 2005 11:59:06 +0400 Subject: [PATCH 42/48] Remove the ignored files. From d59ff2764c3258a18c8555a761aedfc82ada270f Mon Sep 17 00:00:00 2001 From: "gluh@eagle.intranet.mysql.r18.ru" <> Date: Tue, 19 Jul 2005 13:49:43 +0500 Subject: [PATCH 43/48] Bug#11577 'show procedure/function status' doesn't work for user with limited access changed 'SHOW PROCEDURE' behavoiur to be like INFORMATION_SCHEMA.routines --- mysql-test/r/information_schema.result | 3 +++ mysql-test/t/information_schema.test | 2 ++ sql/sql_parse.cc | 10 ++++++---- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result index 98f3d59485f..a0ad743ac2a 100644 --- a/mysql-test/r/information_schema.result +++ b/mysql-test/r/information_schema.result @@ -298,6 +298,9 @@ show create function sub2; Function sql_mode Create Function sub2 CREATE FUNCTION `test`.`sub2`(i int) RETURNS int(11) return i+1 +show function status like "sub2"; +Db Name Type Definer Modified Created Security_type Comment +test sub2 FUNCTION mysqltest_1@localhost # # DEFINER drop function sub2; show create procedure sel2; Procedure sql_mode Create Procedure diff --git a/mysql-test/t/information_schema.test b/mysql-test/t/information_schema.test index 7c0624b67fd..0ac68c37d15 100644 --- a/mysql-test/t/information_schema.test +++ b/mysql-test/t/information_schema.test @@ -157,6 +157,8 @@ select ROUTINE_NAME, ROUTINE_DEFINITION from information_schema.ROUTINES; show create procedure sel2; show create function sub1; show create function sub2; +--replace_column 5 # 6 # +show function status like "sub2"; connection default; disconnect user1; drop function sub2; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index d8c7507fd35..11d71d5603b 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2386,10 +2386,12 @@ mysql_execute_command(THD *thd) select_result *result=lex->result; if (all_tables) { - res= check_table_access(thd, - lex->exchange ? SELECT_ACL | FILE_ACL : - SELECT_ACL, - all_tables, 0); + if (lex->orig_sql_command != SQLCOM_SHOW_STATUS_PROC && + lex->orig_sql_command != SQLCOM_SHOW_STATUS_FUNC) + res= check_table_access(thd, + lex->exchange ? SELECT_ACL | FILE_ACL : + SELECT_ACL, + all_tables, 0); } else res= check_access(thd, From d1bc1b7c0e317f73e309697f8e7baebb77b4ca59 Mon Sep 17 00:00:00 2001 From: "gluh@eagle.intranet.mysql.r18.ru" <> Date: Tue, 19 Jul 2005 14:30:32 +0500 Subject: [PATCH 44/48] Fix for bug#11057 information_schema: columns table has some questionable contents fixed BLOB, TEXT(wrong maximum length), BIT and integer types(wrong numeric_precision value) --- mysql-test/r/information_schema.result | 27 ++++++++++++++++++++++++-- mysql-test/t/information_schema.test | 16 +++++++++++++++ sql/sql_show.cc | 22 +++++++++++++++++---- 3 files changed, 59 insertions(+), 6 deletions(-) diff --git a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result index a0ad743ac2a..2b84dee18c1 100644 --- a/mysql-test/r/information_schema.result +++ b/mysql-test/r/information_schema.result @@ -153,7 +153,7 @@ c varchar(64) utf8_general_ci NO select,insert,update,references select * from information_schema.COLUMNS where table_name="t1" and column_name= "a"; TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME ORDINAL_POSITION COLUMN_DEFAULT IS_NULLABLE DATA_TYPE CHARACTER_MAXIMUM_LENGTH CHARACTER_OCTET_LENGTH NUMERIC_PRECISION NUMERIC_SCALE CHARACTER_SET_NAME COLLATION_NAME COLUMN_TYPE COLUMN_KEY EXTRA PRIVILEGES COLUMN_COMMENT -NULL mysqltest t1 a 1 NULL YES int NULL NULL 11 0 NULL NULL int(11) select,insert,update,references +NULL mysqltest t1 a 1 NULL YES int NULL NULL 10 NULL NULL NULL int(11) select,insert,update,references show columns from mysqltest.t1 where field like "%a%"; Field Type Null Key Default Extra a int(11) YES NULL @@ -523,7 +523,7 @@ c float(5,2) NULL NULL 5 2 d decimal(6,4) NULL NULL 6 4 e float NULL NULL 12 NULL f decimal(6,3) NULL NULL 6 3 -g int(11) NULL NULL 11 0 +g int(11) NULL NULL 10 NULL h double(10,3) NULL NULL 10 3 i double NULL NULL 22 NULL drop table t1; @@ -844,3 +844,26 @@ drop procedure p2; show create database information_schema; Database Create Database information_schema CREATE DATABASE `information_schema` /*!40100 DEFAULT CHARACTER SET utf8 */ +create table t1(f1 LONGBLOB, f2 LONGTEXT); +select column_name,data_type,CHARACTER_OCTET_LENGTH, +CHARACTER_MAXIMUM_LENGTH +from information_schema.columns +where table_name='t1'; +column_name data_type CHARACTER_OCTET_LENGTH CHARACTER_MAXIMUM_LENGTH +f1 longblob 4294967295 4294967295 +f2 longtext 4294967295 4294967295 +drop table t1; +create table t1(f1 tinyint, f2 SMALLINT, f3 mediumint, f4 int, +f5 BIGINT, f6 BIT, f7 bit(64)); +select column_name, NUMERIC_PRECISION, NUMERIC_SCALE +from information_schema.columns +where table_name='t1'; +column_name NUMERIC_PRECISION NUMERIC_SCALE +f1 3 NULL +f2 5 NULL +f3 7 NULL +f4 10 NULL +f5 19 NULL +f6 1 NULL +f7 64 NULL +drop table t1; diff --git a/mysql-test/t/information_schema.test b/mysql-test/t/information_schema.test index 0ac68c37d15..44c69e3c143 100644 --- a/mysql-test/t/information_schema.test +++ b/mysql-test/t/information_schema.test @@ -571,3 +571,19 @@ drop procedure p2; # Bug #9434 SHOW CREATE DATABASE information_schema; # show create database information_schema; + +# +# Bug #11057 information_schema: columns table has some questionable contents +# +create table t1(f1 LONGBLOB, f2 LONGTEXT); +select column_name,data_type,CHARACTER_OCTET_LENGTH, + CHARACTER_MAXIMUM_LENGTH +from information_schema.columns +where table_name='t1'; +drop table t1; +create table t1(f1 tinyint, f2 SMALLINT, f3 mediumint, f4 int, + f5 BIGINT, f6 BIT, f7 bit(64)); +select column_name, NUMERIC_PRECISION, NUMERIC_SCALE +from information_schema.columns +where table_name='t1'; +drop table t1; diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 1608999eaef..80e6bbdf388 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2377,6 +2377,7 @@ static int get_schema_column_record(THD *thd, struct st_table_list *tables, { const char *tmp_buff; byte *pos; + bool is_blob; uint flags=field->flags; char tmp[MAX_FIELD_WIDTH]; char tmp1[MAX_FIELD_WIDTH]; @@ -2455,12 +2456,14 @@ static int get_schema_column_record(THD *thd, struct st_table_list *tables, "NO" : "YES"); table->field[6]->store((const char*) pos, strlen((const char*) pos), cs); - if (field->has_charset()) + is_blob= (field->type() == FIELD_TYPE_BLOB); + if (field->has_charset() || is_blob) { - table->field[8]->store((longlong) field->field_length/ - field->charset()->mbmaxlen); + longlong c_octet_len= is_blob ? (longlong) field->max_length() : + (longlong) field->max_length()/field->charset()->mbmaxlen; + table->field[8]->store(c_octet_len); table->field[8]->set_notnull(); - table->field[9]->store((longlong) field->field_length); + table->field[9]->store((longlong) field->max_length()); table->field[9]->set_notnull(); } @@ -2488,6 +2491,17 @@ static int get_schema_column_record(THD *thd, struct st_table_list *tables, case FIELD_TYPE_LONG: case FIELD_TYPE_LONGLONG: case FIELD_TYPE_INT24: + { + table->field[10]->store((longlong) field->max_length() - 1); + table->field[10]->set_notnull(); + break; + } + case FIELD_TYPE_BIT: + { + table->field[10]->store((longlong) field->max_length()); + table->field[10]->set_notnull(); + break; + } case FIELD_TYPE_FLOAT: case FIELD_TYPE_DOUBLE: { From ec15dda2bfa246d71a257da2d25c3d398b4d4cc4 Mon Sep 17 00:00:00 2001 From: "ingo@mysql.com" <> Date: Tue, 19 Jul 2005 14:13:56 +0200 Subject: [PATCH 45/48] Bug#10932 - Building server with key limit of 128, makes test cases fail This patch allows to configure MyISAM for 128 indexes per table. The main problem is the key_map, wich is implemented as an ulonglong. To get rid of the limit and keep the efficient and flexible implementation, the highest bit is now used for all upper keys. This means that the lower keys can be disabled and enabled individually as usual and the high keys can only be disabled and enabled as a block. That way the existing test suite is still applicable, while more keys work, though slightly less efficient. To really get more than 64 keys, some defines need to be changed. Another patch will address this. --- include/my_bitmap.h | 1 + include/myisam.h | 81 ++++++++++++++++++++++++++++++++++++++++---- myisam/mi_check.c | 33 +++++++++--------- myisam/mi_create.c | 2 +- myisam/mi_delete.c | 2 +- myisam/mi_extra.c | 4 +-- myisam/mi_open.c | 16 ++++----- myisam/mi_preload.c | 2 +- myisam/mi_rsame.c | 2 +- myisam/mi_rsamepos.c | 2 +- myisam/mi_search.c | 2 +- myisam/mi_update.c | 2 +- myisam/mi_write.c | 15 ++++---- myisam/myisamchk.c | 16 ++++----- myisam/myisamdef.h | 4 +-- myisam/myisamlog.c | 2 +- myisam/myisampack.c | 12 +++---- myisam/sort.c | 2 +- mysys/my_bitmap.c | 31 +++++++++++++++++ sql/ha_myisam.cc | 10 +++--- sql/sql_bitmap.h | 9 +++++ 21 files changed, 180 insertions(+), 70 deletions(-) diff --git a/include/my_bitmap.h b/include/my_bitmap.h index 84a5d8bd18a..f4fe28266e4 100644 --- a/include/my_bitmap.h +++ b/include/my_bitmap.h @@ -54,6 +54,7 @@ extern void bitmap_clear_all(MY_BITMAP *map); extern void bitmap_clear_bit(MY_BITMAP *map, uint bitmap_bit); extern void bitmap_free(MY_BITMAP *map); extern void bitmap_intersect(MY_BITMAP *map, const MY_BITMAP *map2); +extern void bitmap_set_above(MY_BITMAP *map, uint from_byte, uint use_bit); extern void bitmap_set_all(MY_BITMAP *map); extern void bitmap_set_bit(MY_BITMAP *map, uint bitmap_bit); extern void bitmap_set_prefix(MY_BITMAP *map, uint prefix_size); diff --git a/include/myisam.h b/include/myisam.h index 7d3f0e0c801..03194fe42ae 100644 --- a/include/myisam.h +++ b/include/myisam.h @@ -35,14 +35,26 @@ extern "C" { /* defines used by myisam-funktions */ -/* The following defines can be increased if necessary */ -#define MI_MAX_KEY 64 /* Max allowed keys */ -#define MI_MAX_KEY_SEG 16 /* Max segments for key */ -#define MI_MAX_KEY_LENGTH 1000 +/* + There is a hard limit for the maximum number of keys as there are only + 8 bits in the index file header for the number of keys in a table. + This means that 0..255 keys can exist for a table. The idea of + MI_MAX_POSSIBLE_KEY is to ensure that one can use myisamchk & tools on + a MyISAM table for which one has more keys than MyISAM is normally + compiled for. If you don't have this, you will get a core dump when + running myisamchk compiled for 128 keys on a table with 255 keys. +*/ +#define MI_MAX_POSSIBLE_KEY 255 /* For myisam_chk */ +#define MI_MAX_POSSIBLE_KEY_BUFF (1024+6+6) /* For myisam_chk */ +/* + The following defines can be increased if necessary. + BUT: MI_MAX_KEY must be <= MI_MAX_POSSIBLE_KEY. +*/ +#define MI_MAX_KEY 64 /* Max allowed keys */ +#define MI_MAX_KEY_SEG 16 /* Max segments for key */ +#define MI_MAX_KEY_LENGTH 1000 #define MI_MAX_KEY_BUFF (MI_MAX_KEY_LENGTH+MI_MAX_KEY_SEG*6+8+8) -#define MI_MAX_POSSIBLE_KEY_BUFF (1024+6+6) /* For myisam_chk */ -#define MI_MAX_POSSIBLE_KEY 64 /* For myisam_chk */ #define MI_MAX_MSG_BUF 1024 /* used in CHECK TABLE, REPAIR TABLE */ #define MI_NAME_IEXT ".MYI" #define MI_NAME_DEXT ".MYD" @@ -56,6 +68,63 @@ extern "C" { #define mi_portable_sizeof_char_ptr 8 +/* + In the following macros '_keyno_' is 0 .. keys-1. + If there can be more keys than bits in the key_map, the highest bit + is for all upper keys. They cannot be switched individually. + This means that clearing of high keys is ignored, setting one high key + sets all high keys. +*/ +#define MI_KEYMAP_BITS (8 * SIZEOF_LONG_LONG) +#define MI_KEYMAP_HIGH_MASK (ULL(1) << (MI_KEYMAP_BITS - 1)) +#define mi_get_mask_all_keys_active(_keys_) \ + (((_keys_) < MI_KEYMAP_BITS) ? \ + ((ULL(1) << (_keys_)) - ULL(1)) : \ + (~ ULL(0))) + +#if MI_MAX_KEY > MI_KEYMAP_BITS + +#define mi_is_key_active(_keymap_,_keyno_) \ + (((_keyno_) < MI_KEYMAP_BITS) ? \ + test((_keymap_) & (ULL(1) << (_keyno_))) : \ + test((_keymap_) & MI_KEYMAP_HIGH_MASK)) +#define mi_set_key_active(_keymap_,_keyno_) \ + (_keymap_)|= (((_keyno_) < MI_KEYMAP_BITS) ? \ + (ULL(1) << (_keyno_)) : \ + MI_KEYMAP_HIGH_MASK) +#define mi_clear_key_active(_keymap_,_keyno_) \ + (_keymap_)&= (((_keyno_) < MI_KEYMAP_BITS) ? \ + (~ (ULL(1) << (_keyno_))) : \ + (~ (ULL(0))) /*ignore*/ ) + +#else + +#define mi_is_key_active(_keymap_,_keyno_) \ + test((_keymap_) & (ULL(1) << (_keyno_))) +#define mi_set_key_active(_keymap_,_keyno_) \ + (_keymap_)|= (ULL(1) << (_keyno_)) +#define mi_clear_key_active(_keymap_,_keyno_) \ + (_keymap_)&= (~ (ULL(1) << (_keyno_))) + +#endif + +#define mi_is_any_key_active(_keymap_) \ + test((_keymap_)) +#define mi_is_all_keys_active(_keymap_,_keys_) \ + ((_keymap_) == mi_get_mask_all_keys_active(_keys_)) +#define mi_set_all_keys_active(_keymap_,_keys_) \ + (_keymap_)= mi_get_mask_all_keys_active(_keys_) +#define mi_clear_all_keys_active(_keymap_) \ + (_keymap_)= 0 +#define mi_intersect_keys_active(_to_,_from_) \ + (_to_)&= (_from_) +#define mi_is_any_intersect_keys_active(_keymap1_,_keys_,_keymap2_) \ + ((_keymap1_) & (_keymap2_) & \ + mi_get_mask_all_keys_active(_keys_)) +#define mi_copy_keys_active(_to_,_maxkeys_,_from_) \ + (_to_)= (mi_get_mask_all_keys_active(_maxkeys_) & \ + (_from_)) + /* Param to/from mi_info */ typedef struct st_mi_isaminfo /* Struct from h_info */ diff --git a/myisam/mi_check.c b/myisam/mi_check.c index 9e003a18dac..1db829808a9 100644 --- a/myisam/mi_check.c +++ b/myisam/mi_check.c @@ -282,7 +282,7 @@ int chk_size(MI_CHECK *param, register MI_INFO *info) if ((skr=(my_off_t) info->state->key_file_length) != size) { /* Don't give error if file generated by myisampack */ - if (skr > size && info->s->state.key_map) + if (skr > size && mi_is_any_key_active(info->s->state.key_map)) { error=1; mi_check_print_error(param, @@ -379,7 +379,7 @@ int chk_key(MI_CHECK *param, register MI_INFO *info) rec_per_key_part+=keyinfo->keysegs, key++, keyinfo++) { param->key_crc[key]=0; - if (!(((ulonglong) 1 << key) & share->state.key_map)) + if (! mi_is_key_active(share->state.key_map, key)) { /* Remember old statistics for key */ memcpy((char*) rec_per_key_part, @@ -507,7 +507,7 @@ int chk_key(MI_CHECK *param, register MI_INFO *info) (int) ((my_off_t2double(key_totlength) - my_off_t2double(all_keydata))*100.0/ my_off_t2double(key_totlength))); - else if (all_totaldata != 0L && share->state.key_map) + else if (all_totaldata != 0L && mi_is_any_key_active(share->state.key_map)) puts(""); } if (param->key_file_blocks != info->state->key_file_length && @@ -1034,7 +1034,7 @@ int chk_data_link(MI_CHECK *param, MI_INFO *info,int extend) for (key=0,keyinfo= info->s->keyinfo; key < info->s->base.keys; key++,keyinfo++) { - if ((((ulonglong) 1 << key) & info->s->state.key_map)) + if (mi_is_key_active(info->s->state.key_map, key)) { if(!(keyinfo->flag & HA_FULLTEXT)) { @@ -1298,8 +1298,8 @@ int mi_repair(MI_CHECK *param, register MI_INFO *info, */ if (param->testflag & T_CREATE_MISSING_KEYS) - share->state.key_map= ((((ulonglong) 1L << share->base.keys)-1) & - param->keys_in_use); + mi_copy_keys_active(share->state.key_map, share->base.keys, + param->keys_in_use); info->state->key_file_length=share->base.keystart; @@ -1461,7 +1461,7 @@ static int writekeys(MI_CHECK *param, register MI_INFO *info, byte *buff, key=info->lastkey+info->s->base.max_key_length; for (i=0 ; i < info->s->base.keys ; i++) { - if (((ulonglong) 1 << i) & info->s->state.key_map) + if (mi_is_key_active(info->s->state.key_map, i)) { if (info->s->keyinfo[i].flag & HA_FULLTEXT ) { @@ -1492,7 +1492,7 @@ static int writekeys(MI_CHECK *param, register MI_INFO *info, byte *buff, info->errkey=(int) i; /* This key was found */ while ( i-- > 0 ) { - if (((ulonglong) 1 << i) & info->s->state.key_map) + if (mi_is_key_active(info->s->state.key_map, i)) { if (info->s->keyinfo[i].flag & HA_FULLTEXT) { @@ -1529,7 +1529,7 @@ int movepoint(register MI_INFO *info, byte *record, my_off_t oldpos, key=info->lastkey+info->s->base.max_key_length; for (i=0 ; i < info->s->base.keys; i++) { - if (i != prot_key && (((ulonglong) 1 << i) & info->s->state.key_map)) + if (i != prot_key && mi_is_key_active(info->s->state.key_map, i)) { key_length=_mi_make_key(info,i,key,record,oldpos); if (info->s->keyinfo[i].flag & HA_NOSAME) @@ -1628,7 +1628,7 @@ int mi_sort_index(MI_CHECK *param, register MI_INFO *info, my_string name) for (key= 0,keyinfo= &share->keyinfo[0]; key < share->base.keys ; key++,keyinfo++) { - if (!(((ulonglong) 1 << key) & share->state.key_map)) + if (! mi_is_key_active(info->s->state.key_map, key)) continue; if (share->state.key_root[key] != HA_OFFSET_ERROR) @@ -2023,7 +2023,7 @@ int mi_repair_by_sort(MI_CHECK *param, register MI_INFO *info, sort_param.read_cache=param->read_cache; sort_param.keyinfo=share->keyinfo+sort_param.key; sort_param.seg=sort_param.keyinfo->seg; - if (!(((ulonglong) 1 << sort_param.key) & key_map)) + if (! mi_is_key_active(key_map, sort_param.key)) { /* Remember old statistics for key */ memcpy((char*) rec_per_key_part, @@ -2084,7 +2084,7 @@ int mi_repair_by_sort(MI_CHECK *param, register MI_INFO *info, if (param->testflag & T_STATISTICS) update_key_parts(sort_param.keyinfo, rec_per_key_part, sort_param.unique, (ulonglong) info->state->records); - share->state.key_map|=(ulonglong) 1 << sort_param.key; + mi_set_key_active(share->state.key_map, sort_param.key); if (sort_param.fix_datafile) { @@ -2405,7 +2405,7 @@ int mi_repair_parallel(MI_CHECK *param, register MI_INFO *info, sort_param[i].key=key; sort_param[i].keyinfo=share->keyinfo+key; sort_param[i].seg=sort_param[i].keyinfo->seg; - if (!(((ulonglong) 1 << key) & key_map)) + if (! mi_is_key_active(key_map, key)) { /* Remember old statistics for key */ memcpy((char*) rec_per_key_part, @@ -3917,8 +3917,7 @@ void update_auto_increment_key(MI_CHECK *param, MI_INFO *info, { byte *record; if (!info->s->base.auto_key || - !(((ulonglong) 1 << (info->s->base.auto_key-1) - & info->s->state.key_map))) + ! mi_is_key_active(info->s->state.key_map, info->s->base.auto_key - 1)) { if (!(param->testflag & T_VERY_SILENT)) mi_check_print_info(param, @@ -4070,7 +4069,7 @@ void mi_disable_non_unique_index(MI_INFO *info, ha_rows rows) if (!(key->flag & (HA_NOSAME | HA_SPATIAL | HA_AUTO_KEY)) && ! mi_too_big_key_for_sort(key,rows) && info->s->base.auto_key != i+1) { - share->state.key_map&= ~ ((ulonglong) 1 << i); + mi_clear_key_active(share->state.key_map, i); info->update|= HA_STATE_CHANGED; } } @@ -4094,7 +4093,7 @@ my_bool mi_test_if_sort_rep(MI_INFO *info, ha_rows rows, mi_repair_by_sort only works if we have at least one key. If we don't have any keys, we should use the normal repair. */ - if (!key_map) + if (! mi_is_any_key_active(key_map)) return FALSE; /* Can't use sort */ for (i=0 ; i < share->base.keys ; i++,key++) { diff --git a/myisam/mi_create.c b/myisam/mi_create.c index 33b344405ec..560535f2933 100644 --- a/myisam/mi_create.c +++ b/myisam/mi_create.c @@ -508,7 +508,7 @@ int mi_create(const char *name,uint keys,MI_KEYDEF *keydefs, mi_int2store(share.state.header.key_parts,key_segs); mi_int2store(share.state.header.unique_key_parts,unique_key_parts); - share.state.key_map = ((ulonglong) 1 << keys)-1; + mi_set_all_keys_active(share.state.key_map, keys); share.base.keystart = share.state.state.key_file_length= MY_ALIGN(info_length, myisam_block_size); share.base.max_key_block_length=max_key_block_length; diff --git a/myisam/mi_delete.c b/myisam/mi_delete.c index cc4a17182f7..60a07254e82 100644 --- a/myisam/mi_delete.c +++ b/myisam/mi_delete.c @@ -74,7 +74,7 @@ int mi_delete(MI_INFO *info,const byte *record) old_key=info->lastkey2; for (i=0 ; i < share->base.keys ; i++ ) { - if (((ulonglong) 1 << i) & info->s->state.key_map) + if (mi_is_key_active(info->s->state.key_map, i)) { info->s->keyinfo[i].version++; if (info->s->keyinfo[i].flag & HA_FULLTEXT ) diff --git a/myisam/mi_extra.c b/myisam/mi_extra.c index ba32bb9115a..bfe1748af01 100644 --- a/myisam/mi_extra.c +++ b/myisam/mi_extra.c @@ -244,7 +244,7 @@ int mi_extra(MI_INFO *info, enum ha_extra_function function, void *extra_arg) error=1; /* Not possibly if not lock */ break; } - if (share->state.key_map) + if (mi_is_any_key_active(share->state.key_map)) { MI_KEYDEF *key=share->keyinfo; uint i; @@ -252,7 +252,7 @@ int mi_extra(MI_INFO *info, enum ha_extra_function function, void *extra_arg) { if (!(key->flag & HA_NOSAME) && info->s->base.auto_key != i+1) { - share->state.key_map&= ~ ((ulonglong) 1 << i); + mi_clear_key_active(share->state.key_map, i); info->update|= HA_STATE_CHANGED; } } diff --git a/myisam/mi_open.c b/myisam/mi_open.c index 74b97a65609..82663e0c318 100644 --- a/myisam/mi_open.c +++ b/myisam/mi_open.c @@ -192,14 +192,14 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags) } share->state_diff_length=len-MI_STATE_INFO_SIZE; - mi_state_info_read(disk_cache, &share->state); + mi_state_info_read((uchar*) disk_cache, &share->state); len= mi_uint2korr(share->state.header.base_info_length); if (len != MI_BASE_INFO_SIZE) { DBUG_PRINT("warning",("saved_base_info_length: %d base_info_length: %d", len,MI_BASE_INFO_SIZE)) } - disk_pos=my_n_base_info_read(disk_cache+base_pos, &share->base); + disk_pos=my_n_base_info_read((uchar*) disk_cache + base_pos, &share->base); share->state.state_length=base_pos; if (!(open_flags & HA_OPEN_FOR_REPAIR) && @@ -863,7 +863,7 @@ uint mi_state_info_write(File file, MI_STATE_INFO *state, uint pWrite) } -char *mi_state_info_read(char *ptr, MI_STATE_INFO *state) +char *mi_state_info_read(uchar *ptr, MI_STATE_INFO *state) { uint i,keys,key_parts,key_blocks; memcpy_fixed(&state->header,ptr, sizeof(state->header)); @@ -929,7 +929,7 @@ uint mi_state_info_read_dsk(File file, MI_STATE_INFO *state, my_bool pRead) } else if (my_read(file, buff, state->state_length,MYF(MY_NABP))) return (MY_FILE_ERROR); - mi_state_info_read(buff, state); + mi_state_info_read((uchar*) buff, state); } return 0; } @@ -974,7 +974,7 @@ uint mi_base_info_write(File file, MI_BASE_INFO *base) } -char *my_n_base_info_read(char *ptr, MI_BASE_INFO *base) +char *my_n_base_info_read(uchar *ptr, MI_BASE_INFO *base) { base->keystart = mi_sizekorr(ptr); ptr +=8; base->max_data_file_length = mi_sizekorr(ptr); ptr +=8; @@ -1204,7 +1204,7 @@ int mi_disable_indexes(MI_INFO *info) { MYISAM_SHARE *share= info->s; - share->state.key_map= 0; + mi_clear_all_keys_active(share->state.key_map); return 0; } @@ -1240,7 +1240,7 @@ int mi_enable_indexes(MI_INFO *info) error= HA_ERR_CRASHED; } else - share->state.key_map= ((ulonglong) 1L << share->base.keys) - 1; + mi_set_all_keys_active(share->state.key_map, share->base.keys); return error; } @@ -1265,6 +1265,6 @@ int mi_indexes_are_disabled(MI_INFO *info) { MYISAM_SHARE *share= info->s; - return (! share->state.key_map && share->base.keys); + return (! mi_is_any_key_active(share->state.key_map) && share->base.keys); } diff --git a/myisam/mi_preload.c b/myisam/mi_preload.c index 317ab4ad7fe..d63399b519d 100644 --- a/myisam/mi_preload.c +++ b/myisam/mi_preload.c @@ -51,7 +51,7 @@ int mi_preload(MI_INFO *info, ulonglong key_map, my_bool ignore_leaves) my_off_t pos= share->base.keystart; DBUG_ENTER("mi_preload"); - if (!keys || !key_map || key_file_length == pos) + if (!keys || !mi_is_any_key_active(key_map) || key_file_length == pos) DBUG_RETURN(0); block_length= keyinfo[0].block_length; diff --git a/myisam/mi_rsame.c b/myisam/mi_rsame.c index 56c8d1226ca..321097744b9 100644 --- a/myisam/mi_rsame.c +++ b/myisam/mi_rsame.c @@ -30,7 +30,7 @@ int mi_rsame(MI_INFO *info, byte *record, int inx) { DBUG_ENTER("mi_rsame"); - if (inx != -1 && ! (((ulonglong) 1 << inx) & info->s->state.key_map)) + if (inx != -1 && ! mi_is_key_active(info->s->state.key_map, inx)) { DBUG_RETURN(my_errno=HA_ERR_WRONG_INDEX); } diff --git a/myisam/mi_rsamepos.c b/myisam/mi_rsamepos.c index a1d96fb7104..35cdd41e297 100644 --- a/myisam/mi_rsamepos.c +++ b/myisam/mi_rsamepos.c @@ -32,7 +32,7 @@ int mi_rsame_with_pos(MI_INFO *info, byte *record, int inx, my_off_t filepos) { DBUG_ENTER("mi_rsame_with_pos"); - if (inx < -1 || ! (((ulonglong) 1 << inx) & info->s->state.key_map)) + if (inx < -1 || ! mi_is_key_active(info->s->state.key_map, inx)) { DBUG_RETURN(my_errno=HA_ERR_WRONG_INDEX); } diff --git a/myisam/mi_search.c b/myisam/mi_search.c index c669a8be8f8..ed61bbfe41a 100644 --- a/myisam/mi_search.c +++ b/myisam/mi_search.c @@ -29,7 +29,7 @@ int _mi_check_index(MI_INFO *info, int inx) { if (inx == -1) /* Use last index */ inx=info->lastinx; - if (inx < 0 || ! (((ulonglong) 1 << inx) & info->s->state.key_map)) + if (inx < 0 || ! mi_is_key_active(info->s->state.key_map, inx)) { my_errno=HA_ERR_WRONG_INDEX; return -1; diff --git a/myisam/mi_update.c b/myisam/mi_update.c index cda60694008..ab23f2e6da9 100644 --- a/myisam/mi_update.c +++ b/myisam/mi_update.c @@ -87,7 +87,7 @@ int mi_update(register MI_INFO *info, const byte *oldrec, byte *newrec) changed=0; for (i=0 ; i < share->base.keys ; i++) { - if (((ulonglong) 1 << i) & share->state.key_map) + if (mi_is_key_active(share->state.key_map, i)) { if (share->keyinfo[i].flag & HA_FULLTEXT ) { diff --git a/myisam/mi_write.c b/myisam/mi_write.c index dd062b79769..c8f9aa84a41 100644 --- a/myisam/mi_write.c +++ b/myisam/mi_write.c @@ -101,7 +101,7 @@ int mi_write(MI_INFO *info, byte *record) buff=info->lastkey2; for (i=0 ; i < share->base.keys ; i++) { - if (((ulonglong) 1 << i) & share->state.key_map) + if (mi_is_key_active(share->state.key_map, i)) { bool local_lock_tree= (lock_tree && !(info->bulk_insert && @@ -175,7 +175,7 @@ err: info->errkey= (int) i; while ( i-- > 0) { - if (((ulonglong) 1 << i) & share->state.key_map) + if (mi_is_key_active(share->state.key_map, i)) { bool local_lock_tree= (lock_tree && !(info->bulk_insert && @@ -944,20 +944,21 @@ int mi_init_bulk_insert(MI_INFO *info, ulong cache_size, ha_rows rows) MI_KEYDEF *key=share->keyinfo; bulk_insert_param *params; uint i, num_keys, total_keylength; - ulonglong key_map=0; + ulonglong key_map; DBUG_ENTER("_mi_init_bulk_insert"); DBUG_PRINT("enter",("cache_size: %lu", cache_size)); DBUG_ASSERT(!info->bulk_insert && (!rows || rows >= MI_MIN_ROWS_TO_USE_BULK_INSERT)); + mi_clear_all_keys_active(key_map); for (i=total_keylength=num_keys=0 ; i < share->base.keys ; i++) { - if (!(key[i].flag & HA_NOSAME) && share->base.auto_key != i+1 - && test(share->state.key_map & ((ulonglong) 1 << i))) + if (! (key[i].flag & HA_NOSAME) && (share->base.auto_key != i + 1) && + mi_is_key_active(share->state.key_map, i)) { num_keys++; - key_map |=((ulonglong) 1 << i); + mi_set_key_active(key_map, i); total_keylength+=key[i].maxlength+TREE_ELEMENT_EXTRA_SIZE; } } @@ -981,7 +982,7 @@ int mi_init_bulk_insert(MI_INFO *info, ulong cache_size, ha_rows rows) params=(bulk_insert_param *)(info->bulk_insert+share->base.keys); for (i=0 ; i < share->base.keys ; i++) { - if (test(key_map & ((ulonglong) 1 << i))) + if (mi_is_key_active(key_map, i)) { params->info=info; params->keynr=i; diff --git a/myisam/myisamchk.c b/myisam/myisamchk.c index 519e123e9da..4856d93b320 100644 --- a/myisam/myisamchk.c +++ b/myisam/myisamchk.c @@ -871,8 +871,8 @@ static int myisamchk(MI_CHECK *param, my_string filename) MI_STATE_INFO_SIZE || mi_uint2korr(share->state.header.base_info_length) != MI_BASE_INFO_SIZE || - ((param->keys_in_use & ~share->state.key_map) & - (((ulonglong) 1L << share->base.keys)-1)) || + mi_is_any_intersect_keys_active(param->keys_in_use, share->base.keys, + ~share->state.key_map) || test_if_almost_full(info) || info->s->state.header.file_version[3] != myisam_file_magic[3] || (set_collation && @@ -939,8 +939,8 @@ static int myisamchk(MI_CHECK *param, my_string filename) if (param->testflag & T_REP_ANY) { ulonglong tmp=share->state.key_map; - share->state.key_map= (((ulonglong) 1 << share->base.keys)-1) - & param->keys_in_use; + mi_copy_keys_active(share->state.key_map, share->base.keys, + param->keys_in_use); if (tmp != share->state.key_map) info->update|=HA_STATE_CHANGED; } @@ -961,7 +961,7 @@ static int myisamchk(MI_CHECK *param, my_string filename) if (!error) { if ((param->testflag & (T_REP_BY_SORT | T_REP_PARALLEL)) && - (share->state.key_map || + (mi_is_any_key_active(share->state.key_map) || (rep_quick && !param->keys_in_use && !recreate)) && mi_test_if_sort_rep(info, info->state->records, info->s->state.key_map, @@ -1037,7 +1037,7 @@ static int myisamchk(MI_CHECK *param, my_string filename) llstr(info->state->records,llbuff), llstr(info->state->del,llbuff2)); error =chk_status(param,info); - share->state.key_map &=param->keys_in_use; + mi_intersect_keys_active(share->state.key_map, param->keys_in_use); error =chk_size(param,info); if (!error || !(param->testflag & (T_FAST | T_FORCE_CREATE))) error|=chk_del(param, info,param->testflag); @@ -1266,7 +1266,7 @@ static void descript(MI_CHECK *param, register MI_INFO *info, my_string name) } printf("Recordlength: %13d\n",(int) share->base.pack_reclength); - if (share->state.key_map != (((ulonglong) 1 << share->base.keys) -1)) + if (! mi_is_all_keys_active(share->state.key_map, share->base.keys)) { longlong2str(share->state.key_map,buff,2); printf("Using only keys '%s' of %d possibly keys\n", @@ -1448,7 +1448,7 @@ static int mi_sort_records(MI_CHECK *param, temp_buff=0; new_file= -1; - if (!(((ulonglong) 1 << sort_key) & share->state.key_map)) + if (! mi_is_key_active(share->state.key_map, sort_key)) { mi_check_print_warning(param, "Can't sort table '%s' on key %d; No such key", diff --git a/myisam/myisamdef.h b/myisam/myisamdef.h index 5688b377d3d..74463ec065a 100644 --- a/myisam/myisamdef.h +++ b/myisam/myisamdef.h @@ -676,10 +676,10 @@ extern void _mi_unmap_file(MI_INFO *info); extern uint save_pack_length(byte *block_buff,ulong length); uint mi_state_info_write(File file, MI_STATE_INFO *state, uint pWrite); -char *mi_state_info_read(char *ptr, MI_STATE_INFO *state); +char *mi_state_info_read(uchar *ptr, MI_STATE_INFO *state); uint mi_state_info_read_dsk(File file, MI_STATE_INFO *state, my_bool pRead); uint mi_base_info_write(File file, MI_BASE_INFO *base); -char *my_n_base_info_read(char *ptr, MI_BASE_INFO *base); +char *my_n_base_info_read(uchar *ptr, MI_BASE_INFO *base); int mi_keyseg_write(File file, const HA_KEYSEG *keyseg); char *mi_keyseg_read(char *ptr, HA_KEYSEG *keyseg); uint mi_keydef_write(File file, MI_KEYDEF *keydef); diff --git a/myisam/myisamlog.c b/myisam/myisamlog.c index dc98d813266..de55b86252c 100644 --- a/myisam/myisamlog.c +++ b/myisam/myisamlog.c @@ -810,7 +810,7 @@ static int find_record_with_key(struct file_info *file_info, byte *record) for (key=0 ; key < info->s->base.keys ; key++) { - if ((((ulonglong) 1 << key) & info->s->state.key_map) && + if (mi_is_key_active(info->s->state.key_map, key) && info->s->keyinfo[key].flag & HA_NOSAME) { VOID(_mi_make_key(info,key,tmp_key,record,0L)); diff --git a/myisam/myisampack.c b/myisam/myisampack.c index daf2bbdf85c..ba48cbf1b62 100644 --- a/myisam/myisampack.c +++ b/myisam/myisampack.c @@ -441,9 +441,9 @@ static bool open_isam_files(PACK_MRG_INFO *mrg,char **names,uint count) if (!(mrg->file[i]=open_isam_file(names[i],O_RDONLY))) goto error; - mrg->src_file_has_indexes_disabled|= ((mrg->file[i]->s->state.key_map != - (((ulonglong) 1) << - mrg->file[i]->s->base. keys) - 1)); + mrg->src_file_has_indexes_disabled|= + ! mi_is_all_keys_active(mrg->file[i]->s->state.key_map, + mrg->file[i]->s->base.keys); } /* Check that files are identical */ for (j=0 ; j < count-1 ; j++) @@ -2941,7 +2941,7 @@ static int save_state(MI_INFO *isam_file,PACK_MRG_INFO *mrg,my_off_t new_length, share->state.dellink= HA_OFFSET_ERROR; share->state.split=(ha_rows) mrg->records; share->state.version=(ulong) time((time_t*) 0); - if (share->state.key_map != (ULL(1) << share->base.keys) - 1) + if (! mi_is_all_keys_active(share->state.key_map, share->base.keys)) { /* Some indexes are disabled, cannot use current key_file_length value @@ -2955,7 +2955,7 @@ static int save_state(MI_INFO *isam_file,PACK_MRG_INFO *mrg,my_off_t new_length, original file so "myisamchk -rq" can use this value (this is necessary because index size cannot be easily calculated for fulltext keys) */ - share->state.key_map=0; + mi_clear_all_keys_active(share->state.key_map); for (key=0 ; key < share->base.keys ; key++) share->state.key_root[key]= HA_OFFSET_ERROR; for (key=0 ; key < share->state.header.max_block_size ; key++) @@ -2995,7 +2995,7 @@ static int save_state_mrg(File file,PACK_MRG_INFO *mrg,my_off_t new_length, } state.dellink= HA_OFFSET_ERROR; state.version=(ulong) time((time_t*) 0); - state.key_map=0; + mi_clear_all_keys_active(state.key_map); state.checksum=crc; if (isam_file->s->base.keys) isamchk_neaded=1; diff --git a/myisam/sort.c b/myisam/sort.c index 9d2af2e8c70..f2f8c8ef7ec 100644 --- a/myisam/sort.c +++ b/myisam/sort.c @@ -479,7 +479,7 @@ int thr_write_keys(MI_SORT_PARAM *sort_param) } if (!got_error) { - share->state.key_map|=(ulonglong) 1 << sinfo->key; + mi_set_key_active(share->state.key_map, sinfo->key); if (param->testflag & T_STATISTICS) update_key_parts(sinfo->keyinfo, rec_per_key_part, sinfo->unique, (ulonglong) info->state->records); diff --git a/mysys/my_bitmap.c b/mysys/my_bitmap.c index c70c0fa0754..4a917fc8287 100644 --- a/mysys/my_bitmap.c +++ b/mysys/my_bitmap.c @@ -339,6 +339,37 @@ void bitmap_intersect(MY_BITMAP *map, const MY_BITMAP *map2) } +/* + Set/clear all bits above a bit. + + SYNOPSIS + bitmap_set_above() + map RETURN The bitmap to change. + from_byte The bitmap buffer byte offset to start with. + use_bit The bit value (1/0) to use for all upper bits. + + NOTE + You can only set/clear full bytes. + The function is meant for the situation that you copy a smaller bitmap + to a bigger bitmap. Bitmap lengths are always multiple of eigth (the + size of a byte). Using 'from_byte' saves multiplication and division + by eight during parameter passing. + + RETURN + void +*/ + +void bitmap_set_above(MY_BITMAP *map, uint from_byte, uint use_bit) +{ + uchar use_byte= use_bit ? 0xff : 0; + uchar *to= map->bitmap + from_byte; + uchar *end= map->bitmap + map->bitmap_size; + + while (to < end) + *to++= use_byte; +} + + void bitmap_subtract(MY_BITMAP *map, const MY_BITMAP *map2) { uchar *to=map->bitmap, *from=map2->bitmap, *end; diff --git a/sql/ha_myisam.cc b/sql/ha_myisam.cc index 0d9c32adbfa..e94b697e0bb 100644 --- a/sql/ha_myisam.cc +++ b/sql/ha_myisam.cc @@ -602,7 +602,7 @@ int ha_myisam::repair(THD *thd, MI_CHECK ¶m, bool optimize) !(share->state.changed & STATE_NOT_OPTIMIZED_KEYS)))) { ulonglong key_map= ((local_testflag & T_CREATE_MISSING_KEYS) ? - ((ulonglong) 1L << share->base.keys)-1 : + mi_get_mask_all_keys_active(share->base.keys) : share->state.key_map); uint testflag=param.testflag; if (mi_test_if_sort_rep(file,file->state->records,key_map,0) && @@ -903,7 +903,7 @@ int ha_myisam::enable_indexes(uint mode) { int error; - if (file->s->state.key_map == set_bits(ulonglong, file->s->base.keys)) + if (mi_is_all_keys_active(file->s->state.key_map, file->s->base.keys)) { /* All indexes are enabled already. */ return 0; @@ -1002,8 +1002,8 @@ void ha_myisam::start_bulk_insert(ha_rows rows) if (! rows || (rows > MI_MIN_ROWS_TO_USE_WRITE_CACHE)) mi_extra(file, HA_EXTRA_WRITE_CACHE, (void*) &size); - can_enable_indexes= (file->s->state.key_map == - set_bits(ulonglong, file->s->base.keys)); + can_enable_indexes= mi_is_all_keys_active(file->s->state.key_map, + file->s->base.keys); if (!(specialflag & SPECIAL_SAFE_MODE)) { @@ -1256,7 +1256,7 @@ void ha_myisam::info(uint flag) share->db_options_in_use= info.options; block_size= myisam_block_size; share->keys_in_use.set_prefix(share->keys); - share->keys_in_use.intersect(info.key_map); + share->keys_in_use.intersect_extended(info.key_map); share->keys_for_keyread.intersect(share->keys_in_use); share->db_record_offset= info.record_offset; if (share->key_parts) diff --git a/sql/sql_bitmap.h b/sql/sql_bitmap.h index bc1484b4fb0..0f5b6dcd35e 100644 --- a/sql/sql_bitmap.h +++ b/sql/sql_bitmap.h @@ -51,6 +51,14 @@ public: bitmap_init(&map2, (uchar *)&map2buff, sizeof(ulonglong)*8, 0); bitmap_intersect(&map, &map2); } + /* Use highest bit for all bits above sizeof(ulonglong)*8. */ + void intersect_extended(ulonglong map2buff) + { + intersect(map2buff); + if (map.bitmap_size > sizeof(ulonglong)) + bitmap_set_above(&map, sizeof(ulonglong), + test(map2buff & (LL(1) << (sizeof(ulonglong) * 8 - 1)))); + } void subtract(Bitmap& map2) { bitmap_subtract(&map, &map2.map); } void merge(Bitmap& map2) { bitmap_union(&map, &map2.map); } my_bool is_set(uint n) const { return bitmap_is_set(&map, n); } @@ -116,6 +124,7 @@ public: void clear_all() { map=(ulonglong)0; } void intersect(Bitmap<64>& map2) { map&= map2.map; } void intersect(ulonglong map2) { map&= map2; } + void intersect_extended(ulonglong map2) { map&= map2; } void subtract(Bitmap<64>& map2) { map&= ~map2.map; } void merge(Bitmap<64>& map2) { map|= map2.map; } my_bool is_set(uint n) const { return test(map & (((ulonglong)1) << n)); } From 8a3e723b7411826004b45bca14405fe2c18ea719 Mon Sep 17 00:00:00 2001 From: "dlenev@mysql.com" <> Date: Tue, 19 Jul 2005 20:06:49 +0400 Subject: [PATCH 46/48] Fix for bugs #5892/6182/8751/8758/10994 (based on Antony's patch) "Triggers have the wrong namespace" "Triggers: duplicate names allowed" "Triggers: CREATE TRIGGER does not accept fully qualified names" "SHOW TRIGGERS" --- mysql-test/r/information_schema.result | 77 ++++- mysql-test/r/information_schema_db.result | 2 + mysql-test/r/rpl_sp.result | 4 +- mysql-test/r/trigger.result | 53 ++-- mysql-test/r/view.result | 2 +- mysql-test/t/information_schema.test | 35 +++ mysql-test/t/rpl_sp.test | 2 +- mysql-test/t/trigger.test | 59 ++-- mysql-test/t/view.test | 2 +- sql/handler.cc | 1 + sql/item.h | 4 +- sql/lex.h | 1 + sql/mysql_priv.h | 1 + sql/mysqld.cc | 1 + sql/share/errmsg.txt | 2 + sql/sp.cc | 4 +- sql/sql_base.cc | 2 +- sql/sql_lex.h | 1 + sql/sql_parse.cc | 1 + sql/sql_show.cc | 94 ++++++ sql/sql_table.cc | 14 +- sql/sql_trigger.cc | 367 +++++++++++++++++++--- sql/sql_trigger.h | 11 +- sql/sql_yacc.yy | 32 +- sql/table.h | 2 +- 25 files changed, 644 insertions(+), 130 deletions(-) diff --git a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result index 98f3d59485f..857e8a61a37 100644 --- a/mysql-test/r/information_schema.result +++ b/mysql-test/r/information_schema.result @@ -48,6 +48,7 @@ TABLE_PRIVILEGES COLUMN_PRIVILEGES TABLE_CONSTRAINTS KEY_COLUMN_USAGE +TRIGGERS columns_priv db func @@ -77,6 +78,7 @@ c table_name TABLES TABLES TABLE_PRIVILEGES TABLE_PRIVILEGES TABLE_CONSTRAINTS TABLE_CONSTRAINTS +TRIGGERS TRIGGERS tables_priv tables_priv time_zone time_zone time_zone_leap_second time_zone_leap_second @@ -94,6 +96,7 @@ c table_name TABLES TABLES TABLE_PRIVILEGES TABLE_PRIVILEGES TABLE_CONSTRAINTS TABLE_CONSTRAINTS +TRIGGERS TRIGGERS tables_priv tables_priv time_zone time_zone time_zone_leap_second time_zone_leap_second @@ -111,6 +114,7 @@ c table_name TABLES TABLES TABLE_PRIVILEGES TABLE_PRIVILEGES TABLE_CONSTRAINTS TABLE_CONSTRAINTS +TRIGGERS TRIGGERS tables_priv tables_priv time_zone time_zone time_zone_leap_second time_zone_leap_second @@ -577,6 +581,7 @@ Tables_in_information_schema (T%) TABLES TABLE_PRIVILEGES TABLE_CONSTRAINTS +TRIGGERS create database information_schema; ERROR HY000: Can't create database 'information_schema'; database exists use information_schema; @@ -585,6 +590,7 @@ Tables_in_information_schema (T%) Table_type TABLES TEMPORARY TABLE_PRIVILEGES TEMPORARY TABLE_CONSTRAINTS TEMPORARY +TRIGGERS TEMPORARY create table t1(a int); ERROR 42S02: Unknown table 't1' in information_schema use test; @@ -596,6 +602,7 @@ Tables_in_information_schema (T%) TABLES TABLE_PRIVILEGES TABLE_CONSTRAINTS +TRIGGERS select table_name from tables where table_name='user'; table_name user @@ -690,7 +697,7 @@ CREATE TABLE t_crashme ( f1 BIGINT); CREATE VIEW a1 (t_CRASHME) AS SELECT f1 FROM t_crashme GROUP BY f1; CREATE VIEW a2 AS SELECT t_CRASHME FROM a1; count(*) -100 +101 drop view a2, a1; drop table t_crashme; select table_schema,table_name, column_name from @@ -701,6 +708,8 @@ information_schema COLUMNS COLUMN_TYPE information_schema ROUTINES ROUTINE_DEFINITION information_schema ROUTINES SQL_MODE information_schema VIEWS VIEW_DEFINITION +information_schema TRIGGERS ACTION_CONDITION +information_schema TRIGGERS ACTION_STATEMENT select table_name, column_name, data_type from information_schema.columns where data_type = 'datetime'; table_name column_name data_type @@ -709,6 +718,7 @@ TABLES UPDATE_TIME datetime TABLES CHECK_TIME datetime ROUTINES CREATED datetime ROUTINES LAST_ALTERED datetime +TRIGGERS CREATED datetime SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES A WHERE NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.COLUMNS B @@ -755,8 +765,71 @@ delete from mysql.db where user='mysqltest_4'; flush privileges; SELECT table_schema, count(*) FROM information_schema.TABLES GROUP BY TABLE_SCHEMA; table_schema count(*) -information_schema 15 +information_schema 16 mysql 17 +create table t1 (i int, j int); +create trigger trg1 before insert on t1 for each row +begin +if new.j > 10 then +set new.j := 10; +end if; +end| +create trigger trg2 before update on t1 for each row +begin +if old.i % 2 = 0 then +set new.j := -1; +end if; +end| +create trigger trg3 after update on t1 for each row +begin +if new.j = -1 then +set @fired:= "Yes"; +end if; +end| +show triggers; +Trigger Event Table Statement Timing Created +trg1 INSERT t1 +begin +if new.j > 10 then +set new.j := 10; +end if; +end BEFORE NULL +trg2 UPDATE t1 +begin +if old.i % 2 = 0 then +set new.j := -1; +end if; +end BEFORE NULL +trg3 UPDATE t1 +begin +if new.j = -1 then +set @fired:= "Yes"; +end if; +end AFTER NULL +select * from information_schema.triggers; +TRIGGER_CATALOG TRIGGER_SCHEMA TRIGGER_NAME EVENT_MANIPULATION EVENT_OBJECT_CATALOG EVENT_OBJECT_SCHEMA EVENT_OBJECT_TABLE ACTION_ORDER ACTION_CONDITION ACTION_STATEMENT ACTION_ORIENTATION ACTION_TIMING ACTION_REFERENCE_OLD_TABLE ACTION_REFERENCE_NEW_TABLE ACTION_REFERENCE_OLD_ROW ACTION_REFERENCE_NEW_ROW CREATED +NULL test trg1 INSERT NULL test t1 0 NULL +begin +if new.j > 10 then +set new.j := 10; +end if; +end ROW BEFORE NULL NULL OLD NEW NULL +NULL test trg2 UPDATE NULL test t1 0 NULL +begin +if old.i % 2 = 0 then +set new.j := -1; +end if; +end ROW BEFORE NULL NULL OLD NEW NULL +NULL test trg3 UPDATE NULL test t1 0 NULL +begin +if new.j = -1 then +set @fired:= "Yes"; +end if; +end ROW AFTER NULL NULL OLD NEW NULL +drop trigger trg1; +drop trigger trg2; +drop trigger trg3; +drop table t1; create database mysqltest; create table mysqltest.t1 (f1 int, f2 int); create table mysqltest.t2 (f1 int); diff --git a/mysql-test/r/information_schema_db.result b/mysql-test/r/information_schema_db.result index 3da5cc7bd11..ece30924055 100644 --- a/mysql-test/r/information_schema_db.result +++ b/mysql-test/r/information_schema_db.result @@ -16,11 +16,13 @@ TABLE_PRIVILEGES COLUMN_PRIVILEGES TABLE_CONSTRAINTS KEY_COLUMN_USAGE +TRIGGERS show tables from INFORMATION_SCHEMA like 'T%'; Tables_in_information_schema (T%) TABLES TABLE_PRIVILEGES TABLE_CONSTRAINTS +TRIGGERS create database `inf%`; use `inf%`; show tables; diff --git a/mysql-test/r/rpl_sp.result b/mysql-test/r/rpl_sp.result index 64e09839f1b..15180abe8fd 100644 --- a/mysql-test/r/rpl_sp.result +++ b/mysql-test/r/rpl_sp.result @@ -237,7 +237,7 @@ select * from t1; a 10 delete from t1; -drop trigger t1.trg; +drop trigger trg; insert into t1 values (1); select * from t1; a @@ -248,7 +248,7 @@ master-bin.000002 # Query 1 # use `mysqltest1`; delete from t1 master-bin.000002 # Query 1 # use `mysqltest1`; create trigger trg before insert on t1 for each row set new.a= 10 master-bin.000002 # Query 1 # use `mysqltest1`; insert into t1 values (1) master-bin.000002 # Query 1 # use `mysqltest1`; delete from t1 -master-bin.000002 # Query 1 # use `mysqltest1`; drop trigger t1.trg +master-bin.000002 # Query 1 # use `mysqltest1`; drop trigger trg master-bin.000002 # Query 1 # use `mysqltest1`; insert into t1 values (1) select * from t1; a diff --git a/mysql-test/r/trigger.result b/mysql-test/r/trigger.result index efd09ba08fc..7e3a6fa65d4 100644 --- a/mysql-test/r/trigger.result +++ b/mysql-test/r/trigger.result @@ -12,13 +12,13 @@ insert into t1 values (1); select @a; @a 1 -drop trigger t1.trg; +drop trigger trg; create trigger trg before insert on t1 for each row set @a:=new.i; insert into t1 values (123); select @a; @a 123 -drop trigger t1.trg; +drop trigger trg; drop table t1; create table t1 (i int not null, j int); create trigger trg before insert on t1 for each row @@ -33,7 +33,7 @@ select * from t1| i j 1 10 2 3 -drop trigger t1.trg| +drop trigger trg| drop table t1| create table t1 (i int not null primary key); create trigger trg after insert on t1 for each row @@ -43,7 +43,7 @@ insert into t1 values (2),(3),(4),(5); select @a; @a 2:3:4:5 -drop trigger t1.trg; +drop trigger trg; drop table t1; create table t1 (aid int not null primary key, balance int not null default 0); insert into t1 values (1, 1000), (2,3000); @@ -65,7 +65,7 @@ Too big change for aid = 2 aid balance 1 1500 2 3000 -drop trigger t1.trg| +drop trigger trg| drop table t1| create table t1 (i int); insert into t1 values (1),(2),(3),(4); @@ -76,7 +76,7 @@ update t1 set i=3; select @total_change; @total_change 2 -drop trigger t1.trg; +drop trigger trg; drop table t1; create table t1 (i int); insert into t1 values (1),(2),(3),(4); @@ -87,7 +87,7 @@ delete from t1 where i <= 3; select @del_sum; @del_sum 6 -drop trigger t1.trg; +drop trigger trg; drop table t1; create table t1 (i int); insert into t1 values (1),(2),(3),(4); @@ -97,7 +97,7 @@ delete from t1 where i <> 0; select @del; @del 1 -drop trigger t1.trg; +drop trigger trg; drop table t1; create table t1 (i int, j int); create trigger trg1 before insert on t1 for each row @@ -137,9 +137,9 @@ i j 1 20 2 -1 3 20 -drop trigger t1.trg1; -drop trigger t1.trg2; -drop trigger t1.trg3; +drop trigger trg1; +drop trigger trg2; +drop trigger trg3; drop table t1; create table t1 (id int not null primary key, data int); create trigger t1_bi before insert on t1 for each row @@ -197,7 +197,7 @@ select * from t2; event INSERT INTO t1 id=1 data='one' INSERT INTO t1 id=2 data='two' -drop trigger t1.t1_ai; +drop trigger t1_ai; create trigger t1_bi before insert on t1 for each row begin if exists (select id from t3 where id=new.fk) then @@ -271,6 +271,7 @@ id copy 3 NULL drop table t1, t2; create table t1 (i int); +create table t3 (i int); create trigger trg before insert on t1 for each row set @a:= old.i; ERROR HY000: There is no OLD row in on INSERT trigger create trigger trg before delete on t1 for each row set @a:= new.i; @@ -292,14 +293,19 @@ create trigger trg after insert on t1 for each row set @a:=1; ERROR HY000: Trigger already exists create trigger trg2 before insert on t1 for each row set @a:=1; ERROR HY000: Trigger already exists -drop trigger t1.trg; -drop trigger t1.trg; +create trigger trg before insert on t3 for each row set @a:=1; +ERROR HY000: Trigger already exists +create trigger trg2 before insert on t3 for each row set @a:=1; +drop trigger trg2; +drop trigger trg; +drop trigger trg; ERROR HY000: Trigger does not exist create view v1 as select * from t1; create trigger trg before insert on v1 for each row set @a:=1; ERROR HY000: 'test.v1' is not BASE TABLE drop view v1; drop table t1; +drop table t3; create temporary table t1 (i int); create trigger trg before insert on t1 for each row set @a:=1; ERROR HY000: Trigger's 't1' is view or temporary table @@ -307,7 +313,7 @@ drop table t1; create table t1 (x1col char); create trigger tx1 before insert on t1 for each row set new.x1col = 'x'; insert into t1 values ('y'); -drop trigger t1.tx1; +drop trigger tx1; drop table t1; create table t1 (i int) engine=myisam; insert into t1 values (1), (2); @@ -318,8 +324,8 @@ delete from t1; select @del_before, @del_after; @del_before @del_after 3 3 -drop trigger t1.trg1; -drop trigger t1.trg2; +drop trigger trg1; +drop trigger trg2; drop table t1; create table t1 (a int); create trigger trg1 before insert on t1 for each row set new.a= 10; @@ -336,6 +342,15 @@ create table t1 (i int); create trigger trg1 before insert on t1 for each row set @a:= 1; drop database mysqltest; use test; +create database mysqltest; +create table mysqltest.t1 (i int); +create trigger trg1 before insert on mysqltest.t1 for each row set @a:= 1; +ERROR HY000: Trigger in wrong schema +use mysqltest; +create trigger test.trg1 before insert on t1 for each row set @a:= 1; +ERROR HY000: Trigger in wrong schema +drop database mysqltest; +use test; create table t1 (i int, j int default 10, k int not null, key (k)); create table t2 (i int); insert into t1 (i, k) values (1, 1); @@ -549,7 +564,7 @@ i k 1 1 2 2 alter table t1 add primary key (i); -drop trigger t1.bi; +drop trigger bi; insert into t1 values (2, 4) on duplicate key update k= k + 10; ERROR 42S22: Unknown column 'bt' in 'NEW' select * from t1; @@ -578,5 +593,5 @@ create trigger t1_bu before update on t1 for each row set new.col1= bug5893(); drop function bug5893; update t1 set col2 = 4; ERROR 42000: FUNCTION test.bug5893 does not exist -drop trigger t1.t1_bu; +drop trigger t1_bu; drop table t1; diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index a132279c6bc..624fae4d728 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -1245,7 +1245,7 @@ select * from v1; s1 select * from t1; s1 -drop trigger t1.t1_bi; +drop trigger t1_bi; drop view v1; drop table t1; create table t1 (s1 tinyint); diff --git a/mysql-test/t/information_schema.test b/mysql-test/t/information_schema.test index 7c0624b67fd..08f572a593a 100644 --- a/mysql-test/t/information_schema.test +++ b/mysql-test/t/information_schema.test @@ -505,6 +505,41 @@ flush privileges; # SELECT table_schema, count(*) FROM information_schema.TABLES GROUP BY TABLE_SCHEMA; + +# +# TRIGGERS table test +# +create table t1 (i int, j int); + +delimiter |; +create trigger trg1 before insert on t1 for each row +begin + if new.j > 10 then + set new.j := 10; + end if; +end| +create trigger trg2 before update on t1 for each row +begin + if old.i % 2 = 0 then + set new.j := -1; + end if; +end| +create trigger trg3 after update on t1 for each row +begin + if new.j = -1 then + set @fired:= "Yes"; + end if; +end| +delimiter ;| +show triggers; +select * from information_schema.triggers; + +drop trigger trg1; +drop trigger trg2; +drop trigger trg3; +drop table t1; + + # # Bug #10964 Information Schema:Authorization check on privilege tables is improper # diff --git a/mysql-test/t/rpl_sp.test b/mysql-test/t/rpl_sp.test index e2a8982ebaa..184ac4edea1 100644 --- a/mysql-test/t/rpl_sp.test +++ b/mysql-test/t/rpl_sp.test @@ -249,7 +249,7 @@ select * from t1; connection master; delete from t1; -drop trigger t1.trg; +drop trigger trg; insert into t1 values (1); select * from t1; --replace_column 2 # 5 # diff --git a/mysql-test/t/trigger.test b/mysql-test/t/trigger.test index 8a27636ed84..b36140f2ada 100644 --- a/mysql-test/t/trigger.test +++ b/mysql-test/t/trigger.test @@ -17,13 +17,13 @@ set @a:=0; select @a; insert into t1 values (1); select @a; -drop trigger t1.trg; +drop trigger trg; # let us test simple trigger reading some values create trigger trg before insert on t1 for each row set @a:=new.i; insert into t1 values (123); select @a; -drop trigger t1.trg; +drop trigger trg; drop table t1; @@ -40,7 +40,7 @@ end| insert into t1 (i) values (1)| insert into t1 (i,j) values (2, 3)| select * from t1| -drop trigger t1.trg| +drop trigger trg| drop table t1| delimiter ;| @@ -52,7 +52,7 @@ create trigger trg after insert on t1 for each row set @a:=""; insert into t1 values (2),(3),(4),(5); select @a; -drop trigger t1.trg; +drop trigger trg; drop table t1; # PS doesn't work with multi-row statements @@ -75,7 +75,7 @@ set @update_failed:=""| update t1 set balance=1500| select @update_failed; select * from t1| -drop trigger t1.trg| +drop trigger trg| drop table t1| delimiter ;| --enable_ps_protocol @@ -88,7 +88,7 @@ create trigger trg after update on t1 for each row set @total_change:=0; update t1 set i=3; select @total_change; -drop trigger t1.trg; +drop trigger trg; drop table t1; # Before delete trigger @@ -100,7 +100,7 @@ create trigger trg before delete on t1 for each row set @del_sum:= 0; delete from t1 where i <= 3; select @del_sum; -drop trigger t1.trg; +drop trigger trg; drop table t1; # After delete trigger. @@ -111,7 +111,7 @@ create trigger trg after delete on t1 for each row set @del:= 1; set @del:= 0; delete from t1 where i <> 0; select @del; -drop trigger t1.trg; +drop trigger trg; drop table t1; # Several triggers on one table @@ -145,9 +145,9 @@ update t1 set j= 20; select @fired; select * from t1; -drop trigger t1.trg1; -drop trigger t1.trg2; -drop trigger t1.trg3; +drop trigger trg1; +drop trigger trg2; +drop trigger trg3; drop table t1; @@ -212,7 +212,7 @@ create trigger t1_ai after insert on t1 for each row insert into t1 (id, data) values (1, "one"), (2, "two"); select * from t1; select * from t2; -drop trigger t1.t1_ai; +drop trigger t1_ai; # Trigger which uses couple of tables (and partially emulates FK constraint) delimiter |; create trigger t1_bi before insert on t1 for each row @@ -282,6 +282,7 @@ drop table t1, t2; # Test of wrong column specifiers in triggers # create table t1 (i int); +create table t3 (i int); --error 1363 create trigger trg before insert on t1 for each row set @a:= old.i; @@ -301,7 +302,7 @@ create trigger trg before update on t1 for each row set @a:=old.j; # # Let us test various trigger creation errors -# +# Also quickly test table namespace (bug#5892/6182) # --error 1146 create trigger trg before insert on t2 for each row set @a:=1; @@ -311,10 +312,14 @@ create trigger trg before insert on t1 for each row set @a:=1; create trigger trg after insert on t1 for each row set @a:=1; --error 1359 create trigger trg2 before insert on t1 for each row set @a:=1; -drop trigger t1.trg; +--error 1359 +create trigger trg before insert on t3 for each row set @a:=1; +create trigger trg2 before insert on t3 for each row set @a:=1; +drop trigger trg2; +drop trigger trg; --error 1360 -drop trigger t1.trg; +drop trigger trg; create view v1 as select * from t1; --error 1347 @@ -322,6 +327,7 @@ create trigger trg before insert on v1 for each row set @a:=1; drop view v1; drop table t1; +drop table t3; create temporary table t1 (i int); --error 1361 @@ -339,7 +345,7 @@ drop table t1; create table t1 (x1col char); create trigger tx1 before insert on t1 for each row set new.x1col = 'x'; insert into t1 values ('y'); -drop trigger t1.tx1; +drop trigger tx1; drop table t1; # @@ -355,8 +361,8 @@ create trigger trg2 after delete on t1 for each row set @del_after:= @del_after set @del_before:=0, @del_after:= 0; delete from t1; select @del_before, @del_after; -drop trigger t1.trg1; -drop trigger t1.trg2; +drop trigger trg1; +drop trigger trg2; drop table t1; # Test for bug #5859 "DROP TABLE does not drop triggers". Trigger should not @@ -378,6 +384,19 @@ create trigger trg1 before insert on t1 for each row set @a:= 1; drop database mysqltest; use test; +# Test for bug #8791 +# "Triggers: Allowed to create triggers on a subject table in a different DB". +create database mysqltest; +create table mysqltest.t1 (i int); +--error 1429 +create trigger trg1 before insert on mysqltest.t1 for each row set @a:= 1; +use mysqltest; +--error 1429 +create trigger test.trg1 before insert on t1 for each row set @a:= 1; +drop database mysqltest; +use test; + + # Test for bug #5860 "Multi-table UPDATE does not activate update triggers" # We will also test how delete triggers wor for multi-table DELETE. create table t1 (i int, j int default 10, k int not null, key (k)); @@ -559,7 +578,7 @@ select * from t1; # To test properly code-paths different from those that are used # in ordinary INSERT we need to drop "before insert" trigger. alter table t1 add primary key (i); -drop trigger t1.bi; +drop trigger bi; --error 1054 insert into t1 values (2, 4) on duplicate key update k= k + 10; select * from t1; @@ -589,5 +608,5 @@ drop function bug5893; --error 1305 update t1 set col2 = 4; # This should not crash server too. -drop trigger t1.t1_bu; +drop trigger t1_bu; drop table t1; diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index a98ecfe232f..449bca63f0d 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -1182,7 +1182,7 @@ create view v1 as select * from t1 where s1 <> 127 with check option; insert into v1 values (0); select * from v1; select * from t1; -drop trigger t1.t1_bi; +drop trigger t1_bi; drop view v1; drop table t1; diff --git a/sql/handler.cc b/sql/handler.cc index a61dce35501..9f8fdceb69e 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -2434,6 +2434,7 @@ TYPELIB *ha_known_exts(void) known_extensions_id= mysys_usage_id; found_exts.push_back((char*) triggers_file_ext); + found_exts.push_back((char*) trigname_file_ext); for (types= sys_table_types; types->type; types++) { if (*types->value == SHOW_OPTION_YES) diff --git a/sql/item.h b/sql/item.h index f195557fb69..5a1cf193806 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1781,7 +1781,7 @@ public: */ enum trg_action_time_type { - TRG_ACTION_BEFORE= 0, TRG_ACTION_AFTER= 1 + TRG_ACTION_BEFORE= 0, TRG_ACTION_AFTER= 1, TRG_ACTION_MAX }; /* @@ -1789,7 +1789,7 @@ enum trg_action_time_type */ enum trg_event_type { - TRG_EVENT_INSERT= 0 , TRG_EVENT_UPDATE= 1, TRG_EVENT_DELETE= 2 + TRG_EVENT_INSERT= 0 , TRG_EVENT_UPDATE= 1, TRG_EVENT_DELETE= 2, TRG_EVENT_MAX }; class Table_triggers_list; diff --git a/sql/lex.h b/sql/lex.h index aa10328ced0..122e7040c80 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -497,6 +497,7 @@ static SYMBOL symbols[] = { { "TRAILING", SYM(TRAILING)}, { "TRANSACTION", SYM(TRANSACTION_SYM)}, { "TRIGGER", SYM(TRIGGER_SYM)}, + { "TRIGGERS", SYM(TRIGGERS_SYM)}, { "TRUE", SYM(TRUE_SYM)}, { "TRUNCATE", SYM(TRUNCATE_SYM)}, { "TYPE", SYM(TYPE_SYM)}, diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 49eb50be580..ea9b5c5bf7c 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1082,6 +1082,7 @@ extern const char **errmesg; /* Error messages */ extern const char *myisam_recover_options_str; extern const char *in_left_expr_name, *in_additional_cond; extern const char * const triggers_file_ext; +extern const char * const trigname_file_ext; extern Eq_creator eq_creator; extern Ne_creator ne_creator; extern Gt_creator gt_creator; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 78d9af387da..bd8d81ed904 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -5722,6 +5722,7 @@ struct show_var_st status_vars[]= { {"Com_show_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_STATUS]), SHOW_LONG_STATUS}, {"Com_show_storage_engines", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_STORAGE_ENGINES]), SHOW_LONG_STATUS}, {"Com_show_tables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TABLES]), SHOW_LONG_STATUS}, + {"Com_show_triggers", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TRIGGERS]), SHOW_LONG_STATUS}, {"Com_show_variables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_VARIABLES]), SHOW_LONG_STATUS}, {"Com_show_warnings", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_WARNS]), SHOW_LONG_STATUS}, {"Com_slave_start", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SLAVE_START]), SHOW_LONG_STATUS}, diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index f999f17aedf..971061e22eb 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5370,3 +5370,5 @@ ER_SCALE_BIGGER_THAN_PRECISION 42000 S1009 eng "Scale may not be larger than the precision (column '%-.64s')." ER_WRONG_LOCK_OF_SYSTEM_TABLE eng "You can't combine write-locking of system '%-.64s.%-.64s' table with other tables" +ER_TRG_IN_WRONG_SCHEMA + eng "Trigger in wrong schema" diff --git a/sql/sp.cc b/sql/sp.cc index 55087f47f5e..a277c6bd253 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -1442,8 +1442,8 @@ sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex, { Sroutine_hash_entry **last_cached_routine_ptr= (Sroutine_hash_entry **)lex->sroutines_list.next; - for (int i= 0; i < 3; i++) - for (int j= 0; j < 2; j++) + for (int i= 0; i < (int)TRG_EVENT_MAX; i++) + for (int j= 0; j < (int)TRG_ACTION_MAX; j++) if (triggers->bodies[i][j]) { (void)triggers->bodies[i][j]->add_used_tables_to_table_list(thd, diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 7021f61a052..576f5a503f0 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1740,7 +1740,7 @@ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db, !my_strcasecmp(system_charset_info, name, "proc")) entry->s->system_table= 1; - if (Table_triggers_list::check_n_load(thd, db, name, entry)) + if (Table_triggers_list::check_n_load(thd, db, name, entry, 0)) goto err; /* diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 45c8182a29c..4bba0c432c7 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -57,6 +57,7 @@ enum enum_sql_command { SQLCOM_SHOW_PROCESSLIST, SQLCOM_SHOW_MASTER_STAT, SQLCOM_SHOW_SLAVE_STAT, SQLCOM_SHOW_GRANTS, SQLCOM_SHOW_CREATE, SQLCOM_SHOW_CHARSETS, SQLCOM_SHOW_COLLATIONS, SQLCOM_SHOW_CREATE_DB, SQLCOM_SHOW_TABLE_STATUS, + SQLCOM_SHOW_TRIGGERS, SQLCOM_LOAD,SQLCOM_SET_OPTION,SQLCOM_LOCK_TABLES,SQLCOM_UNLOCK_TABLES, SQLCOM_GRANT, diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 2e5cab4bb1c..4ef754d69bb 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2104,6 +2104,7 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident, case SCH_TABLE_NAMES: case SCH_TABLES: case SCH_VIEWS: + case SCH_TRIGGERS: #ifdef DONT_ALLOW_SHOW_COMMANDS my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND), MYF(0)); /* purecov: inspected */ diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 8343f9ec582..fd82529464f 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -21,6 +21,7 @@ #include "sql_select.h" // For select_describe #include "repl_failsafe.h" #include "sp_head.h" +#include "sql_trigger.h" #include #ifdef HAVE_BERKELEY_DB @@ -1696,6 +1697,7 @@ void get_index_field_values(LEX *lex, INDEX_FIELD_VALUES *index_field_values) break; case SQLCOM_SHOW_TABLES: case SQLCOM_SHOW_TABLE_STATUS: + case SQLCOM_SHOW_TRIGGERS: index_field_values->db_value= lex->current_select->db; index_field_values->table_value= wild; break; @@ -2963,6 +2965,73 @@ static int get_schema_constraints_record(THD *thd, struct st_table_list *tables, } +static bool store_trigger(THD *thd, TABLE *table, const char *db, + const char *tname, LEX_STRING *trigger_name, + enum trg_event_type event, + enum trg_action_time_type timing, + LEX_STRING *trigger_stmt) +{ + CHARSET_INFO *cs= system_charset_info; + restore_record(table, s->default_values); + table->field[1]->store(db, strlen(db), cs); + table->field[2]->store(trigger_name->str, trigger_name->length, cs); + table->field[3]->store(trg_event_type_names[event].str, + trg_event_type_names[event].length, cs); + table->field[5]->store(db, strlen(db), cs); + table->field[6]->store(tname, strlen(tname), cs); + table->field[9]->store(trigger_stmt->str, trigger_stmt->length, cs); + table->field[10]->store("ROW", 3, cs); + table->field[11]->store(trg_action_time_type_names[timing].str, + trg_action_time_type_names[timing].length, cs); + table->field[14]->store("OLD", 3, cs); + table->field[15]->store("NEW", 3, cs); + return schema_table_store_record(thd, table); +} + + +static int get_schema_triggers_record(THD *thd, struct st_table_list *tables, + TABLE *table, bool res, + const char *base_name, + const char *file_name) +{ + DBUG_ENTER("get_schema_triggers_record"); + /* + res can be non zero value when processed table is a view or + error happened during opening of processed table. + */ + if (res) + { + if (!tables->view) + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + thd->net.last_errno, thd->net.last_error); + thd->clear_error(); + DBUG_RETURN(0); + } + if (!tables->view && tables->table->triggers) + { + Table_triggers_list *triggers= tables->table->triggers; + int event, timing; + for (event= 0; event < (int)TRG_EVENT_MAX; event++) + { + for (timing= 0; timing < (int)TRG_ACTION_MAX; timing++) + { + LEX_STRING trigger_name; + LEX_STRING trigger_stmt; + if (triggers->get_trigger_info(thd, (enum trg_event_type) event, + (enum trg_action_time_type)timing, + &trigger_name, &trigger_stmt)) + continue; + if (store_trigger(thd, table, base_name, file_name, &trigger_name, + (enum trg_event_type) event, + (enum trg_action_time_type) timing, &trigger_stmt)) + DBUG_RETURN(1); + } + } + } + DBUG_RETURN(0); +} + + void store_key_column_usage(TABLE *table, const char*db, const char *tname, const char *key_name, uint key_len, const char *con_type, uint con_len, longlong idx) @@ -3847,6 +3916,29 @@ ST_FIELD_INFO open_tables_fields_info[]= }; +ST_FIELD_INFO triggers_fields_info[]= +{ + {"TRIGGER_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0}, + {"TRIGGER_SCHEMA",NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, + {"TRIGGER_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Trigger"}, + {"EVENT_MANIPULATION", 6, MYSQL_TYPE_STRING, 0, 0, "Event"}, + {"EVENT_OBJECT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0}, + {"EVENT_OBJECT_SCHEMA",NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, + {"EVENT_OBJECT_TABLE", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Table"}, + {"ACTION_ORDER", 4, MYSQL_TYPE_LONG, 0, 0, 0}, + {"ACTION_CONDITION", 65535, MYSQL_TYPE_STRING, 0, 1, 0}, + {"ACTION_STATEMENT", 65535, MYSQL_TYPE_STRING, 0, 0, "Statement"}, + {"ACTION_ORIENTATION", 9, MYSQL_TYPE_STRING, 0, 0, 0}, + {"ACTION_TIMING", 6, MYSQL_TYPE_STRING, 0, 0, "Timing"}, + {"ACTION_REFERENCE_OLD_TABLE", NAME_LEN, MYSQL_TYPE_STRING, 0, 1, 0}, + {"ACTION_REFERENCE_NEW_TABLE", NAME_LEN, MYSQL_TYPE_STRING, 0, 1, 0}, + {"ACTION_REFERENCE_OLD_ROW", 3, MYSQL_TYPE_STRING, 0, 0, 0}, + {"ACTION_REFERENCE_NEW_ROW", 3, MYSQL_TYPE_STRING, 0, 0, 0}, + {"CREATED", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, "Created"}, + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} +}; + + ST_FIELD_INFO variables_fields_info[]= { {"Variable_name", 80, MYSQL_TYPE_STRING, 0, 0, "Variable_name"}, @@ -3897,6 +3989,8 @@ ST_SCHEMA_TABLE schema_tables[]= fill_open_tables, make_old_format, 0, -1, -1, 1}, {"STATUS", variables_fields_info, create_schema_table, fill_status, make_old_format, 0, -1, -1, 1}, + {"TRIGGERS", triggers_fields_info, create_schema_table, + get_all_tables, make_old_format, get_schema_triggers_record, 5, 6, 0}, {"VARIABLES", variables_fields_info, create_schema_table, fill_variables, make_old_format, 0, -1, -1, 1}, {0, 0, 0, 0, 0, 0, 0, 0, 0} diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 89282d9fcb9..e3f85f05c17 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -23,6 +23,8 @@ #include #include #include +#include "sp_head.h" +#include "sql_trigger.h" #ifdef __WIN__ #include @@ -290,16 +292,8 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, if (!(new_error=my_delete(path,MYF(MY_WME)))) { some_tables_deleted=1; - /* - Destroy triggers for this table if there are any. - - We won't need this as soon as we will have new .FRM format, - in which we will store trigger definitions in the same .FRM - files as table descriptions. - */ - strmov(end, triggers_file_ext); - if (!access(path, F_OK)) - new_error= my_delete(path, MYF(MY_WME)); + new_error= Table_triggers_list::drop_all_triggers(thd, db, + table->table_name); } error|= new_error; } diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index fd79fc8b878..a7aee95197e 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -38,6 +38,45 @@ static File_option triggers_file_parameters[]= }; +/* + Structure representing contents of .TRN file which are used to support + database wide trigger namespace. +*/ + +struct st_trigname +{ + LEX_STRING trigger_table; +}; + +static const LEX_STRING trigname_file_type= {(char *)"TRIGGERNAME", 11}; + +const char * const trigname_file_ext= ".TRN"; + +static File_option trigname_file_parameters[]= +{ + {{(char*)"trigger_table", 15}, offsetof(struct st_trigname, trigger_table), + FILE_OPTIONS_ESTRING}, + {{0, 0}, 0, FILE_OPTIONS_STRING} +}; + + +const LEX_STRING trg_action_time_type_names[]= +{ + { (char *) STRING_WITH_LEN("BEFORE") }, + { (char *) STRING_WITH_LEN("AFTER") } +}; + +const LEX_STRING trg_event_type_names[]= +{ + { (char *) STRING_WITH_LEN("INSERT") }, + { (char *) STRING_WITH_LEN("UPDATE") }, + { (char *) STRING_WITH_LEN("DELETE") } +}; + + +static TABLE_LIST *add_table_for_trigger(THD *thd, sp_name *trig); + + /* Create or drop trigger for table. @@ -69,6 +108,10 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) But do we want this ? */ + if (!create && + !(tables= add_table_for_trigger(thd, thd->lex->spname))) + DBUG_RETURN(TRUE); + /* We should have only one table in table list. */ DBUG_ASSERT(tables->next_global == 0); @@ -174,11 +217,22 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables) { LEX *lex= thd->lex; TABLE *table= tables->table; - char dir_buff[FN_REFLEN], file_buff[FN_REFLEN]; - LEX_STRING dir, file; + char dir_buff[FN_REFLEN], file_buff[FN_REFLEN], trigname_buff[FN_REFLEN], + trigname_path[FN_REFLEN]; + LEX_STRING dir, file, trigname_file; LEX_STRING *trg_def, *name; Item_trigger_field *trg_field; - List_iterator_fast it(names_list); + struct st_trigname trigname; + + + /* Trigger must be in the same schema as target table. */ + if (my_strcasecmp(system_charset_info, table->s->db, + lex->spname->m_db.str ? lex->spname->m_db.str : + thd->db)) + { + my_error(ER_TRG_IN_WRONG_SCHEMA, MYF(0)); + return 1; + } /* We don't allow creation of several triggers of the same type yet */ if (bodies[lex->trg_chistics.event][lex->trg_chistics.action_time]) @@ -187,17 +241,6 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables) return 1; } - /* Let us check if trigger with the same name exists */ - while ((name= it++)) - { - if (my_strcasecmp(system_charset_info, lex->ident.str, - name->str) == 0) - { - my_message(ER_TRG_ALREADY_EXISTS, ER(ER_TRG_ALREADY_EXISTS), MYF(0)); - return 1; - } - } - /* Let us check if all references to fields in old/new versions of row in this trigger are ok. @@ -234,6 +277,25 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables) file.length= strxnmov(file_buff, FN_REFLEN, tables->table_name, triggers_file_ext, NullS) - file_buff; file.str= file_buff; + trigname_file.length= strxnmov(trigname_buff, FN_REFLEN, + lex->spname->m_name.str, + trigname_file_ext, NullS) - trigname_buff; + trigname_file.str= trigname_buff; + strxnmov(trigname_path, FN_REFLEN, dir_buff, trigname_buff, NullS); + + /* Use the filesystem to enforce trigger namespace constraints. */ + if (!access(trigname_path, F_OK)) + { + my_error(ER_TRG_ALREADY_EXISTS, MYF(0)); + return 1; + } + + trigname.trigger_table.str= tables->table_name; + trigname.trigger_table.length= tables->table_name_length; + + if (sql_create_definition_file(&dir, &trigname_file, &trigname_file_type, + (gptr)&trigname, trigname_file_parameters, 0)) + return 1; /* Soon we will invalidate table object and thus Table_triggers_list object @@ -246,13 +308,66 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables) if (!(trg_def= (LEX_STRING *)alloc_root(&table->mem_root, sizeof(LEX_STRING))) || definitions_list.push_back(trg_def, &table->mem_root)) - return 1; + goto err_with_cleanup; trg_def->str= thd->query; trg_def->length= thd->query_length; - return sql_create_definition_file(&dir, &file, &triggers_file_type, - (gptr)this, triggers_file_parameters, 3); + if (!sql_create_definition_file(&dir, &file, &triggers_file_type, + (gptr)this, triggers_file_parameters, 3)) + return 0; + +err_with_cleanup: + my_delete(trigname_path, MYF(MY_WME)); + return 1; +} + + +/* + Deletes the .TRG file for a table + + SYNOPSIS + rm_trigger_file() + path - char buffer of size FN_REFLEN to be used + for constructing path to .TRG file. + db - table's database name + table_name - table's name + + RETURN VALUE + False - success + True - error +*/ + +static bool rm_trigger_file(char *path, char *db, char *table_name) +{ + strxnmov(path, FN_REFLEN, mysql_data_home, "/", db, "/", table_name, + triggers_file_ext, NullS); + unpack_filename(path, path); + return my_delete(path, MYF(MY_WME)); +} + + +/* + Deletes the .TRN file for a trigger + + SYNOPSIS + rm_trigname_file() + path - char buffer of size FN_REFLEN to be used + for constructing path to .TRN file. + db - trigger's database name + table_name - trigger's name + + RETURN VALUE + False - success + True - error +*/ + +static bool rm_trigname_file(char *path, char *db, char *trigger_name) +{ + strxnmov(path, FN_REFLEN, mysql_data_home, "/", db, "/", trigger_name, + trigname_file_ext, NullS); + unpack_filename(path, path); + return my_delete(path, MYF(MY_WME)); } @@ -275,12 +390,13 @@ bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables) LEX_STRING *name; List_iterator_fast it_name(names_list); List_iterator it_def(definitions_list); + char path[FN_REFLEN]; while ((name= it_name++)) { it_def++; - if (my_strcasecmp(system_charset_info, lex->ident.str, + if (my_strcasecmp(system_charset_info, lex->spname->m_name.str, name->str) == 0) { /* @@ -291,18 +407,14 @@ bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables) if (definitions_list.is_empty()) { - char path[FN_REFLEN]; - /* TODO: Probably instead of removing .TRG file we should move to archive directory but this should be done as part of parse_file.cc functionality (because we will need it elsewhere). */ - strxnmov(path, FN_REFLEN, mysql_data_home, "/", tables->db, "/", - tables->table_name, triggers_file_ext, NullS); - unpack_filename(path, path); - return my_delete(path, MYF(MY_WME)); + if (rm_trigger_file(path, tables->db, tables->table_name)) + return 1; } else { @@ -317,10 +429,15 @@ bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables) triggers_file_ext, NullS) - file_buff; file.str= file_buff; - return sql_create_definition_file(&dir, &file, &triggers_file_type, - (gptr)this, - triggers_file_parameters, 3); + if (sql_create_definition_file(&dir, &file, &triggers_file_type, + (gptr)this, triggers_file_parameters, + 3)) + return 1; } + + if (rm_trigname_file(path, tables->db, lex->spname->m_name.str)) + return 1; + return 0; } } @@ -331,8 +448,8 @@ bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables) Table_triggers_list::~Table_triggers_list() { - for (int i= 0; i < 3; i++) - for (int j= 0; j < 2; j++) + for (int i= 0; i < (int)TRG_EVENT_MAX; i++) + for (int j= 0; j < (int)TRG_ACTION_MAX; j++) delete bodies[i][j]; if (record1_field) @@ -389,13 +506,16 @@ bool Table_triggers_list::prepare_record1_accessors(TABLE *table) db - table's database name table_name - table's name table - pointer to table object + names_only - stop after loading trigger names RETURN VALUE False - success True - error */ + bool Table_triggers_list::check_n_load(THD *thd, const char *db, - const char *table_name, TABLE *table) + const char *table_name, TABLE *table, + bool names_only) { char path_buff[FN_REFLEN]; LEX_STRING path; @@ -451,7 +571,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, TODO: This could be avoided if there is no triggers for UPDATE and DELETE. */ - if (triggers->prepare_record1_accessors(table)) + if (!names_only && triggers->prepare_record1_accessors(table)) DBUG_RETURN(1); List_iterator_fast it(triggers->definitions_list); @@ -471,32 +591,20 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, Free lex associated resources QQ: Do we really need all this stuff here ? */ - if (lex.sphead) - { - delete lex.sphead; - lex.sphead= 0; - } + delete lex.sphead; goto err_with_lex_cleanup; } triggers->bodies[lex.trg_chistics.event] [lex.trg_chistics.action_time]= lex.sphead; - lex.sphead= 0; + if (triggers->names_list.push_back(&lex.sphead->m_name, &table->mem_root)) + goto err_with_lex_cleanup; - if (!(trg_name_buff= alloc_root(&table->mem_root, - sizeof(LEX_STRING) + - lex.ident.length + 1))) - goto err_with_lex_cleanup; - - trg_name_str= (LEX_STRING *)trg_name_buff; - trg_name_buff+= sizeof(LEX_STRING); - memcpy(trg_name_buff, lex.ident.str, - lex.ident.length + 1); - trg_name_str->str= trg_name_buff; - trg_name_str->length= lex.ident.length; - - if (triggers->names_list.push_back(trg_name_str, &table->mem_root)) - goto err_with_lex_cleanup; + if (names_only) + { + lex_end(&lex); + continue; + } /* Let us bind Item_trigger_field objects representing access to fields @@ -537,3 +645,160 @@ err_with_lex_cleanup: DBUG_RETURN(1); } + + +/* + Obtains and returns trigger metadata + + SYNOPSIS + get_trigger_info() + thd - current thread context + event - trigger event type + time_type - trigger action time + name - returns name of trigger + stmt - returns statement of trigger + + RETURN VALUE + False - success + True - error +*/ + +bool Table_triggers_list::get_trigger_info(THD *thd, trg_event_type event, + trg_action_time_type time_type, + LEX_STRING *trigger_name, + LEX_STRING *trigger_stmt) +{ + sp_head *body; + DBUG_ENTER("get_trigger_info"); + if ((body= bodies[event][time_type])) + { + *trigger_name= body->m_name; + *trigger_stmt= body->m_body; + DBUG_RETURN(0); + } + DBUG_RETURN(1); +} + + +/* + Find trigger's table from trigger identifier and add it to + the statement table list. + + SYNOPSIS + mysql_table_for_trigger() + thd - current thread context + trig - identifier for trigger + + RETURN VALUE + 0 - error + # - pointer to TABLE_LIST object for the table +*/ + +static TABLE_LIST *add_table_for_trigger(THD *thd, sp_name *trig) +{ + const char *db= !trig->m_db.str ? thd->db : trig->m_db.str; + LEX *lex= thd->lex; + char path_buff[FN_REFLEN]; + LEX_STRING path; + File_parser *parser; + struct st_trigname trigname; + DBUG_ENTER("add_table_for_trigger"); + + strxnmov(path_buff, FN_REFLEN, mysql_data_home, "/", db, "/", + trig->m_name.str, trigname_file_ext, NullS); + path.length= unpack_filename(path_buff, path_buff); + path.str= path_buff; + + if (access(path_buff, F_OK)) + { + my_error(ER_TRG_DOES_NOT_EXIST, MYF(0)); + DBUG_RETURN(0); + } + + if (!(parser= sql_parse_prepare(&path, thd->mem_root, 1))) + DBUG_RETURN(0); + + if (strncmp(trigname_file_type.str, parser->type()->str, + parser->type()->length)) + { + my_error(ER_WRONG_OBJECT, MYF(0), trig->m_name.str, trigname_file_ext, + "TRIGGERNAME"); + DBUG_RETURN(0); + } + + if (parser->parse((gptr)&trigname, thd->mem_root, + trigname_file_parameters, 1)) + DBUG_RETURN(0); + + /* We need to reset statement table list to be PS/SP friendly. */ + lex->query_tables= 0; + lex->query_tables_last= &lex->query_tables; + DBUG_RETURN(sp_add_to_query_tables(thd, lex, db, + trigname.trigger_table.str, TL_WRITE)); +} + + +/* + Drop all triggers for table. + + SYNOPSIS + drop_all_triggers() + thd - current thread context + db - schema for table + name - name for table + + NOTE + The calling thread should hold the LOCK_open mutex; + + RETURN VALUE + False - success + True - error +*/ + +bool Table_triggers_list::drop_all_triggers(THD *thd, char *db, char *name) +{ + TABLE table; + char path[FN_REFLEN]; + bool result= 0; + DBUG_ENTER("drop_all_triggers"); + + bzero(&table, sizeof(table)); + init_alloc_root(&table.mem_root, 8192, 0); + + safe_mutex_assert_owner(&LOCK_open); + + if (Table_triggers_list::check_n_load(thd, db, name, &table, 1)) + { + result= 1; + goto end; + } + if (table.triggers) + { + LEX_STRING *trigger; + List_iterator_fast it_name(table.triggers->names_list); + + while ((trigger= it_name++)) + { + if (rm_trigname_file(path, db, trigger->str)) + { + /* + Instead of immediately bailing out with error if we were unable + to remove .TRN file we will try to drop other files. + */ + result= 1; + continue; + } + } + + if (rm_trigger_file(path, db, name)) + { + result= 1; + goto end; + } + } +end: + if (table.triggers) + delete table.triggers; + free_root(&table.mem_root, MYF(0)); + DBUG_RETURN(result); +} diff --git a/sql/sql_trigger.h b/sql/sql_trigger.h index 044219d5ac9..e751741fa34 100644 --- a/sql/sql_trigger.h +++ b/sql/sql_trigger.h @@ -23,7 +23,7 @@ class Table_triggers_list: public Sql_alloc { /* Triggers as SPs grouped by event, action_time */ - sp_head *bodies[3][2]; + sp_head *bodies[TRG_EVENT_MAX][TRG_ACTION_MAX]; /* Copy of TABLE::Field array with field pointers set to TABLE::record[1] buffer instead of TABLE::record[0] (used for OLD values in on UPDATE @@ -121,9 +121,13 @@ public: return res; } + bool get_trigger_info(THD *thd, trg_event_type event, + trg_action_time_type time_type, + LEX_STRING *trigger_name, LEX_STRING *trigger_stmt); static bool check_n_load(THD *thd, const char *db, const char *table_name, - TABLE *table); + TABLE *table, bool names_only); + static bool drop_all_triggers(THD *thd, char *db, char *table_name); bool has_delete_triggers() { @@ -143,3 +147,6 @@ public: private: bool prepare_record1_accessors(TABLE *table); }; + +extern const LEX_STRING trg_action_time_type_names[]; +extern const LEX_STRING trg_event_type_names[]; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 756a8a6ee3d..d1d8cf5305e 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -599,6 +599,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token TRAILING %token TRANSACTION_SYM %token TRIGGER_SYM +%token TRIGGERS_SYM %token TRIM %token TRUE_SYM %token TRUNCATE_SYM @@ -1266,7 +1267,7 @@ create: } opt_view_list AS select_init check_option {} - | CREATE TRIGGER_SYM ident trg_action_time trg_event + | CREATE TRIGGER_SYM sp_name trg_action_time trg_event ON table_ident FOR_SYM EACH_SYM ROW_SYM { LEX *lex= Lex; @@ -1285,6 +1286,7 @@ create: sp->m_type= TYPE_ENUM_TRIGGER; lex->sphead= sp; + lex->spname= $3; /* We have to turn of CLIENT_MULTI_QUERIES while parsing a stored procedure, otherwise yylex will chop it into pieces @@ -1295,7 +1297,7 @@ create: bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); lex->sphead->m_chistics= &lex->sp_chistics; - lex->sphead->m_body_begin= lex->tok_start; + lex->sphead->m_body_begin= lex->ptr; } sp_proc_stmt { @@ -1303,14 +1305,12 @@ create: sp_head *sp= lex->sphead; lex->sql_command= SQLCOM_CREATE_TRIGGER; - sp->init_strings(YYTHD, lex, NULL); + sp->init_strings(YYTHD, lex, $3); /* Restore flag if it was cleared above */ if (sp->m_old_cmq) YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES; sp->restore_thd_mem_root(YYTHD); - lex->ident= $3; - /* We have to do it after parsing trigger body, because some of sp_proc_stmt alternatives are not saving/restoring LEX, so @@ -5919,19 +5919,11 @@ drop: lex->sql_command= SQLCOM_DROP_VIEW; lex->drop_if_exists= $3; } - | DROP TRIGGER_SYM ident '.' ident + | DROP TRIGGER_SYM sp_name { LEX *lex= Lex; - lex->sql_command= SQLCOM_DROP_TRIGGER; - /* QQ: Could we loosen lock type in certain cases ? */ - if (!lex->select_lex.add_table_to_list(YYTHD, - new Table_ident($3), - (LEX_STRING*) 0, - TL_OPTION_UPDATING, - TL_WRITE)) - YYABORT; - lex->ident= $5; + lex->spname= $3; } ; @@ -6296,6 +6288,15 @@ show_param: if (prepare_schema_table(YYTHD, lex, 0, SCH_TABLE_NAMES)) YYABORT; } + | opt_full TRIGGERS_SYM opt_db wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SELECT; + lex->orig_sql_command= SQLCOM_SHOW_TRIGGERS; + lex->select_lex.db= $3; + if (prepare_schema_table(YYTHD, lex, 0, SCH_TRIGGERS)) + YYABORT; + } | TABLE_SYM STATUS_SYM opt_db wild_and_where { LEX *lex= Lex; @@ -7590,6 +7591,7 @@ keyword_sp: | TEMPTABLE_SYM {} | TEXT_SYM {} | TRANSACTION_SYM {} + | TRIGGERS_SYM {} | TIMESTAMP {} | TIMESTAMP_ADD {} | TIMESTAMP_DIFF {} diff --git a/sql/table.h b/sql/table.h index e5653a1f213..13d44766804 100644 --- a/sql/table.h +++ b/sql/table.h @@ -282,7 +282,7 @@ enum enum_schema_tables SCH_COLLATION_CHARACTER_SET_APPLICABILITY, SCH_PROCEDURES, SCH_STATISTICS, SCH_VIEWS, SCH_USER_PRIVILEGES, SCH_SCHEMA_PRIVILEGES, SCH_TABLE_PRIVILEGES, SCH_COLUMN_PRIVILEGES, SCH_TABLE_CONSTRAINTS, SCH_KEY_COLUMN_USAGE, - SCH_TABLE_NAMES, SCH_OPEN_TABLES, SCH_STATUS, SCH_VARIABLES + SCH_TABLE_NAMES, SCH_OPEN_TABLES, SCH_STATUS, SCH_TRIGGERS, SCH_VARIABLES }; From 8ad9af446baa8ad41b7d06bc107284d7e4aecaa8 Mon Sep 17 00:00:00 2001 From: "dlenev@mysql.com" <> Date: Tue, 19 Jul 2005 21:38:12 +0400 Subject: [PATCH 47/48] After merge fix. --- mysql-test/t/trigger.test | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mysql-test/t/trigger.test b/mysql-test/t/trigger.test index b36140f2ada..f6b3c714d28 100644 --- a/mysql-test/t/trigger.test +++ b/mysql-test/t/trigger.test @@ -388,10 +388,10 @@ use test; # "Triggers: Allowed to create triggers on a subject table in a different DB". create database mysqltest; create table mysqltest.t1 (i int); ---error 1429 +--error ER_TRG_IN_WRONG_SCHEMA create trigger trg1 before insert on mysqltest.t1 for each row set @a:= 1; use mysqltest; ---error 1429 +--error ER_TRG_IN_WRONG_SCHEMA create trigger test.trg1 before insert on t1 for each row set @a:= 1; drop database mysqltest; use test; From 14344b658a2a41a221bc71399c9211145dac8b03 Mon Sep 17 00:00:00 2001 From: "konstantin@mysql.com" <> Date: Tue, 19 Jul 2005 22:21:12 +0400 Subject: [PATCH 48/48] A fix and a test case for Bug#10760 and complementary cleanups. The idea of the patch is that every cursor gets its own lock id for table level locking. Thus cursors are protected from updates performed within the same connection. Additionally a list of transient (must be closed at commit) cursors is maintained and all transient cursors are closed when necessary. Lastly, this patch adds support for deadlock timeouts to TLL locking when using cursors. + post-review fixes. --- include/thr_lock.h | 41 ++++++++- mysys/thr_lock.c | 184 ++++++++++++++++++++++++++----------- sql/examples/ha_archive.cc | 12 ++- sql/examples/ha_archive.h | 9 +- sql/examples/ha_example.cc | 22 +++++ sql/examples/ha_example.h | 4 +- sql/examples/ha_tina.cc | 31 +++++++ sql/examples/ha_tina.h | 14 +-- sql/ha_berkeley.cc | 14 ++- sql/ha_berkeley.h | 7 +- sql/ha_blackhole.cc | 28 ++++++ sql/ha_blackhole.h | 4 +- sql/ha_federated.cc | 31 +++++++ sql/ha_federated.h | 6 +- sql/ha_heap.cc | 24 +++++ sql/ha_heap.h | 3 +- sql/ha_innodb.cc | 27 +++++- sql/ha_innodb.h | 14 +-- sql/ha_myisam.cc | 34 +++++++ sql/ha_myisam.h | 8 +- sql/ha_myisammrg.cc | 24 +++++ sql/ha_myisammrg.h | 2 +- sql/ha_ndbcluster.cc | 5 +- sql/handler.cc | 16 ++-- sql/handler.h | 9 +- sql/lock.cc | 19 +++- sql/mysqld.cc | 10 +- sql/set_var.cc | 4 + sql/sql_class.cc | 20 ++++ sql/sql_class.h | 24 ++++- sql/sql_prepare.cc | 17 ++-- sql/sql_select.cc | 20 +++- sql/sql_select.h | 2 + tests/mysql_client_test.c | 129 ++++++++++++++++++++++++++ 34 files changed, 671 insertions(+), 147 deletions(-) diff --git a/include/thr_lock.h b/include/thr_lock.h index dc4f9968cb7..8c6540d83e2 100644 --- a/include/thr_lock.h +++ b/include/thr_lock.h @@ -62,17 +62,45 @@ enum thr_lock_type { TL_IGNORE=-1, /* Abort new lock request with an error */ TL_WRITE_ONLY}; +enum enum_thr_lock_result { THR_LOCK_SUCCESS= 0, THR_LOCK_ABORTED= 1, + THR_LOCK_WAIT_TIMEOUT= 2, THR_LOCK_DEADLOCK= 3 }; + + extern ulong max_write_lock_count; +extern ulong table_lock_wait_timeout; extern my_bool thr_lock_inited; extern enum thr_lock_type thr_upgraded_concurrent_insert_lock; -typedef struct st_thr_lock_data { +/* + A description of the thread which owns the lock. The address + of an instance of this structure is used to uniquely identify the thread. +*/ + +typedef struct st_thr_lock_info +{ pthread_t thread; + ulong thread_id; + ulong n_cursors; +} THR_LOCK_INFO; + +/* + Lock owner identifier. Globally identifies the lock owner within the + thread and among all the threads. The address of an instance of this + structure is used as id. +*/ + +typedef struct st_thr_lock_owner +{ + THR_LOCK_INFO *info; +} THR_LOCK_OWNER; + + +typedef struct st_thr_lock_data { + THR_LOCK_OWNER *owner; struct st_thr_lock_data *next,**prev; struct st_thr_lock *lock; pthread_cond_t *cond; enum thr_lock_type type; - ulong thread_id; void *status_param; /* Param to status functions */ void *debug_print_param; } THR_LOCK_DATA; @@ -102,13 +130,18 @@ extern LIST *thr_lock_thread_list; extern pthread_mutex_t THR_LOCK_lock; my_bool init_thr_lock(void); /* Must be called once/thread */ +#define thr_lock_owner_init(owner, info_arg) (owner)->info= (info_arg) +void thr_lock_info_init(THR_LOCK_INFO *info); void thr_lock_init(THR_LOCK *lock); void thr_lock_delete(THR_LOCK *lock); void thr_lock_data_init(THR_LOCK *lock,THR_LOCK_DATA *data, void *status_param); -int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type); +enum enum_thr_lock_result thr_lock(THR_LOCK_DATA *data, + THR_LOCK_OWNER *owner, + enum thr_lock_type lock_type); void thr_unlock(THR_LOCK_DATA *data); -int thr_multi_lock(THR_LOCK_DATA **data,uint count); +enum enum_thr_lock_result thr_multi_lock(THR_LOCK_DATA **data, + uint count, THR_LOCK_OWNER *owner); void thr_multi_unlock(THR_LOCK_DATA **data,uint count); void thr_abort_locks(THR_LOCK *lock); void thr_abort_locks_for_thread(THR_LOCK *lock, pthread_t thread); diff --git a/mysys/thr_lock.c b/mysys/thr_lock.c index 85a2b34b851..b12f8234c26 100644 --- a/mysys/thr_lock.c +++ b/mysys/thr_lock.c @@ -84,6 +84,7 @@ multiple read locks. my_bool thr_lock_inited=0; ulong locks_immediate = 0L, locks_waited = 0L; +ulong table_lock_wait_timeout; enum thr_lock_type thr_upgraded_concurrent_insert_lock = TL_WRITE; /* The following constants are only for debug output */ @@ -109,25 +110,32 @@ my_bool init_thr_lock() return 0; } +static inline my_bool +thr_lock_owner_equal(THR_LOCK_OWNER *rhs, THR_LOCK_OWNER *lhs) +{ + return rhs == lhs; +} + + #ifdef EXTRA_DEBUG #define MAX_FOUND_ERRORS 10 /* Report 10 first errors */ static uint found_errors=0; static int check_lock(struct st_lock_list *list, const char* lock_type, - const char *where, my_bool same_thread, bool no_cond) + const char *where, my_bool same_owner, bool no_cond) { THR_LOCK_DATA *data,**prev; uint count=0; - pthread_t first_thread; - LINT_INIT(first_thread); + THR_LOCK_OWNER *first_owner; + LINT_INIT(first_owner); prev= &list->data; if (list->data) { enum thr_lock_type last_lock_type=list->data->type; - if (same_thread && list->data) - first_thread=list->data->thread; + if (same_owner && list->data) + first_owner= list->data->owner; for (data=list->data; data && count++ < MAX_LOCKS ; data=data->next) { if (data->type != last_lock_type) @@ -139,7 +147,8 @@ static int check_lock(struct st_lock_list *list, const char* lock_type, count, lock_type, where); return 1; } - if (same_thread && ! pthread_equal(data->thread,first_thread) && + if (same_owner && + !thr_lock_owner_equal(data->owner, first_owner) && last_lock_type != TL_WRITE_ALLOW_WRITE) { fprintf(stderr, @@ -255,8 +264,8 @@ static void check_locks(THR_LOCK *lock, const char *where, } if (lock->read.data) { - if (!pthread_equal(lock->write.data->thread, - lock->read.data->thread) && + if (!thr_lock_owner_equal(lock->write.data->owner, + lock->read.data->owner) && ((lock->write.data->type > TL_WRITE_DELAYED && lock->write.data->type != TL_WRITE_ONLY) || ((lock->write.data->type == TL_WRITE_CONCURRENT_INSERT || @@ -330,24 +339,32 @@ void thr_lock_delete(THR_LOCK *lock) DBUG_VOID_RETURN; } + +void thr_lock_info_init(THR_LOCK_INFO *info) +{ + info->thread= pthread_self(); + info->thread_id= my_thread_id(); /* for debugging */ + info->n_cursors= 0; +} + /* Initialize a lock instance */ void thr_lock_data_init(THR_LOCK *lock,THR_LOCK_DATA *data, void *param) { data->lock=lock; data->type=TL_UNLOCK; - data->thread=pthread_self(); - data->thread_id=my_thread_id(); /* for debugging */ + data->owner= 0; /* no owner yet */ data->status_param=param; data->cond=0; } -static inline my_bool have_old_read_lock(THR_LOCK_DATA *data,pthread_t thread) +static inline my_bool +have_old_read_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner) { for ( ; data ; data=data->next) { - if ((pthread_equal(data->thread,thread))) + if (thr_lock_owner_equal(data->owner, owner)) return 1; /* Already locked by thread */ } return 0; @@ -365,12 +382,16 @@ static inline my_bool have_specific_lock(THR_LOCK_DATA *data, } -static my_bool wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, - my_bool in_wait_list) +static enum enum_thr_lock_result +wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, + my_bool in_wait_list) { - pthread_cond_t *cond=get_cond(); - struct st_my_thread_var *thread_var=my_thread_var; - int result; + struct st_my_thread_var *thread_var= my_thread_var; + pthread_cond_t *cond= &thread_var->suspend; + struct timeval now; + struct timespec wait_timeout; + enum enum_thr_lock_result result= THR_LOCK_ABORTED; + my_bool can_deadlock= test(data->owner->info->n_cursors); if (!in_wait_list) { @@ -382,31 +403,56 @@ static my_bool wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, /* Set up control struct to allow others to abort locks */ thread_var->current_mutex= &data->lock->mutex; thread_var->current_cond= cond; + data->cond= cond; - data->cond=cond; + if (can_deadlock) + { + gettimeofday(&now, 0); + wait_timeout.tv_sec= now.tv_sec + table_lock_wait_timeout; + wait_timeout.tv_nsec= now.tv_usec * 1000; + } while (!thread_var->abort || in_wait_list) { - pthread_cond_wait(cond,&data->lock->mutex); - if (data->cond != cond) + int rc= can_deadlock ? pthread_cond_timedwait(cond, &data->lock->mutex, + &wait_timeout) : + pthread_cond_wait(cond, &data->lock->mutex); + /* + We must break the wait if one of the following occurs: + - the connection has been aborted (!thread_var->abort), but + this is not a delayed insert thread (in_wait_list). For a delayed + insert thread the proper action at shutdown is, apparently, to + acquire the lock and complete the insert. + - the lock has been granted (data->cond is set to NULL by the granter), + or the waiting has been aborted (additionally data->type is set to + TL_UNLOCK). + - the wait has timed out (rc == ETIMEDOUT) + Order of checks below is important to not report about timeout + if the predicate is true. + */ + if (data->cond == 0) break; + if (rc == ETIMEDOUT) + { + result= THR_LOCK_WAIT_TIMEOUT; + break; + } } if (data->cond || data->type == TL_UNLOCK) { - if (data->cond) /* aborted */ + if (data->cond) /* aborted or timed out */ { if (((*data->prev)=data->next)) /* remove from wait-list */ data->next->prev= data->prev; else wait->last=data->prev; + data->type= TL_UNLOCK; /* No lock */ } - data->type=TL_UNLOCK; /* No lock */ - result=1; /* Didn't get lock */ check_locks(data->lock,"failed wait_for_lock",0); } else { - result=0; + result= THR_LOCK_SUCCESS; statistic_increment(locks_waited, &THR_LOCK_lock); if (data->lock->get_status) (*data->lock->get_status)(data->status_param, 0); @@ -423,20 +469,24 @@ static my_bool wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, } -int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type) +enum enum_thr_lock_result +thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner, + enum thr_lock_type lock_type) { THR_LOCK *lock=data->lock; - int result=0; + enum enum_thr_lock_result result= THR_LOCK_SUCCESS; + struct st_lock_list *wait_queue; + THR_LOCK_DATA *lock_owner; DBUG_ENTER("thr_lock"); data->next=0; data->cond=0; /* safety */ data->type=lock_type; - data->thread=pthread_self(); /* Must be reset ! */ - data->thread_id=my_thread_id(); /* Must be reset ! */ + data->owner= owner; /* Must be reset ! */ VOID(pthread_mutex_lock(&lock->mutex)); DBUG_PRINT("lock",("data: 0x%lx thread: %ld lock: 0x%lx type: %d", - data,data->thread_id,lock,(int) lock_type)); + data, data->owner->info->thread_id, + lock, (int) lock_type)); check_locks(lock,(uint) lock_type <= (uint) TL_READ_NO_INSERT ? "enter read_lock" : "enter write_lock",0); if ((int) lock_type <= (int) TL_READ_NO_INSERT) @@ -454,8 +504,8 @@ int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type) */ DBUG_PRINT("lock",("write locked by thread: %ld", - lock->write.data->thread_id)); - if (pthread_equal(data->thread,lock->write.data->thread) || + lock->write.data->owner->info->thread_id)); + if (thr_lock_owner_equal(data->owner, lock->write.data->owner) || (lock->write.data->type <= TL_WRITE_DELAYED && (((int) lock_type <= (int) TL_READ_HIGH_PRIORITY) || (lock->write.data->type != TL_WRITE_CONCURRENT_INSERT && @@ -476,14 +526,14 @@ int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type) { /* We are not allowed to get a READ lock in this case */ data->type=TL_UNLOCK; - result=1; /* Can't wait for this one */ + result= THR_LOCK_ABORTED; /* Can't wait for this one */ goto end; } } else if (!lock->write_wait.data || lock->write_wait.data->type <= TL_WRITE_LOW_PRIORITY || lock_type == TL_READ_HIGH_PRIORITY || - have_old_read_lock(lock->read.data,data->thread)) + have_old_read_lock(lock->read.data, data->owner)) { /* No important write-locks */ (*lock->read.last)=data; /* Add to running FIFO */ data->prev=lock->read.last; @@ -496,8 +546,12 @@ int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type) statistic_increment(locks_immediate,&THR_LOCK_lock); goto end; } - /* Can't get lock yet; Wait for it */ - DBUG_RETURN(wait_for_lock(&lock->read_wait,data,0)); + /* + We're here if there is an active write lock or no write + lock but a high priority write waiting in the write_wait queue. + In the latter case we should yield the lock to the writer. + */ + wait_queue= &lock->read_wait; } else /* Request for WRITE lock */ { @@ -506,7 +560,7 @@ int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type) if (lock->write.data && lock->write.data->type == TL_WRITE_ONLY) { data->type=TL_UNLOCK; - result=1; /* Can't wait for this one */ + result= THR_LOCK_ABORTED; /* Can't wait for this one */ goto end; } /* @@ -540,7 +594,7 @@ int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type) { /* We are not allowed to get a lock in this case */ data->type=TL_UNLOCK; - result=1; /* Can't wait for this one */ + result= THR_LOCK_ABORTED; /* Can't wait for this one */ goto end; } @@ -549,7 +603,7 @@ int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type) TL_WRITE_ALLOW_WRITE, TL_WRITE_ALLOW_READ or TL_WRITE_DELAYED in the same thread, but this will never happen within MySQL. */ - if (pthread_equal(data->thread,lock->write.data->thread) || + if (thr_lock_owner_equal(data->owner, lock->write.data->owner) || (lock_type == TL_WRITE_ALLOW_WRITE && !lock->write_wait.data && lock->write.data->type == TL_WRITE_ALLOW_WRITE)) @@ -572,7 +626,7 @@ int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type) goto end; } DBUG_PRINT("lock",("write locked by thread: %ld", - lock->write.data->thread_id)); + lock->write.data->owner->info->thread_id)); } else { @@ -608,10 +662,24 @@ int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type) } } DBUG_PRINT("lock",("write locked by thread: %ld, type: %ld", - lock->read.data->thread_id,data->type)); + lock->read.data->owner->info->thread_id, data->type)); } - DBUG_RETURN(wait_for_lock(&lock->write_wait,data,0)); + wait_queue= &lock->write_wait; } + /* + Try to detect a trivial deadlock when using cursors: attempt to + lock a table that is already locked by an open cursor within the + same connection. lock_owner can be zero if we succumbed to a high + priority writer in the write_wait queue. + */ + lock_owner= lock->read.data ? lock->read.data : lock->write.data; + if (lock_owner && lock_owner->owner->info == owner->info) + { + result= THR_LOCK_DEADLOCK; + goto end; + } + /* Can't get lock yet; Wait for it */ + DBUG_RETURN(wait_for_lock(wait_queue, data, 0)); end: pthread_mutex_unlock(&lock->mutex); DBUG_RETURN(result); @@ -656,7 +724,7 @@ static inline void free_all_read_locks(THR_LOCK *lock, lock->read_no_write_count++; } DBUG_PRINT("lock",("giving read lock to thread: %ld", - data->thread_id)); + data->owner->info->thread_id)); data->cond=0; /* Mark thread free */ VOID(pthread_cond_signal(cond)); } while ((data=data->next)); @@ -674,7 +742,7 @@ void thr_unlock(THR_LOCK_DATA *data) enum thr_lock_type lock_type=data->type; DBUG_ENTER("thr_unlock"); DBUG_PRINT("lock",("data: 0x%lx thread: %ld lock: 0x%lx", - data,data->thread_id,lock)); + data, data->owner->info->thread_id, lock)); pthread_mutex_lock(&lock->mutex); check_locks(lock,"start of release lock",0); @@ -734,7 +802,7 @@ void thr_unlock(THR_LOCK_DATA *data) (*lock->check_status)(data->status_param)) data->type=TL_WRITE; /* Upgrade lock */ DBUG_PRINT("lock",("giving write lock of type %d to thread: %ld", - data->type,data->thread_id)); + data->type, data->owner->info->thread_id)); { pthread_cond_t *cond=data->cond; data->cond=0; /* Mark thread free */ @@ -842,7 +910,8 @@ static void sort_locks(THR_LOCK_DATA **data,uint count) } -int thr_multi_lock(THR_LOCK_DATA **data,uint count) +enum enum_thr_lock_result +thr_multi_lock(THR_LOCK_DATA **data, uint count, THR_LOCK_OWNER *owner) { THR_LOCK_DATA **pos,**end; DBUG_ENTER("thr_multi_lock"); @@ -852,10 +921,11 @@ int thr_multi_lock(THR_LOCK_DATA **data,uint count) /* lock everything */ for (pos=data,end=data+count; pos < end ; pos++) { - if (thr_lock(*pos,(*pos)->type)) + enum enum_thr_lock_result result= thr_lock(*pos, owner, (*pos)->type); + if (result != THR_LOCK_SUCCESS) { /* Aborted */ thr_multi_unlock(data,(uint) (pos-data)); - DBUG_RETURN(1); + DBUG_RETURN(result); } #ifdef MAIN printf("Thread: %s Got lock: 0x%lx type: %d\n",my_thread_name(), @@ -909,7 +979,7 @@ int thr_multi_lock(THR_LOCK_DATA **data,uint count) } while (pos != data); } #endif - DBUG_RETURN(0); + DBUG_RETURN(THR_LOCK_SUCCESS); } /* free all locks */ @@ -932,7 +1002,7 @@ void thr_multi_unlock(THR_LOCK_DATA **data,uint count) else { DBUG_PRINT("lock",("Free lock: data: 0x%lx thread: %ld lock: 0x%lx", - *pos,(*pos)->thread_id,(*pos)->lock)); + *pos, (*pos)->owner->info->thread_id, (*pos)->lock)); } } DBUG_VOID_RETURN; @@ -952,6 +1022,7 @@ void thr_abort_locks(THR_LOCK *lock) for (data=lock->read_wait.data; data ; data=data->next) { data->type=TL_UNLOCK; /* Mark killed */ + /* It's safe to signal the cond first: we're still holding the mutex. */ pthread_cond_signal(data->cond); data->cond=0; /* Removed from list */ } @@ -985,10 +1056,11 @@ void thr_abort_locks_for_thread(THR_LOCK *lock, pthread_t thread) pthread_mutex_lock(&lock->mutex); for (data= lock->read_wait.data; data ; data= data->next) { - if (pthread_equal(thread, data->thread)) + if (pthread_equal(thread, data->owner->info->thread)) { DBUG_PRINT("info",("Aborting read-wait lock")); data->type= TL_UNLOCK; /* Mark killed */ + /* It's safe to signal the cond first: we're still holding the mutex. */ pthread_cond_signal(data->cond); data->cond= 0; /* Removed from list */ @@ -1000,7 +1072,7 @@ void thr_abort_locks_for_thread(THR_LOCK *lock, pthread_t thread) } for (data= lock->write_wait.data; data ; data= data->next) { - if (pthread_equal(thread, data->thread)) + if (pthread_equal(thread, data->owner->info->thread)) { DBUG_PRINT("info",("Aborting write-wait lock")); data->type= TL_UNLOCK; @@ -1117,7 +1189,8 @@ static void thr_print_lock(const char* name,struct st_lock_list *list) prev= &list->data; for (data=list->data; data && count++ < MAX_LOCKS ; data=data->next) { - printf("0x%lx (%lu:%d); ",(ulong) data,data->thread_id,(int) data->type); + printf("0x%lx (%lu:%d); ", (ulong) data, data->owner->info->thread_id, + (int) data->type); if (data->prev != prev) printf("\nWarning: prev didn't point at previous lock\n"); prev= &data->next; @@ -1250,11 +1323,16 @@ static void *test_thread(void *arg) { int i,j,param=*((int*) arg); THR_LOCK_DATA data[MAX_LOCK_COUNT]; + THR_LOCK_OWNER owner; + THR_LOCK_INFO lock_info; THR_LOCK_DATA *multi_locks[MAX_LOCK_COUNT]; my_thread_init(); printf("Thread %s (%d) started\n",my_thread_name(),param); fflush(stdout); + + thr_lock_info_init(&lock_info); + thr_lock_owner_init(&owner, &lock_info); for (i=0; i < lock_counts[param] ; i++) thr_lock_data_init(locks+tests[param][i].lock_nr,data+i,NULL); for (j=1 ; j < 10 ; j++) /* try locking 10 times */ @@ -1264,7 +1342,7 @@ static void *test_thread(void *arg) multi_locks[i]= &data[i]; data[i].type= tests[param][i].lock_type; } - thr_multi_lock(multi_locks,lock_counts[param]); + thr_multi_lock(multi_locks, lock_counts[param], &owner); pthread_mutex_lock(&LOCK_thread_count); { int tmp=rand() & 7; /* Do something from 0-2 sec */ diff --git a/sql/examples/ha_archive.cc b/sql/examples/ha_archive.cc index c362985f565..e5c35ae019d 100644 --- a/sql/examples/ha_archive.cc +++ b/sql/examples/ha_archive.cc @@ -149,7 +149,8 @@ static handlerton archive_hton = { 0, /* prepare */ 0, /* recover */ 0, /* commit_by_xid */ - 0 /* rollback_by_xid */ + 0, /* rollback_by_xid */ + HTON_NO_FLAGS }; @@ -208,6 +209,15 @@ bool archive_db_end() return FALSE; } +ha_archive::ha_archive(TABLE *table_arg) + :handler(&archive_hton, table_arg), delayed_insert(0), bulk_insert(0) +{ + /* Set our original buffer from pre-allocated memory */ + buffer.set((char *)byte_buffer, IO_SIZE, system_charset_info); + + /* The size of the offset value we will use for position() */ + ref_length = sizeof(z_off_t); +} /* This method reads the header of a datafile and returns whether or not it was successful. diff --git a/sql/examples/ha_archive.h b/sql/examples/ha_archive.h index 3932b62980c..41835c5fb6f 100644 --- a/sql/examples/ha_archive.h +++ b/sql/examples/ha_archive.h @@ -58,14 +58,7 @@ class ha_archive: public handler bool bulk_insert; /* If we are performing a bulk insert */ public: - ha_archive(TABLE *table): handler(table), delayed_insert(0), bulk_insert(0) - { - /* Set our original buffer from pre-allocated memory */ - buffer.set((char *)byte_buffer, IO_SIZE, system_charset_info); - - /* The size of the offset value we will use for position() */ - ref_length = sizeof(z_off_t); - } + ha_archive(TABLE *table_arg); ~ha_archive() { } diff --git a/sql/examples/ha_example.cc b/sql/examples/ha_example.cc index 9da297ccd1f..2818c176cd3 100644 --- a/sql/examples/ha_example.cc +++ b/sql/examples/ha_example.cc @@ -72,6 +72,24 @@ #ifdef HAVE_EXAMPLE_DB #include "ha_example.h" + +static handlerton example_hton= { + "CSV", + 0, /* slot */ + 0, /* savepoint size. */ + 0, /* close_connection */ + 0, /* savepoint */ + 0, /* rollback to savepoint */ + 0, /* release savepoint */ + 0, /* commit */ + 0, /* rollback */ + 0, /* prepare */ + 0, /* recover */ + 0, /* commit_by_xid */ + 0, /* rollback_by_xid */ + HTON_NO_FLAGS +}; + /* Variables for example share methods */ 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 @@ -179,6 +197,10 @@ static int free_share(EXAMPLE_SHARE *share) } +ha_example::ha_example(TABLE *table_arg) + :handler(&example_hton, table_arg) +{} + /* 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 diff --git a/sql/examples/ha_example.h b/sql/examples/ha_example.h index ae72e5bb275..37f38fe5210 100644 --- a/sql/examples/ha_example.h +++ b/sql/examples/ha_example.h @@ -45,9 +45,7 @@ class ha_example: public handler EXAMPLE_SHARE *share; /* Shared lock info */ public: - ha_example(TABLE *table): handler(table) - { - } + ha_example(TABLE *table_arg); ~ha_example() { } diff --git a/sql/examples/ha_tina.cc b/sql/examples/ha_tina.cc index a030960d08a..9c774c1f75c 100644 --- a/sql/examples/ha_tina.cc +++ b/sql/examples/ha_tina.cc @@ -54,6 +54,23 @@ pthread_mutex_t tina_mutex; static HASH tina_open_tables; static int tina_init= 0; +static handlerton tina_hton= { + "CSV", + 0, /* slot */ + 0, /* savepoint size. */ + 0, /* close_connection */ + 0, /* savepoint */ + 0, /* rollback to savepoint */ + 0, /* release savepoint */ + 0, /* commit */ + 0, /* rollback */ + 0, /* prepare */ + 0, /* recover */ + 0, /* commit_by_xid */ + 0, /* rollback_by_xid */ + HTON_NO_FLAGS +}; + /***************************************************************************** ** TINA tables *****************************************************************************/ @@ -228,6 +245,20 @@ byte * find_eoln(byte *data, off_t begin, off_t end) return 0; } + +ha_tina::ha_tina(TABLE *table_arg) + :handler(&tina_hton, table_arg), + /* + These definitions are found in hanler.h + These are not probably completely right. + */ + current_position(0), next_position(0), chain_alloced(0), chain_size(DEFAULT_CHAIN_LENGTH) +{ + /* Set our original buffers from pre-allocated memory */ + buffer.set(byte_buffer, IO_SIZE, system_charset_info); + chain= chain_buffer; +} + /* Encode a buffer into the quoted format. */ diff --git a/sql/examples/ha_tina.h b/sql/examples/ha_tina.h index 22193c01013..5679d77a4dc 100644 --- a/sql/examples/ha_tina.h +++ b/sql/examples/ha_tina.h @@ -49,18 +49,8 @@ class ha_tina: public handler byte chain_alloced; uint32 chain_size; - public: - ha_tina(TABLE *table): handler(table), - /* - These definitions are found in hanler.h - Theses are not probably completely right. - */ - current_position(0), next_position(0), chain_alloced(0), chain_size(DEFAULT_CHAIN_LENGTH) - { - /* Set our original buffers from pre-allocated memory */ - buffer.set(byte_buffer, IO_SIZE, system_charset_info); - chain = chain_buffer; - } +public: + ha_tina(TABLE *table_arg); ~ha_tina() { if (chain_alloced) diff --git a/sql/ha_berkeley.cc b/sql/ha_berkeley.cc index 568fb727e63..26e743d4a71 100644 --- a/sql/ha_berkeley.cc +++ b/sql/ha_berkeley.cc @@ -120,7 +120,8 @@ static handlerton berkeley_hton = { NULL, /* prepare */ NULL, /* recover */ NULL, /* commit_by_xid */ - NULL /* rollback_by_xid */ + NULL, /* rollback_by_xid */ + HTON_CLOSE_CURSORS_AT_COMMIT }; typedef struct st_berkeley_trx_data { @@ -372,6 +373,17 @@ void berkeley_cleanup_log_files(void) /***************************************************************************** ** Berkeley DB tables *****************************************************************************/ + +ha_berkeley::ha_berkeley(TABLE *table_arg) + :handler(&berkeley_hton, table_arg), alloc_ptr(0), rec_buff(0), file(0), + int_table_flags(HA_REC_NOT_IN_SEQ | HA_FAST_KEY_READ | + HA_NULL_IN_KEY | HA_CAN_INDEX_BLOBS | HA_NOT_EXACT_COUNT | + HA_PRIMARY_KEY_IN_READ_INDEX | HA_FILE_BASED | + HA_AUTO_PART_KEY | HA_TABLE_SCAN_ON_INDEX), + changed_rows(0), last_dup_key((uint) -1), version(0), using_ignore(0) +{} + + static const char *ha_berkeley_exts[] = { ha_berkeley_ext, NullS diff --git a/sql/ha_berkeley.h b/sql/ha_berkeley.h index f6376939445..aa92908ecde 100644 --- a/sql/ha_berkeley.h +++ b/sql/ha_berkeley.h @@ -85,12 +85,7 @@ class ha_berkeley: public handler DBT *get_pos(DBT *to, byte *pos); public: - ha_berkeley(TABLE *table): handler(table), alloc_ptr(0),rec_buff(0), file(0), - int_table_flags(HA_REC_NOT_IN_SEQ | HA_FAST_KEY_READ | - HA_NULL_IN_KEY | HA_CAN_INDEX_BLOBS | HA_NOT_EXACT_COUNT | - HA_PRIMARY_KEY_IN_READ_INDEX | HA_FILE_BASED | - HA_AUTO_PART_KEY | HA_TABLE_SCAN_ON_INDEX), - changed_rows(0),last_dup_key((uint) -1),version(0),using_ignore(0) {} + ha_berkeley(TABLE *table_arg); ~ha_berkeley() {} const char *table_type() const { return "BerkeleyDB"; } ulong index_flags(uint idx, uint part, bool all_parts) const; diff --git a/sql/ha_blackhole.cc b/sql/ha_blackhole.cc index 6abbe983f48..856a053db9e 100644 --- a/sql/ha_blackhole.cc +++ b/sql/ha_blackhole.cc @@ -24,6 +24,34 @@ #include "ha_blackhole.h" +/* Blackhole storage engine handlerton */ + +static handlerton myisam_hton= { + "BLACKHOLE", + 0, /* slot */ + 0, /* savepoint size. */ + 0, /* close_connection */ + 0, /* savepoint */ + 0, /* rollback to savepoint */ + 0, /* release savepoint */ + 0, /* commit */ + 0, /* rollback */ + 0, /* prepare */ + 0, /* recover */ + 0, /* commit_by_xid */ + 0, /* rollback_by_xid */ + HTON_NO_FLAGS +}; + +/***************************************************************************** +** BLACKHOLE tables +*****************************************************************************/ + +ha_blackhole::ha_blackhole(TABLE *table_arg) + :handler(&blackhole_hton, table_arg) +{} + + static const char *ha_blackhole_exts[] = { NullS }; diff --git a/sql/ha_blackhole.h b/sql/ha_blackhole.h index 84a386e17f8..2dccabf17cc 100644 --- a/sql/ha_blackhole.h +++ b/sql/ha_blackhole.h @@ -28,9 +28,7 @@ class ha_blackhole: public handler THR_LOCK thr_lock; public: - ha_blackhole(TABLE *table): handler(table) - { - } + ha_blackhole(TABLE *table_arg); ~ha_blackhole() { } diff --git a/sql/ha_federated.cc b/sql/ha_federated.cc index e0e35c6b866..1d7b8cda8e2 100644 --- a/sql/ha_federated.cc +++ b/sql/ha_federated.cc @@ -679,6 +679,37 @@ error: } +/* Federated storage engine handlerton */ + +static handlerton federated_hton= { + "FEDERATED", + 0, /* slot */ + 0, /* savepoint size. */ + 0, /* close_connection */ + 0, /* savepoint */ + 0, /* rollback to savepoint */ + 0, /* release savepoint */ + 0, /* commit */ + 0, /* rollback */ + 0, /* prepare */ + 0, /* recover */ + 0, /* commit_by_xid */ + 0, /* rollback_by_xid */ + HTON_NO_FLAGS +}; + + +/***************************************************************************** +** FEDERATED tables +*****************************************************************************/ + +ha_federated::ha_federated(TABLE *table_arg) + :handler(&federated_hton, table_arg), + mysql(0), stored_result(0), scan_flag(0), + ref_length(sizeof(MYSQL_ROW_OFFSET)), current_position(0) +{} + + /* Convert MySQL result set row to handler internal format diff --git a/sql/ha_federated.h b/sql/ha_federated.h index 3e55419f266..58b78ab0dde 100644 --- a/sql/ha_federated.h +++ b/sql/ha_federated.h @@ -162,11 +162,7 @@ private: bool records_in_range); public: - ha_federated(TABLE *table): handler(table), - mysql(0), stored_result(0), scan_flag(0), - ref_length(sizeof(MYSQL_ROW_OFFSET)), current_position(0) - { - } + ha_federated(TABLE *table_arg); ~ha_federated() { } diff --git a/sql/ha_heap.cc b/sql/ha_heap.cc index 6e609a94be3..92a5fe0ea09 100644 --- a/sql/ha_heap.cc +++ b/sql/ha_heap.cc @@ -23,9 +23,33 @@ #include #include "ha_heap.h" +static handlerton heap_hton= { + "MEMORY", + 0, /* slot */ + 0, /* savepoint size. */ + 0, /* close_connection */ + 0, /* savepoint */ + 0, /* rollback to savepoint */ + 0, /* release savepoint */ + 0, /* commit */ + 0, /* rollback */ + 0, /* prepare */ + 0, /* recover */ + 0, /* commit_by_xid */ + 0, /* rollback_by_xid */ + HTON_NO_FLAGS +}; + /***************************************************************************** ** HEAP tables *****************************************************************************/ + +ha_heap::ha_heap(TABLE *table_arg) + :handler(&heap_hton, table_arg), file(0), records_changed(0), + key_stats_ok(0) +{} + + static const char *ha_heap_exts[] = { NullS }; diff --git a/sql/ha_heap.h b/sql/ha_heap.h index 7a97c727049..f7368436456 100644 --- a/sql/ha_heap.h +++ b/sql/ha_heap.h @@ -31,8 +31,7 @@ class ha_heap: public handler uint records_changed; bool key_stats_ok; public: - ha_heap(TABLE *table): handler(table), file(0), records_changed(0), - key_stats_ok(0) {} + ha_heap(TABLE *table); ~ha_heap() {} const char *table_type() const { diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index 98199a22efe..69451493d4b 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -215,7 +215,14 @@ static handlerton innobase_hton = { innobase_xa_prepare, /* prepare */ innobase_xa_recover, /* recover */ innobase_commit_by_xid, /* commit_by_xid */ - innobase_rollback_by_xid /* rollback_by_xid */ + innobase_rollback_by_xid, /* rollback_by_xid */ + /* + For now when one opens a cursor, MySQL does not create an own + InnoDB consistent read view for it, and uses the view of the + currently active transaction. Therefore, cursors can not + survive COMMIT or ROLLBACK statements, which free this view. + */ + HTON_CLOSE_CURSORS_AT_COMMIT }; /********************************************************************* @@ -765,6 +772,24 @@ check_trx_exists( return(trx); } + +/************************************************************************* +Construct ha_innobase handler. */ + +ha_innobase::ha_innobase(TABLE *table_arg) + :handler(&innobase_hton, table_arg), + int_table_flags(HA_REC_NOT_IN_SEQ | + HA_NULL_IN_KEY | + HA_CAN_INDEX_BLOBS | + HA_CAN_SQL_HANDLER | + HA_NOT_EXACT_COUNT | + HA_PRIMARY_KEY_IN_READ_INDEX | + HA_TABLE_SCAN_ON_INDEX), + last_dup_key((uint) -1), + start_of_scan(0), + num_write_row(0) +{} + /************************************************************************* Updates the user_thd field in a handle and also allocates a new InnoDB transaction handle if needed, and updates the transaction fields in the diff --git a/sql/ha_innodb.h b/sql/ha_innodb.h index 90cae3998ed..1584a2182c9 100644 --- a/sql/ha_innodb.h +++ b/sql/ha_innodb.h @@ -81,19 +81,7 @@ class ha_innobase: public handler /* Init values for the class: */ public: - ha_innobase(TABLE *table): handler(table), - int_table_flags(HA_REC_NOT_IN_SEQ | - HA_NULL_IN_KEY | - HA_CAN_INDEX_BLOBS | - HA_CAN_SQL_HANDLER | - HA_NOT_EXACT_COUNT | - HA_PRIMARY_KEY_IN_READ_INDEX | - HA_TABLE_SCAN_ON_INDEX), - last_dup_key((uint) -1), - start_of_scan(0), - num_write_row(0) - { - } + ha_innobase(TABLE *table_arg); ~ha_innobase() {} /* Get the row type from the storage engine. If this method returns diff --git a/sql/ha_myisam.cc b/sql/ha_myisam.cc index e94b697e0bb..fefa05e92b0 100644 --- a/sql/ha_myisam.cc +++ b/sql/ha_myisam.cc @@ -44,6 +44,29 @@ TYPELIB myisam_recover_typelib= {array_elements(myisam_recover_names)-1,"", ** MyISAM tables *****************************************************************************/ +/* MyISAM handlerton */ + +static handlerton myisam_hton= { + "MyISAM", + 0, /* slot */ + 0, /* savepoint size. */ + 0, /* close_connection */ + 0, /* savepoint */ + 0, /* rollback to savepoint */ + 0, /* release savepoint */ + 0, /* commit */ + 0, /* rollback */ + 0, /* prepare */ + 0, /* recover */ + 0, /* commit_by_xid */ + 0, /* rollback_by_xid */ + /* + MyISAM doesn't support transactions and doesn't have + transaction-dependent context: cursors can survive a commit. + */ + HTON_NO_FLAGS +}; + // collect errors printed by mi_check routines static void mi_check_print_msg(MI_CHECK *param, const char* msg_type, @@ -123,6 +146,17 @@ void mi_check_print_warning(MI_CHECK *param, const char *fmt,...) } + +ha_myisam::ha_myisam(TABLE *table_arg) + :handler(&myisam_hton, table_arg), file(0), + int_table_flags(HA_NULL_IN_KEY | HA_CAN_FULLTEXT | HA_CAN_SQL_HANDLER | + HA_DUPP_POS | HA_CAN_INDEX_BLOBS | HA_AUTO_PART_KEY | + HA_FILE_BASED | HA_CAN_GEOMETRY | HA_READ_RND_SAME | + HA_CAN_INSERT_DELAYED | HA_CAN_BIT_FIELD), + can_enable_indexes(1) +{} + + static const char *ha_myisam_exts[] = { ".MYI", ".MYD", diff --git a/sql/ha_myisam.h b/sql/ha_myisam.h index bbd9721f8e2..ca684463311 100644 --- a/sql/ha_myisam.h +++ b/sql/ha_myisam.h @@ -43,13 +43,7 @@ class ha_myisam: public handler int repair(THD *thd, MI_CHECK ¶m, bool optimize); public: - ha_myisam(TABLE *table): handler(table), file(0), - int_table_flags(HA_NULL_IN_KEY | HA_CAN_FULLTEXT | HA_CAN_SQL_HANDLER | - HA_DUPP_POS | HA_CAN_INDEX_BLOBS | HA_AUTO_PART_KEY | - HA_FILE_BASED | HA_CAN_GEOMETRY | HA_READ_RND_SAME | - HA_CAN_INSERT_DELAYED | HA_CAN_BIT_FIELD), - can_enable_indexes(1) - {} + ha_myisam(TABLE *table_arg); ~ha_myisam() {} const char *table_type() const { return "MyISAM"; } const char *index_type(uint key_number); diff --git a/sql/ha_myisammrg.cc b/sql/ha_myisammrg.cc index 5d3f379081c..8c4b4e790b1 100644 --- a/sql/ha_myisammrg.cc +++ b/sql/ha_myisammrg.cc @@ -32,6 +32,30 @@ ** MyISAM MERGE tables *****************************************************************************/ +/* MyISAM MERGE handlerton */ + +static handlerton myisammrg_hton= { + "MRG_MyISAM", + 0, /* slot */ + 0, /* savepoint size. */ + 0, /* close_connection */ + 0, /* savepoint */ + 0, /* rollback to savepoint */ + 0, /* release savepoint */ + 0, /* commit */ + 0, /* rollback */ + 0, /* prepare */ + 0, /* recover */ + 0, /* commit_by_xid */ + 0, /* rollback_by_xid */ + HTON_NO_FLAGS +}; + + +ha_myisammrg::ha_myisammrg(TABLE *table_arg) + :handler(&myisammrg_hton, table_arg), file(0) +{} + static const char *ha_myisammrg_exts[] = { ".MRG", NullS diff --git a/sql/ha_myisammrg.h b/sql/ha_myisammrg.h index 7348096b695..c762b7c286e 100644 --- a/sql/ha_myisammrg.h +++ b/sql/ha_myisammrg.h @@ -28,7 +28,7 @@ class ha_myisammrg: public handler MYRG_INFO *file; public: - ha_myisammrg(TABLE *table): handler(table), file(0) {} + ha_myisammrg(TABLE *table_arg); ~ha_myisammrg() {} const char *table_type() const { return "MRG_MyISAM"; } const char **bas_ext() const; diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index d35b8af80aa..81723690b83 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -62,7 +62,8 @@ static handlerton ndbcluster_hton = { NULL, /* prepare */ NULL, /* recover */ NULL, /* commit_by_xid */ - NULL /* rollback_by_xid */ + NULL, /* rollback_by_xid */ + HTON_NO_FLAGS }; #define NDB_HIDDEN_PRIMARY_KEY_LENGTH 8 @@ -4174,7 +4175,7 @@ ulonglong ha_ndbcluster::get_auto_increment() */ ha_ndbcluster::ha_ndbcluster(TABLE *table_arg): - handler(table_arg), + handler(&ndbcluster_hton, table_arg), m_active_trans(NULL), m_active_cursor(NULL), m_table(NULL), diff --git a/sql/handler.cc b/sql/handler.cc index a61dce35501..cf5ddd95764 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -208,15 +208,8 @@ handler *get_new_handler(TABLE *table, enum db_type db_type) case DB_TYPE_HASH: return new ha_hash(table); #endif -#ifdef HAVE_ISAM - case DB_TYPE_MRG_ISAM: - return new ha_isammrg(table); - case DB_TYPE_ISAM: - return new ha_isam(table); -#else case DB_TYPE_MRG_ISAM: return new ha_myisammrg(table); -#endif #ifdef HAVE_BERKELEY_DB case DB_TYPE_BERKELEY_DB: return new ha_berkeley(table); @@ -634,6 +627,11 @@ int ha_commit_trans(THD *thd, bool all) DBUG_RETURN(1); } DBUG_EXECUTE_IF("crash_commit_before", abort();); + + /* Close all cursors that can not survive COMMIT */ + if (is_real_trans) /* not a statement commit */ + thd->stmt_map.close_transient_cursors(); + if (!trans->no_2pc && trans->nht > 1) { for (; *ht && !error; ht++) @@ -735,6 +733,10 @@ int ha_rollback_trans(THD *thd, bool all) #ifdef USING_TRANSACTIONS if (trans->nht) { + /* Close all cursors that can not survive ROLLBACK */ + if (is_real_trans) /* not a statement commit */ + thd->stmt_map.close_transient_cursors(); + for (handlerton **ht=trans->ht; *ht; ht++) { int err; diff --git a/sql/handler.h b/sql/handler.h index df906e284e7..02b2353b890 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -349,8 +349,13 @@ typedef struct int (*recover)(XID *xid_list, uint len); int (*commit_by_xid)(XID *xid); int (*rollback_by_xid)(XID *xid); + uint32 flags; /* global handler flags */ } handlerton; +/* Possible flags of a handlerton */ +#define HTON_NO_FLAGS 0 +#define HTON_CLOSE_CURSORS_AT_COMMIT 1 + typedef struct st_thd_trans { /* number of entries in the ht[] */ @@ -445,6 +450,7 @@ class handler :public Sql_alloc virtual int rnd_end() { return 0; } public: + const handlerton *ht; /* storage engine of this handler */ byte *ref; /* Pointer to current row */ byte *dupp_ref; /* Pointer to dupp row */ ulonglong data_file_length; /* Length off data file */ @@ -486,7 +492,8 @@ public: bool implicit_emptied; /* Can be !=0 only if HEAP */ const COND *pushed_cond; - handler(TABLE *table_arg) :table(table_arg), + handler(const handlerton *ht_arg, TABLE *table_arg) :table(table_arg), + ht(ht_arg), ref(0), data_file_length(0), max_data_file_length(0), index_file_length(0), delete_length(0), auto_increment_value(0), records(0), deleted(0), mean_rec_length(0), diff --git a/sql/lock.cc b/sql/lock.cc index 7f3fe5ac5da..aa162a23b40 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -103,6 +103,10 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, uint flags) { MYSQL_LOCK *sql_lock; TABLE *write_lock_used; + int rc; + /* Map the return value of thr_lock to an error from errmsg.txt */ + const static int thr_lock_errno_to_mysql[]= + { 0, 1, ER_LOCK_WAIT_TIMEOUT, ER_LOCK_DEADLOCK }; DBUG_ENTER("mysql_lock_tables"); for (;;) @@ -135,15 +139,24 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, uint flags) { my_free((gptr) sql_lock,MYF(0)); sql_lock=0; - thd->proc_info=0; break; } thd->proc_info="Table lock"; thd->locked=1; - if (thr_multi_lock(sql_lock->locks,sql_lock->lock_count)) + rc= thr_lock_errno_to_mysql[(int) thr_multi_lock(sql_lock->locks, + sql_lock->lock_count, + thd->lock_id)]; + if (rc > 1) /* a timeout or a deadlock */ + { + my_error(rc, MYF(0)); + my_free((gptr) sql_lock,MYF(0)); + sql_lock= 0; + break; + } + else if (rc == 1) /* aborted */ { thd->some_tables_deleted=1; // Try again - sql_lock->lock_count=0; // Locks are alread freed + sql_lock->lock_count= 0; // Locks are already freed } else if (!thd->some_tables_deleted || (flags & MYSQL_LOCK_IGNORE_FLUSH)) { diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 7c8b2b781e4..f31a6727619 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -4342,7 +4342,8 @@ enum options_mysqld OPT_ENABLE_LARGE_PAGES, OPT_TIMED_MUTEXES, OPT_OLD_STYLE_USER_LIMITS, - OPT_LOG_SLOW_ADMIN_STATEMENTS + OPT_LOG_SLOW_ADMIN_STATEMENTS, + OPT_TABLE_LOCK_WAIT_TIMEOUT }; @@ -5595,6 +5596,11 @@ The minimum value for this variable is 4096.", "The number of open tables for all threads.", (gptr*) &table_cache_size, (gptr*) &table_cache_size, 0, GET_ULONG, REQUIRED_ARG, 64, 1, 512*1024L, 0, 1, 0}, + {"table_lock_wait_timeout", OPT_TABLE_LOCK_WAIT_TIMEOUT, "Timeout in " + "seconds to wait for a table level lock before returning an error. Used" + " only if the connection has active cursors.", + (gptr*) &table_lock_wait_timeout, (gptr*) &table_lock_wait_timeout, + 0, GET_ULONG, REQUIRED_ARG, 50, 1, 1024 * 1024 * 1024, 0, 1, 0}, {"thread_cache_size", OPT_THREAD_CACHE_SIZE, "How many threads we should keep in a cache for reuse.", (gptr*) &thread_cache_size, (gptr*) &thread_cache_size, 0, GET_ULONG, @@ -7083,4 +7089,6 @@ template class I_List_iterator; template class I_List; template class I_List; template class I_List; +template class I_List; +template class I_List_iterator; #endif diff --git a/sql/set_var.cc b/sql/set_var.cc index 2d7c3364e41..09581aed217 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -375,6 +375,8 @@ sys_var_thd_ulong sys_sync_replication_timeout( sys_var_bool_ptr sys_sync_frm("sync_frm", &opt_sync_frm); sys_var_long_ptr sys_table_cache_size("table_cache", &table_cache_size); +sys_var_long_ptr sys_table_lock_wait_timeout("table_lock_wait_timeout", + &table_lock_wait_timeout); sys_var_long_ptr sys_thread_cache_size("thread_cache_size", &thread_cache_size); sys_var_thd_enum sys_tx_isolation("tx_isolation", @@ -682,6 +684,7 @@ sys_var *sys_variables[]= #endif &sys_sync_frm, &sys_table_cache_size, + &sys_table_lock_wait_timeout, &sys_table_type, &sys_thread_cache_size, &sys_time_format, @@ -972,6 +975,7 @@ struct show_var_st init_vars[]= { {"system_time_zone", system_time_zone, SHOW_CHAR}, #endif {"table_cache", (char*) &table_cache_size, SHOW_LONG}, + {"table_lock_wait_timeout", (char*) &table_lock_wait_timeout, SHOW_LONG }, {sys_table_type.name, (char*) &sys_table_type, SHOW_SYS}, {sys_thread_cache_size.name,(char*) &sys_thread_cache_size, SHOW_SYS}, #ifdef HAVE_THR_SETCONCURRENCY diff --git a/sql/sql_class.cc b/sql/sql_class.cc index d0ac1a16f6b..89d5b543dfc 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -173,6 +173,7 @@ Open_tables_state::Open_tables_state() THD::THD() :Statement(CONVENTIONAL_EXECUTION, 0, ALLOC_ROOT_MIN_BLOCK_SIZE, 0), Open_tables_state(), + lock_id(&main_lock_id), user_time(0), global_read_lock(0), is_fatal_error(0), rand_used(0), time_zone_used(0), last_insert_id_used(0), insert_id_used(0), clear_next_insert_id(0), @@ -265,6 +266,8 @@ THD::THD() tablespace_op=FALSE; ulong tmp=sql_rnd_with_mutex(); randominit(&rand, tmp + (ulong) &rand, tmp + (ulong) ::query_id); + thr_lock_info_init(&lock_info); /* safety: will be reset after start */ + thr_lock_owner_init(&main_lock_id, &lock_info); } @@ -406,6 +409,8 @@ THD::~THD() net_end(&net); } #endif + stmt_map.destroy(); /* close all prepared statements */ + DBUG_ASSERT(lock_info.n_cursors == 0); if (!cleanup_done) cleanup(); @@ -518,6 +523,7 @@ bool THD::store_globals() if this is the slave SQL thread. */ variables.pseudo_thread_id= thread_id; + thr_lock_info_init(&lock_info); return 0; } @@ -1563,6 +1569,12 @@ void Statement::restore_backup_statement(Statement *stmt, Statement *backup) } +void Statement::close_cursor() +{ + DBUG_ASSERT("Statement::close_cursor()" == "not implemented"); +} + + void THD::end_statement() { /* Cleanup SQL processing state to resuse this statement in next query. */ @@ -1683,6 +1695,14 @@ int Statement_map::insert(Statement *statement) } +void Statement_map::close_transient_cursors() +{ + Statement *stmt; + while ((stmt= transient_cursor_list.head())) + stmt->close_cursor(); /* deletes itself from the list */ +} + + bool select_dumpvar::send_data(List &items) { List_iterator_fast li(vars); diff --git a/sql/sql_class.h b/sql/sql_class.h index 6c4315b95fa..d6847f5fb35 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -756,7 +756,7 @@ class Cursor; be used explicitly. */ -class Statement: public Query_arena +class Statement: public ilink, public Query_arena { Statement(const Statement &rhs); /* not implemented: */ Statement &operator=(const Statement &rhs); /* non-copyable */ @@ -833,6 +833,8 @@ public: void restore_backup_statement(Statement *stmt, Statement *backup); /* return class type */ virtual Type type() const; + /* Close the cursor open for this statement, if there is one */ + virtual void close_cursor(); }; @@ -884,15 +886,25 @@ public: } hash_delete(&st_hash, (byte *) statement); } + void add_transient_cursor(Statement *stmt) + { transient_cursor_list.append(stmt); } + void erase_transient_cursor(Statement *stmt) { stmt->unlink(); } + /* + Close all cursors of this connection that use tables of a storage + engine that has transaction-specific state and therefore can not + survive COMMIT or ROLLBACK. Currently all but MyISAM cursors are closed. + */ + void close_transient_cursors(); /* Erase all statements (calls Statement destructor) */ void reset() { my_hash_reset(&names_hash); my_hash_reset(&st_hash); + transient_cursor_list.empty(); last_found_statement= 0; } - ~Statement_map() + void destroy() { hash_free(&names_hash); hash_free(&st_hash); @@ -900,6 +912,7 @@ public: private: HASH st_hash; HASH names_hash; + I_List transient_cursor_list; Statement *last_found_statement; }; @@ -1017,8 +1030,7 @@ public: a thread/connection descriptor */ -class THD :public ilink, - public Statement, +class THD :public Statement, public Open_tables_state { public: @@ -1044,6 +1056,10 @@ public: struct rand_struct rand; // used for authentication struct system_variables variables; // Changeable local variables struct system_status_var status_var; // Per thread statistic vars + THR_LOCK_INFO lock_info; // Locking info of this thread + THR_LOCK_OWNER main_lock_id; // To use for conventional queries + THR_LOCK_OWNER *lock_id; // If not main_lock_id, points to + // the lock_id of a cursor. pthread_mutex_t LOCK_delete; // Locked before thd is deleted /* all prepared statements and cursors of this connection */ Statement_map stmt_map; diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index aa47b506f73..b0f93463a8e 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -2020,6 +2020,7 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) DBUG_VOID_RETURN; /* If lex->result is set, mysql_execute_command will use it */ stmt->lex->result= &cursor->result; + thd->lock_id= &cursor->lock_id; } } #ifndef EMBEDDED_LIBRARY @@ -2069,6 +2070,9 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) Cursor::open is buried deep in JOIN::exec of the top level join. */ cursor->init_from_thd(thd); + + if (cursor->close_at_commit) + thd->stmt_map.add_transient_cursor(stmt); } else { @@ -2078,6 +2082,7 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) } thd->set_statement(&stmt_backup); + thd->lock_id= &thd->main_lock_id; thd->current_arena= thd; DBUG_VOID_RETURN; @@ -2252,6 +2257,8 @@ void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length) the previous calls. */ free_root(cursor->mem_root, MYF(0)); + if (cursor->close_at_commit) + thd->stmt_map.erase_transient_cursor(stmt); } thd->restore_backup_statement(stmt, &stmt_backup); @@ -2291,14 +2298,6 @@ void mysql_stmt_reset(THD *thd, char *packet) DBUG_VOID_RETURN; stmt->close_cursor(); /* will reset statement params */ - cursor= stmt->cursor; - if (cursor && cursor->is_open()) - { - thd->change_list= cursor->change_list; - cursor->close(FALSE); - cleanup_stmt_and_thd_after_use(stmt, thd); - free_root(cursor->mem_root, MYF(0)); - } stmt->state= Query_arena::PREPARED; @@ -2478,6 +2477,8 @@ void Prepared_statement::close_cursor() cursor->close(FALSE); cleanup_stmt_and_thd_after_use(this, thd); free_root(cursor->mem_root, MYF(0)); + if (cursor->close_at_commit) + thd->stmt_map.erase_transient_cursor(this); } /* Clear parameters from data which could be set by diff --git a/sql/sql_select.cc b/sql/sql_select.cc index b333e369dc4..6147a1b62a5 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1702,10 +1702,12 @@ JOIN::destroy() Cursor::Cursor(THD *thd) :Query_arena(&main_mem_root, INITIALIZED), - join(0), unit(0) + join(0), unit(0), + close_at_commit(FALSE) { /* We will overwrite it at open anyway. */ init_sql_alloc(&main_mem_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0); + thr_lock_owner_init(&lock_id, &thd->lock_info); } @@ -1739,6 +1741,21 @@ Cursor::init_from_thd(THD *thd) free_list= thd->free_list; change_list= thd->change_list; reset_thd(thd); + /* Now we have an active cursor and can cause a deadlock */ + thd->lock_info.n_cursors++; + + close_at_commit= FALSE; /* reset in case we're reusing the cursor */ + for (TABLE *table= open_tables; table; table= table->next) + { + const handlerton *ht= table->file->ht; + if (ht) + close_at_commit|= (ht->flags & HTON_CLOSE_CURSORS_AT_COMMIT); + else + { + close_at_commit= TRUE; /* handler status is unknown */ + break; + } + } /* XXX: thd->locked_tables is not changed. What problems can we have with it if cursor is open? @@ -1907,6 +1924,7 @@ Cursor::close(bool is_active) thd->derived_tables= tmp_derived_tables; thd->lock= tmp_lock; } + thd->lock_info.n_cursors--; /* Decrease the number of active cursors */ join= 0; unit= 0; free_items(); diff --git a/sql/sql_select.h b/sql/sql_select.h index 9285e33be33..7f6d661a4de 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -392,6 +392,8 @@ class Cursor: public Sql_alloc, public Query_arena public: Item_change_list change_list; select_send result; + THR_LOCK_OWNER lock_id; + my_bool close_at_commit; /* Temporary implementation as now we replace THD state by value */ /* Save THD state into cursor */ diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 97138e564a4..ffd99915168 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -55,6 +55,7 @@ static char current_db[]= "client_test_db"; static unsigned int test_count= 0; static unsigned int opt_count= 0; static unsigned int iter_count= 0; +static my_bool have_innodb= FALSE; static const char *opt_basedir= "./"; @@ -220,6 +221,28 @@ static void print_st_error(MYSQL_STMT *stmt, const char *msg) } } +/* Check if the connection has InnoDB tables */ + +static my_bool check_have_innodb(MYSQL *conn) +{ + MYSQL_RES *res; + MYSQL_ROW row; + int rc; + my_bool result; + + rc= mysql_query(conn, "show variables like 'have_innodb'"); + myquery(rc); + res= mysql_use_result(conn); + DIE_UNLESS(res); + + row= mysql_fetch_row(res); + DIE_UNLESS(row); + + result= strcmp(row[1], "YES") == 0; + mysql_free_result(res); + return result; +} + /* This is to be what mysql_query() is for mysql_real_query(), for @@ -290,6 +313,7 @@ static void client_connect(ulong flag) strxmov(query, "USE ", current_db, NullS); rc= mysql_query(mysql, query); myquery(rc); + have_innodb= check_have_innodb(mysql); if (!opt_silent) fprintf(stdout, " OK"); @@ -13749,6 +13773,110 @@ static void test_bug11037() myquery(rc); } +/* Bug#10760: cursors, crash in a fetch after rollback. */ + +static void test_bug10760() +{ + MYSQL_STMT *stmt; + MYSQL_BIND bind[1]; + int rc; + const char *stmt_text; + char id_buf[20]; + ulong id_len; + int i= 0; + ulong type; + + myheader("test_bug10760"); + + mysql_query(mysql, "drop table if exists t1, t2"); + + /* create tables */ + rc= mysql_query(mysql, "create table t1 (id integer not null primary key)" + " engine=MyISAM"); + myquery(rc); + for (; i < 42; ++i) + { + char buf[100]; + sprintf(buf, "insert into t1 (id) values (%d)", i+1); + rc= mysql_query(mysql, buf); + myquery(rc); + } + mysql_autocommit(mysql, FALSE); + /* create statement */ + stmt= mysql_stmt_init(mysql); + type= (ulong) CURSOR_TYPE_READ_ONLY; + mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (const void*) &type); + + /* + 1: check that a deadlock within the same connection + is resolved and an error is returned. The deadlock is modelled + as follows: + con1: open cursor for select * from t1; + con1: insert into t1 (id) values (1) + */ + stmt_text= "select id from t1 order by 1"; + rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text)); + check_execute(stmt, rc); + rc= mysql_stmt_execute(stmt); + check_execute(stmt, rc); + rc= mysql_query(mysql, "update t1 set id=id+100"); + DIE_UNLESS(rc); + if (!opt_silent) + printf("Got error (as expected): %s\n", mysql_error(mysql)); + /* + 2: check that MyISAM tables used in cursors survive + COMMIT/ROLLBACK. + */ + rc= mysql_rollback(mysql); /* should not close the cursor */ + myquery(rc); + rc= mysql_stmt_fetch(stmt); + check_execute(stmt, rc); + + /* + 3: check that cursors to InnoDB tables are closed (for now) by + COMMIT/ROLLBACK. + */ + if (! have_innodb) + { + if (!opt_silent) + printf("Testing that cursors are closed at COMMIT/ROLLBACK requires " + "InnoDB.\n"); + } + else + { + stmt_text= "select id from t1 order by 1"; + rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text)); + check_execute(stmt, rc); + + rc= mysql_query(mysql, "alter table t1 engine=InnoDB"); + myquery(rc); + + bzero(bind, sizeof(bind)); + bind[0].buffer_type= MYSQL_TYPE_STRING; + bind[0].buffer= (void*) id_buf; + bind[0].buffer_length= sizeof(id_buf); + bind[0].length= &id_len; + check_execute(stmt, rc); + mysql_stmt_bind_result(stmt, bind); + + rc= mysql_stmt_execute(stmt); + rc= mysql_stmt_fetch(stmt); + DIE_UNLESS(rc == 0); + if (!opt_silent) + printf("Fetched row %s\n", id_buf); + rc= mysql_rollback(mysql); /* should close the cursor */ + myquery(rc); + rc= mysql_stmt_fetch(stmt); + DIE_UNLESS(rc); + if (!opt_silent) + printf("Got error (as expected): %s\n", mysql_error(mysql)); + } + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "drop table t1"); + myquery(rc); + mysql_autocommit(mysql, TRUE); /* restore default */ +} /* Read and parse arguments and MySQL options from my.cnf @@ -13994,6 +14122,7 @@ static struct my_tests_st my_tests[]= { { "test_bug9735", test_bug9735 }, { "test_bug11183", test_bug11183 }, { "test_bug11037", test_bug11037 }, + { "test_bug10760", test_bug10760 }, { 0, 0 } };