From 28eb7825ef16f0fd4daf0c719fd42869a3e73bc9 Mon Sep 17 00:00:00 2001 From: Mikael Ronstrom Date: Thu, 10 Sep 2009 11:14:23 +0200 Subject: [PATCH 1/9] WL#4571, Enable Key cache defined for a partition to enable more scalability on partitioned MyISAM tables among other things --- BUILD/build_mccge.sh | 2 +- sql/ha_partition.cc | 58 ++++++++++++++++++++++++++- sql/ha_partition.h | 5 ++- sql/partition_element.h | 3 +- sql/sql_partition.cc | 2 +- sql/sql_table.cc | 1 + sql/sql_yacc.yy | 87 +++++++++++++++++++++++++++++++---------- 7 files changed, 132 insertions(+), 26 deletions(-) diff --git a/BUILD/build_mccge.sh b/BUILD/build_mccge.sh index ad3e728453c..379ca1b2c68 100755 --- a/BUILD/build_mccge.sh +++ b/BUILD/build_mccge.sh @@ -556,7 +556,7 @@ parse_package() package="pro" ;; extended ) - package="" + package="extended" ;; cge ) package="cge" diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index ac8c46ec4e3..dc5ce9f4bdc 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -847,9 +847,12 @@ int ha_partition::rename_partitions(const char *path) #define ANALYZE_PARTS 2 #define CHECK_PARTS 3 #define REPAIR_PARTS 4 +#define ASSIGN_KEYCACHE_PARTS 5 +#define PRELOAD_KEYS_PARTS 6 static const char *opt_op_name[]= {NULL, - "optimize", "analyze", "check", "repair" }; + "optimize", "analyze", "check", "repair", + "assign_to_keycache", "preload_keys"}; /* Optimize table @@ -934,7 +937,44 @@ int ha_partition::repair(THD *thd, HA_CHECK_OPT *check_opt) DBUG_RETURN(handle_opt_partitions(thd, check_opt, REPAIR_PARTS)); } +/** + Assign to keycache + @param thd Thread object + @param check_opt Check/analyze/repair/optimize options + + @return + @retval >0 Error + @retval 0 Success +*/ + +int ha_partition::assign_to_keycache(THD *thd, HA_CHECK_OPT *check_opt) +{ + DBUG_ENTER("ha_partition::assign_to_keycache"); + + DBUG_RETURN(handle_opt_partitions(thd, check_opt, ASSIGN_KEYCACHE_PARTS)); +} + + +/** + Preload to keycache + + @param thd Thread object + @param check_opt Check/analyze/repair/optimize options + + @return + @retval >0 Error + @retval 0 Success +*/ + +int ha_partition::preload_keys(THD *thd, HA_CHECK_OPT *check_opt) +{ + DBUG_ENTER("ha_partition::preload_keys"); + + DBUG_RETURN(handle_opt_partitions(thd, check_opt, PRELOAD_KEYS_PARTS)); +} + + /* Handle optimize/analyze/check/repair of one partition @@ -965,6 +1005,10 @@ static int handle_opt_part(THD *thd, HA_CHECK_OPT *check_opt, error= file->ha_check(thd, check_opt); else if (flag == REPAIR_PARTS) error= file->ha_repair(thd, check_opt); + else if (flag == ASSIGN_KEYCACHE_PARTS) + error= file->assign_to_keycache(thd, check_opt); + else if (flag == PRELOAD_KEYS_PARTS) + error= file->preload_keys(thd, check_opt); else { DBUG_ASSERT(FALSE); @@ -1094,6 +1138,12 @@ int ha_partition::handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt, "Subpartition %s returned error", sub_elem->partition_name); } + /* reset part_state for the remaining partitions */ + do + { + if (part_elem->part_state == PART_ADMIN) + part_elem->part_state= PART_NORMAL; + } while (part_elem= part_it++); DBUG_RETURN(error); } } while (++j < no_subparts); @@ -1120,6 +1170,12 @@ int ha_partition::handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt, opt_op_name[flag], "Partition %s returned error", part_elem->partition_name); } + /* reset part_state for the remaining partitions */ + do + { + if (part_elem->part_state == PART_ADMIN) + part_elem->part_state= PART_NORMAL; + } while (part_elem= part_it++); DBUG_RETURN(error); } } diff --git a/sql/ha_partition.h b/sql/ha_partition.h index 8a81a759e2a..2f0ce6e7910 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -1061,12 +1061,13 @@ public: virtual int backup(TD* thd, HA_CHECK_OPT *check_opt); virtual int restore(THD* thd, HA_CHECK_OPT *check_opt); - virtual int assign_to_keycache(THD* thd, HA_CHECK_OPT *check_opt); - virtual int preload_keys(THD *thd, HA_CHECK_OPT *check_opt); virtual int dump(THD* thd, int fd = -1); virtual int net_read_dump(NET* net); virtual uint checksum() const; */ + /* Enabled keycache for performance reasons, WL#4571 */ + virtual int assign_to_keycache(THD* thd, HA_CHECK_OPT *check_opt); + virtual int preload_keys(THD* thd, HA_CHECK_OPT* check_opt); /* ------------------------------------------------------------------------- diff --git a/sql/partition_element.h b/sql/partition_element.h index 905bc38165b..bede5264c71 100644 --- a/sql/partition_element.h +++ b/sql/partition_element.h @@ -32,7 +32,8 @@ enum partition_state { PART_REORGED_DROPPED= 5, PART_CHANGED= 6, PART_IS_CHANGED= 7, - PART_IS_ADDED= 8 + PART_IS_ADDED= 8, + PART_ADMIN= 9 }; /* diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 61766e5c509..1c109362a0b 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -4151,7 +4151,7 @@ uint set_part_state(Alter_info *alter_info, partition_info *tab_part_info, /* Mark the partition. I.e mark the partition as a partition to be "changed" by - analyzing/optimizing/rebuilding/checking/repairing + analyzing/optimizing/rebuilding/checking/repairing/... */ no_parts_found++; part_elem->part_state= part_state; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 81d00f46000..9b8c23ce5e9 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -4521,6 +4521,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, /* Set up which partitions that should be processed if ALTER TABLE t ANALYZE/CHECK/OPTIMIZE/REPAIR PARTITION .. + CACHE INDEX/LOAD INDEX for specified partitions */ Alter_info *alter_info= &lex->alter_info; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 4ed9946a334..54458c53570 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1261,7 +1261,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); slave master_def master_defs master_file_def slave_until_opts repair restore backup analyze check start checksum field_list field_list_item field_spec kill column_def key_def - keycache_list assign_to_keycache preload_list preload_keys + keycache_list keycache_list_or_parts assign_to_keycache + assign_to_keycache_parts + preload_list preload_list_or_parts preload_keys preload_keys_parts select_item_list select_item values_list no_braces opt_limit_clause delete_limit_clause fields opt_values values procedure_list procedure_list2 procedure_item @@ -3747,17 +3749,9 @@ opt_partitioning: ; partitioning: - PARTITION_SYM + PARTITION_SYM have_partitioning { -#ifdef WITH_PARTITION_STORAGE_ENGINE LEX *lex= Lex; - LEX_STRING partition_name={C_STRING_WITH_LEN("partition")}; - if (!plugin_is_ready(&partition_name, MYSQL_STORAGE_ENGINE_PLUGIN)) - { - my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), - "--skip-partition"); - MYSQL_YYABORT; - } lex->part_info= new partition_info(); if (!lex->part_info) { @@ -3768,16 +3762,29 @@ partitioning: { lex->alter_info.flags|= ALTER_PARTITION; } -#else - my_error(ER_FEATURE_DISABLED, MYF(0), - "partitioning", "--with-partition"); - MYSQL_YYABORT; -#endif - } partition ; +have_partitioning: + /* empty */ + { +#ifdef WITH_PARTITION_STORAGE_ENGINE + LEX_STRING partition_name={C_STRING_WITH_LEN("partition")}; + if (!plugin_is_ready(&partition_name, MYSQL_STORAGE_ENGINE_PLUGIN)) + { + my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), + "--skip-partition"); + MYSQL_YYABORT; + } +#else + my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), + "--skip-partition"); + MYSQL_YYABORT; +#endif + } + ; + partition_entry: PARTITION_SYM { @@ -5437,7 +5444,6 @@ alter: if (!lex->select_lex.add_table_to_list(thd, $4, NULL, TL_OPTION_UPDATING)) MYSQL_YYABORT; - lex->alter_info.reset(); lex->col_list.empty(); lex->select_lex.init_order(); lex->select_lex.db= @@ -6296,14 +6302,23 @@ table_to_table: ; keycache: - CACHE_SYM INDEX_SYM keycache_list IN_SYM key_cache_name + CACHE_SYM INDEX_SYM + { + Lex->alter_info.reset(); + } + keycache_list_or_parts IN_SYM key_cache_name { LEX *lex=Lex; lex->sql_command= SQLCOM_ASSIGN_TO_KEYCACHE; - lex->ident= $5; + lex->ident= $6; } ; +keycache_list_or_parts: + keycache_list + | assign_to_keycache_parts + ; + keycache_list: assign_to_keycache | keycache_list ',' assign_to_keycache @@ -6318,6 +6333,15 @@ assign_to_keycache: } ; +assign_to_keycache_parts: + table_ident adm_partition cache_keys_spec + { + if (!Select->add_table_to_list(YYTHD, $1, NULL, 0, TL_READ, + Select->pop_index_hints())) + MYSQL_YYABORT; + } + ; + key_cache_name: ident { $$= $1; } | DEFAULT { $$ = default_key_cache_base; } @@ -6328,11 +6352,17 @@ preload: { LEX *lex=Lex; lex->sql_command=SQLCOM_PRELOAD_KEYS; + lex->alter_info.reset(); } - preload_list + preload_list_or_parts {} ; +preload_list_or_parts: + preload_keys_parts + | preload_list + ; + preload_list: preload_keys | preload_list ',' preload_keys @@ -6347,6 +6377,23 @@ preload_keys: } ; +preload_keys_parts: + table_ident adm_partition cache_keys_spec opt_ignore_leaves + { + if (!Select->add_table_to_list(YYTHD, $1, NULL, $4, TL_READ, + Select->pop_index_hints())) + MYSQL_YYABORT; + } + ; + +adm_partition: + PARTITION_SYM have_partitioning + { + Lex->alter_info.flags|= ALTER_ADMIN_PARTITION; + } + '(' all_or_alt_part_name_list ')' + ; + cache_keys_spec: { Lex->select_lex.alloc_index_hints(YYTHD); From ff1df12a20535be9b94e669f29a6134597dbe4a1 Mon Sep 17 00:00:00 2001 From: Mikael Ronstrom Date: Thu, 10 Sep 2009 11:15:39 +0200 Subject: [PATCH 2/9] WL#4444 Added TRUNCATE partition support, fixes bug#19405 and bug #35111 --- BUILD/build_mccge.sh | 2 +- mysql-test/r/partition_truncate.result | 18 ++ mysql-test/suite/parts/inc/partition_mgm.inc | 90 ++++++++++ .../parts/r/partition_mgm_lc0_archive.result | 12 ++ .../parts/r/partition_mgm_lc0_innodb.result | 164 ++++++++++++++++++ .../parts/r/partition_mgm_lc0_memory.result | 164 ++++++++++++++++++ .../parts/r/partition_mgm_lc0_myisam.result | 164 ++++++++++++++++++ .../parts/r/partition_mgm_lc0_ndb.result | 12 ++ .../parts/r/partition_mgm_lc1_archive.result | 12 ++ .../parts/r/partition_mgm_lc1_innodb.result | 164 ++++++++++++++++++ .../parts/r/partition_mgm_lc1_memory.result | 164 ++++++++++++++++++ .../parts/r/partition_mgm_lc1_myisam.result | 164 ++++++++++++++++++ .../parts/r/partition_mgm_lc1_ndb.result | 12 ++ .../parts/r/partition_mgm_lc2_archive.result | 12 ++ .../parts/r/partition_mgm_lc2_innodb.result | 164 ++++++++++++++++++ .../parts/r/partition_mgm_lc2_memory.result | 164 ++++++++++++++++++ .../parts/r/partition_mgm_lc2_myisam.result | 164 ++++++++++++++++++ .../parts/r/partition_mgm_lc2_ndb.result | 12 ++ .../parts/t/partition_mgm_lc0_archive.test | 1 + .../suite/parts/t/partition_mgm_lc0_ndb.test | 2 + .../parts/t/partition_mgm_lc1_archive.test | 1 + .../suite/parts/t/partition_mgm_lc1_ndb.test | 2 + .../parts/t/partition_mgm_lc2_archive.test | 1 + .../suite/parts/t/partition_mgm_lc2_ndb.test | 2 + mysql-test/t/partition_truncate.test | 26 +++ sql/ha_partition.cc | 83 ++++++++- sql/handler.cc | 3 + sql/partition_element.h | 3 +- sql/sql_delete.cc | 19 +- sql/sql_partition.cc | 2 + sql/sql_table.cc | 2 +- sql/sql_yacc.yy | 16 +- 32 files changed, 1810 insertions(+), 11 deletions(-) create mode 100644 mysql-test/r/partition_truncate.result create mode 100644 mysql-test/t/partition_truncate.test diff --git a/BUILD/build_mccge.sh b/BUILD/build_mccge.sh index ad3e728453c..379ca1b2c68 100755 --- a/BUILD/build_mccge.sh +++ b/BUILD/build_mccge.sh @@ -556,7 +556,7 @@ parse_package() package="pro" ;; extended ) - package="" + package="extended" ;; cge ) package="cge" diff --git a/mysql-test/r/partition_truncate.result b/mysql-test/r/partition_truncate.result new file mode 100644 index 00000000000..8f594a319df --- /dev/null +++ b/mysql-test/r/partition_truncate.result @@ -0,0 +1,18 @@ +drop table if exists t1, t2, t3, t4; +create table t1 (a int) +partition by list (a) +(partition p1 values in (0)); +alter table t1 truncate partition p1,p1; +ERROR HY000: Incorrect partition name +alter table t1 truncate partition p0; +ERROR HY000: Incorrect partition name +drop table t1; +create table t1 (a int) +partition by list (a) +subpartition by hash (a) +subpartitions 1 +(partition p1 values in (1) +(subpartition sp1)); +alter table t1 truncate partition sp1; +ERROR HY000: Incorrect partition name +drop table t1; diff --git a/mysql-test/suite/parts/inc/partition_mgm.inc b/mysql-test/suite/parts/inc/partition_mgm.inc index 1ab548222a8..9dfa2b2ffb3 100644 --- a/mysql-test/suite/parts/inc/partition_mgm.inc +++ b/mysql-test/suite/parts/inc/partition_mgm.inc @@ -13,6 +13,7 @@ # part_optA-D Extra partitioning options (E.g. INDEX/DATA DIR) # # # # have_bug33158 NDB case insensitive create, but case sensitive rename # +# no_truncate No support for truncate partition # #------------------------------------------------------------------------------# # Original Author: mattiasj # # Original Date: 2008-06-27 # @@ -518,6 +519,95 @@ DROP TABLE TableA; } # End of $can_only_key +if ($no_truncate) +{ +--echo # Verify that TRUNCATE PARTITION gives error +eval CREATE TABLE t1 +(a BIGINT AUTO_INCREMENT PRIMARY KEY, + b VARCHAR(255)) +ENGINE = $engine +PARTITION BY KEY (a) +(PARTITION LT1000, + PARTITION LT2000, + PARTITION MAX); +INSERT INTO t1 VALUES (NULL, "First"), (NULL, "Second"), (999, "Last in LT1000"), (NULL, "First in LT2000"), (NULL, "Second in LT2000"), (1999, "Last in LT2000"), (NULL, "First in MAX"), (NULL, "Second in MAX"); +--error ER_PARTITION_MGMT_ON_NONPARTITIONED, ER_ILLEGAL_HA +ALTER TABLE t1 TRUNCATE PARTITION MAX; +} +if (!$no_truncate) +{ +--echo # Testing TRUNCATE PARTITION +eval CREATE TABLE t1 +(a BIGINT AUTO_INCREMENT PRIMARY KEY, + b VARCHAR(255)) +ENGINE = $engine +PARTITION BY RANGE (a) +(PARTITION LT1000 VALUES LESS THAN (1000), + PARTITION LT2000 VALUES LESS THAN (2000), + PARTITION MAX VALUES LESS THAN MAXVALUE); +INSERT INTO t1 VALUES (NULL, "First"), (NULL, "Second"), (999, "Last in LT1000"), (NULL, "First in LT2000"), (NULL, "Second in LT2000"), (1999, "Last in LT2000"), (NULL, "First in MAX"), (NULL, "Second in MAX"); +SHOW CREATE TABLE t1; +SELECT * FROM t1 ORDER BY a; +ALTER TABLE t1 ANALYZE PARTITION MAX; +--echo # Truncate without FLUSH +ALTER TABLE t1 TRUNCATE PARTITION MAX; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE MAX (1)"); +SELECT * FROM t1 WHERE a >= 2000; +--echo # Truncate with FLUSH after +ALTER TABLE t1 TRUNCATE PARTITION MAX; +FLUSH TABLES; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE MAX (2)"); +SELECT * FROM t1 WHERE a >= 2000; +--echo # Truncate with FLUSH before +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION MAX; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE MAX (3)"); +SELECT * FROM t1 WHERE a >= 2000; +--echo # Truncate with FLUSH after INSERT +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION MAX; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE MAX (4)"); +SELECT * FROM t1 WHERE a >= 2000; +--echo # Truncate without FLUSH +ALTER TABLE t1 TRUNCATE PARTITION LT1000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT1000 (1)"); +SELECT * FROM t1 ORDER BY a; +--echo # Truncate with FLUSH after +ALTER TABLE t1 TRUNCATE PARTITION LT1000; +FLUSH TABLES; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT1000 (2)"); +SELECT * FROM t1 ORDER BY a; +--echo # Truncate with FLUSH before +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION LT1000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT1000 (3)"); +SELECT * FROM t1 ORDER BY a; +--echo # Truncate with FLUSH after INSERT +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION LT1000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT1000 (4)"); +SELECT * FROM t1 ORDER BY a; +--echo # Truncate without FLUSH +ALTER TABLE t1 TRUNCATE PARTITION LT2000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT2000 (1)"); +SELECT * FROM t1 ORDER BY a; +--echo # Truncate with FLUSH after +ALTER TABLE t1 TRUNCATE PARTITION LT2000; +FLUSH TABLES; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT2000 (2)"); +SELECT * FROM t1 ORDER BY a; +--echo # Truncate with FLUSH before +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION LT2000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT2000 (3)"); +SELECT * FROM t1 ORDER BY a; +--echo # Truncate with FLUSH after INSERT +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION LT2000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT2000 (4)"); +SELECT * FROM t1 ORDER BY a; +DROP TABLE t1; +} --echo # Cleaning up before exit eval USE $old_db; DROP DATABASE MySQL_Test_DB; diff --git a/mysql-test/suite/parts/r/partition_mgm_lc0_archive.result b/mysql-test/suite/parts/r/partition_mgm_lc0_archive.result index 30ff27df298..4f623813386 100644 --- a/mysql-test/suite/parts/r/partition_mgm_lc0_archive.result +++ b/mysql-test/suite/parts/r/partition_mgm_lc0_archive.result @@ -915,6 +915,18 @@ TableA CREATE TABLE `TableA` ( ) ENGINE=ARCHIVE DEFAULT CHARSET=latin1 # Cleaning up after LIST PARTITIONING test DROP TABLE TableA; +# Verify that TRUNCATE PARTITION gives error +CREATE TABLE t1 +(a BIGINT AUTO_INCREMENT PRIMARY KEY, +b VARCHAR(255)) +ENGINE = 'Archive' +PARTITION BY KEY (a) +(PARTITION LT1000, +PARTITION LT2000, +PARTITION MAX); +INSERT INTO t1 VALUES (NULL, "First"), (NULL, "Second"), (999, "Last in LT1000"), (NULL, "First in LT2000"), (NULL, "Second in LT2000"), (1999, "Last in LT2000"), (NULL, "First in MAX"), (NULL, "Second in MAX"); +ALTER TABLE t1 TRUNCATE PARTITION MAX; +Got one of the listed errors # Cleaning up before exit USE test; DROP DATABASE MySQL_Test_DB; diff --git a/mysql-test/suite/parts/r/partition_mgm_lc0_innodb.result b/mysql-test/suite/parts/r/partition_mgm_lc0_innodb.result index cd55ffbad03..19f16780d13 100644 --- a/mysql-test/suite/parts/r/partition_mgm_lc0_innodb.result +++ b/mysql-test/suite/parts/r/partition_mgm_lc0_innodb.result @@ -915,6 +915,170 @@ TableA CREATE TABLE `TableA` ( ) ENGINE=InnoDB DEFAULT CHARSET=latin1 # Cleaning up after LIST PARTITIONING test DROP TABLE TableA; +# Testing TRUNCATE PARTITION +CREATE TABLE t1 +(a BIGINT AUTO_INCREMENT PRIMARY KEY, +b VARCHAR(255)) +ENGINE = 'InnoDB' +PARTITION BY RANGE (a) +(PARTITION LT1000 VALUES LESS THAN (1000), +PARTITION LT2000 VALUES LESS THAN (2000), +PARTITION MAX VALUES LESS THAN MAXVALUE); +INSERT INTO t1 VALUES (NULL, "First"), (NULL, "Second"), (999, "Last in LT1000"), (NULL, "First in LT2000"), (NULL, "Second in LT2000"), (1999, "Last in LT2000"), (NULL, "First in MAX"), (NULL, "Second in MAX"); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` bigint(20) NOT NULL AUTO_INCREMENT, + `b` varchar(255) DEFAULT NULL, + PRIMARY KEY (`a`) +) ENGINE=InnoDB AUTO_INCREMENT=2002 DEFAULT CHARSET=latin1 +/*!50100 PARTITION BY RANGE (a) +(PARTITION LT1000 VALUES LESS THAN (1000) ENGINE = InnoDB, + PARTITION LT2000 VALUES LESS THAN (2000) ENGINE = InnoDB, + PARTITION MAX VALUES LESS THAN MAXVALUE ENGINE = InnoDB) */ +SELECT * FROM t1 ORDER BY a; +a b +1 First +2 Second +999 Last in LT1000 +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First in MAX +2001 Second in MAX +ALTER TABLE t1 ANALYZE PARTITION MAX; +Table Op Msg_type Msg_text +MySQL_Test_DB.t1 analyze status OK +# Truncate without FLUSH +ALTER TABLE t1 TRUNCATE PARTITION MAX; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE MAX (1)"); +SELECT * FROM t1 WHERE a >= 2000; +a b +2000 First after TRUNCATE MAX (1) +# Truncate with FLUSH after +ALTER TABLE t1 TRUNCATE PARTITION MAX; +FLUSH TABLES; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE MAX (2)"); +SELECT * FROM t1 WHERE a >= 2000; +a b +2000 First after TRUNCATE MAX (2) +# Truncate with FLUSH before +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION MAX; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE MAX (3)"); +SELECT * FROM t1 WHERE a >= 2000; +a b +2000 First after TRUNCATE MAX (3) +# Truncate with FLUSH after INSERT +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION MAX; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE MAX (4)"); +SELECT * FROM t1 WHERE a >= 2000; +a b +2000 First after TRUNCATE MAX (4) +# Truncate without FLUSH +ALTER TABLE t1 TRUNCATE PARTITION LT1000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT1000 (1)"); +SELECT * FROM t1 ORDER BY a; +a b +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +# Truncate with FLUSH after +ALTER TABLE t1 TRUNCATE PARTITION LT1000; +FLUSH TABLES; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT1000 (2)"); +SELECT * FROM t1 ORDER BY a; +a b +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +# Truncate with FLUSH before +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION LT1000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT1000 (3)"); +SELECT * FROM t1 ORDER BY a; +a b +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +# Truncate with FLUSH after INSERT +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION LT1000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT1000 (4)"); +SELECT * FROM t1 ORDER BY a; +a b +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +# Truncate without FLUSH +ALTER TABLE t1 TRUNCATE PARTITION LT2000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT2000 (1)"); +SELECT * FROM t1 ORDER BY a; +a b +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +2005 First after TRUNCATE LT2000 (1) +# Truncate with FLUSH after +ALTER TABLE t1 TRUNCATE PARTITION LT2000; +FLUSH TABLES; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT2000 (2)"); +SELECT * FROM t1 ORDER BY a; +a b +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +2005 First after TRUNCATE LT2000 (1) +2006 First after TRUNCATE LT2000 (2) +# Truncate with FLUSH before +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION LT2000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT2000 (3)"); +SELECT * FROM t1 ORDER BY a; +a b +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +2005 First after TRUNCATE LT2000 (1) +2006 First after TRUNCATE LT2000 (2) +2007 First after TRUNCATE LT2000 (3) +# Truncate with FLUSH after INSERT +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION LT2000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT2000 (4)"); +SELECT * FROM t1 ORDER BY a; +a b +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +2005 First after TRUNCATE LT2000 (1) +2006 First after TRUNCATE LT2000 (2) +2007 First after TRUNCATE LT2000 (3) +2008 First after TRUNCATE LT2000 (4) +DROP TABLE t1; # Cleaning up before exit USE test; DROP DATABASE MySQL_Test_DB; diff --git a/mysql-test/suite/parts/r/partition_mgm_lc0_memory.result b/mysql-test/suite/parts/r/partition_mgm_lc0_memory.result index faf776e03a3..69a43b64d87 100644 --- a/mysql-test/suite/parts/r/partition_mgm_lc0_memory.result +++ b/mysql-test/suite/parts/r/partition_mgm_lc0_memory.result @@ -915,6 +915,170 @@ TableA CREATE TABLE `TableA` ( ) ENGINE=MEMORY DEFAULT CHARSET=latin1 # Cleaning up after LIST PARTITIONING test DROP TABLE TableA; +# Testing TRUNCATE PARTITION +CREATE TABLE t1 +(a BIGINT AUTO_INCREMENT PRIMARY KEY, +b VARCHAR(255)) +ENGINE = 'Memory' +PARTITION BY RANGE (a) +(PARTITION LT1000 VALUES LESS THAN (1000), +PARTITION LT2000 VALUES LESS THAN (2000), +PARTITION MAX VALUES LESS THAN MAXVALUE); +INSERT INTO t1 VALUES (NULL, "First"), (NULL, "Second"), (999, "Last in LT1000"), (NULL, "First in LT2000"), (NULL, "Second in LT2000"), (1999, "Last in LT2000"), (NULL, "First in MAX"), (NULL, "Second in MAX"); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` bigint(20) NOT NULL AUTO_INCREMENT, + `b` varchar(255) DEFAULT NULL, + PRIMARY KEY (`a`) +) ENGINE=MEMORY AUTO_INCREMENT=2002 DEFAULT CHARSET=latin1 +/*!50100 PARTITION BY RANGE (a) +(PARTITION LT1000 VALUES LESS THAN (1000) ENGINE = MEMORY, + PARTITION LT2000 VALUES LESS THAN (2000) ENGINE = MEMORY, + PARTITION MAX VALUES LESS THAN MAXVALUE ENGINE = MEMORY) */ +SELECT * FROM t1 ORDER BY a; +a b +1 First +2 Second +999 Last in LT1000 +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First in MAX +2001 Second in MAX +ALTER TABLE t1 ANALYZE PARTITION MAX; +Table Op Msg_type Msg_text +MySQL_Test_DB.t1 analyze note The storage engine for the table doesn't support analyze +# Truncate without FLUSH +ALTER TABLE t1 TRUNCATE PARTITION MAX; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE MAX (1)"); +SELECT * FROM t1 WHERE a >= 2000; +a b +2000 First after TRUNCATE MAX (1) +# Truncate with FLUSH after +ALTER TABLE t1 TRUNCATE PARTITION MAX; +FLUSH TABLES; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE MAX (2)"); +SELECT * FROM t1 WHERE a >= 2000; +a b +2000 First after TRUNCATE MAX (2) +# Truncate with FLUSH before +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION MAX; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE MAX (3)"); +SELECT * FROM t1 WHERE a >= 2000; +a b +2000 First after TRUNCATE MAX (3) +# Truncate with FLUSH after INSERT +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION MAX; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE MAX (4)"); +SELECT * FROM t1 WHERE a >= 2000; +a b +2000 First after TRUNCATE MAX (4) +# Truncate without FLUSH +ALTER TABLE t1 TRUNCATE PARTITION LT1000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT1000 (1)"); +SELECT * FROM t1 ORDER BY a; +a b +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +# Truncate with FLUSH after +ALTER TABLE t1 TRUNCATE PARTITION LT1000; +FLUSH TABLES; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT1000 (2)"); +SELECT * FROM t1 ORDER BY a; +a b +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +# Truncate with FLUSH before +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION LT1000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT1000 (3)"); +SELECT * FROM t1 ORDER BY a; +a b +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +# Truncate with FLUSH after INSERT +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION LT1000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT1000 (4)"); +SELECT * FROM t1 ORDER BY a; +a b +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +# Truncate without FLUSH +ALTER TABLE t1 TRUNCATE PARTITION LT2000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT2000 (1)"); +SELECT * FROM t1 ORDER BY a; +a b +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +2005 First after TRUNCATE LT2000 (1) +# Truncate with FLUSH after +ALTER TABLE t1 TRUNCATE PARTITION LT2000; +FLUSH TABLES; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT2000 (2)"); +SELECT * FROM t1 ORDER BY a; +a b +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +2005 First after TRUNCATE LT2000 (1) +2006 First after TRUNCATE LT2000 (2) +# Truncate with FLUSH before +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION LT2000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT2000 (3)"); +SELECT * FROM t1 ORDER BY a; +a b +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +2005 First after TRUNCATE LT2000 (1) +2006 First after TRUNCATE LT2000 (2) +2007 First after TRUNCATE LT2000 (3) +# Truncate with FLUSH after INSERT +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION LT2000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT2000 (4)"); +SELECT * FROM t1 ORDER BY a; +a b +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +2005 First after TRUNCATE LT2000 (1) +2006 First after TRUNCATE LT2000 (2) +2007 First after TRUNCATE LT2000 (3) +2008 First after TRUNCATE LT2000 (4) +DROP TABLE t1; # Cleaning up before exit USE test; DROP DATABASE MySQL_Test_DB; diff --git a/mysql-test/suite/parts/r/partition_mgm_lc0_myisam.result b/mysql-test/suite/parts/r/partition_mgm_lc0_myisam.result index 827f7a15c24..9b4e85be9d0 100644 --- a/mysql-test/suite/parts/r/partition_mgm_lc0_myisam.result +++ b/mysql-test/suite/parts/r/partition_mgm_lc0_myisam.result @@ -915,6 +915,170 @@ TableA CREATE TABLE `TableA` ( ) ENGINE=MyISAM DEFAULT CHARSET=latin1 # Cleaning up after LIST PARTITIONING test DROP TABLE TableA; +# Testing TRUNCATE PARTITION +CREATE TABLE t1 +(a BIGINT AUTO_INCREMENT PRIMARY KEY, +b VARCHAR(255)) +ENGINE = 'MyISAM' +PARTITION BY RANGE (a) +(PARTITION LT1000 VALUES LESS THAN (1000), +PARTITION LT2000 VALUES LESS THAN (2000), +PARTITION MAX VALUES LESS THAN MAXVALUE); +INSERT INTO t1 VALUES (NULL, "First"), (NULL, "Second"), (999, "Last in LT1000"), (NULL, "First in LT2000"), (NULL, "Second in LT2000"), (1999, "Last in LT2000"), (NULL, "First in MAX"), (NULL, "Second in MAX"); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` bigint(20) NOT NULL AUTO_INCREMENT, + `b` varchar(255) DEFAULT NULL, + PRIMARY KEY (`a`) +) ENGINE=MyISAM AUTO_INCREMENT=2002 DEFAULT CHARSET=latin1 +/*!50100 PARTITION BY RANGE (a) +(PARTITION LT1000 VALUES LESS THAN (1000) ENGINE = MyISAM, + PARTITION LT2000 VALUES LESS THAN (2000) ENGINE = MyISAM, + PARTITION MAX VALUES LESS THAN MAXVALUE ENGINE = MyISAM) */ +SELECT * FROM t1 ORDER BY a; +a b +1 First +2 Second +999 Last in LT1000 +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First in MAX +2001 Second in MAX +ALTER TABLE t1 ANALYZE PARTITION MAX; +Table Op Msg_type Msg_text +MySQL_Test_DB.t1 analyze status OK +# Truncate without FLUSH +ALTER TABLE t1 TRUNCATE PARTITION MAX; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE MAX (1)"); +SELECT * FROM t1 WHERE a >= 2000; +a b +2000 First after TRUNCATE MAX (1) +# Truncate with FLUSH after +ALTER TABLE t1 TRUNCATE PARTITION MAX; +FLUSH TABLES; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE MAX (2)"); +SELECT * FROM t1 WHERE a >= 2000; +a b +2000 First after TRUNCATE MAX (2) +# Truncate with FLUSH before +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION MAX; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE MAX (3)"); +SELECT * FROM t1 WHERE a >= 2000; +a b +2000 First after TRUNCATE MAX (3) +# Truncate with FLUSH after INSERT +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION MAX; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE MAX (4)"); +SELECT * FROM t1 WHERE a >= 2000; +a b +2000 First after TRUNCATE MAX (4) +# Truncate without FLUSH +ALTER TABLE t1 TRUNCATE PARTITION LT1000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT1000 (1)"); +SELECT * FROM t1 ORDER BY a; +a b +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +# Truncate with FLUSH after +ALTER TABLE t1 TRUNCATE PARTITION LT1000; +FLUSH TABLES; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT1000 (2)"); +SELECT * FROM t1 ORDER BY a; +a b +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +# Truncate with FLUSH before +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION LT1000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT1000 (3)"); +SELECT * FROM t1 ORDER BY a; +a b +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +# Truncate with FLUSH after INSERT +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION LT1000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT1000 (4)"); +SELECT * FROM t1 ORDER BY a; +a b +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +# Truncate without FLUSH +ALTER TABLE t1 TRUNCATE PARTITION LT2000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT2000 (1)"); +SELECT * FROM t1 ORDER BY a; +a b +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +2005 First after TRUNCATE LT2000 (1) +# Truncate with FLUSH after +ALTER TABLE t1 TRUNCATE PARTITION LT2000; +FLUSH TABLES; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT2000 (2)"); +SELECT * FROM t1 ORDER BY a; +a b +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +2005 First after TRUNCATE LT2000 (1) +2006 First after TRUNCATE LT2000 (2) +# Truncate with FLUSH before +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION LT2000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT2000 (3)"); +SELECT * FROM t1 ORDER BY a; +a b +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +2005 First after TRUNCATE LT2000 (1) +2006 First after TRUNCATE LT2000 (2) +2007 First after TRUNCATE LT2000 (3) +# Truncate with FLUSH after INSERT +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION LT2000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT2000 (4)"); +SELECT * FROM t1 ORDER BY a; +a b +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +2005 First after TRUNCATE LT2000 (1) +2006 First after TRUNCATE LT2000 (2) +2007 First after TRUNCATE LT2000 (3) +2008 First after TRUNCATE LT2000 (4) +DROP TABLE t1; # Cleaning up before exit USE test; DROP DATABASE MySQL_Test_DB; diff --git a/mysql-test/suite/parts/r/partition_mgm_lc0_ndb.result b/mysql-test/suite/parts/r/partition_mgm_lc0_ndb.result index 45b674638e7..15b3f424527 100644 --- a/mysql-test/suite/parts/r/partition_mgm_lc0_ndb.result +++ b/mysql-test/suite/parts/r/partition_mgm_lc0_ndb.result @@ -181,6 +181,18 @@ TableA CREATE TABLE `TableA` ( ) ENGINE=ndbcluster DEFAULT CHARSET=latin1 # Cleaning up after KEY PARTITIONING test DROP TABLE TableA; +# Verify that TRUNCATE PARTITION gives error +CREATE TABLE t1 +(a BIGINT AUTO_INCREMENT PRIMARY KEY, +b VARCHAR(255)) +ENGINE = 'NDBCluster' +PARTITION BY KEY (a) +(PARTITION LT1000, +PARTITION LT2000, +PARTITION MAX); +INSERT INTO t1 VALUES (NULL, "First"), (NULL, "Second"), (999, "Last in LT1000"), (NULL, "First in LT2000"), (NULL, "Second in LT2000"), (1999, "Last in LT2000"), (NULL, "First in MAX"), (NULL, "Second in MAX"); +ALTER TABLE t1 TRUNCATE PARTITION MAX; +Got one of the listed errors # Cleaning up before exit USE test; DROP DATABASE MySQL_Test_DB; diff --git a/mysql-test/suite/parts/r/partition_mgm_lc1_archive.result b/mysql-test/suite/parts/r/partition_mgm_lc1_archive.result index 443453a2d70..4cd8cafa3ee 100644 --- a/mysql-test/suite/parts/r/partition_mgm_lc1_archive.result +++ b/mysql-test/suite/parts/r/partition_mgm_lc1_archive.result @@ -882,6 +882,18 @@ TableA CREATE TABLE `tablea` ( ) ENGINE=ARCHIVE DEFAULT CHARSET=latin1 # Cleaning up after LIST PARTITIONING test DROP TABLE TableA; +# Verify that TRUNCATE PARTITION gives error +CREATE TABLE t1 +(a BIGINT AUTO_INCREMENT PRIMARY KEY, +b VARCHAR(255)) +ENGINE = 'Archive' +PARTITION BY KEY (a) +(PARTITION LT1000, +PARTITION LT2000, +PARTITION MAX); +INSERT INTO t1 VALUES (NULL, "First"), (NULL, "Second"), (999, "Last in LT1000"), (NULL, "First in LT2000"), (NULL, "Second in LT2000"), (1999, "Last in LT2000"), (NULL, "First in MAX"), (NULL, "Second in MAX"); +ALTER TABLE t1 TRUNCATE PARTITION MAX; +Got one of the listed errors # Cleaning up before exit USE test; DROP DATABASE MySQL_Test_DB; diff --git a/mysql-test/suite/parts/r/partition_mgm_lc1_innodb.result b/mysql-test/suite/parts/r/partition_mgm_lc1_innodb.result index 49ccc7b1808..952f4136cb6 100644 --- a/mysql-test/suite/parts/r/partition_mgm_lc1_innodb.result +++ b/mysql-test/suite/parts/r/partition_mgm_lc1_innodb.result @@ -882,6 +882,170 @@ TableA CREATE TABLE `tablea` ( ) ENGINE=InnoDB DEFAULT CHARSET=latin1 # Cleaning up after LIST PARTITIONING test DROP TABLE TableA; +# Testing TRUNCATE PARTITION +CREATE TABLE t1 +(a BIGINT AUTO_INCREMENT PRIMARY KEY, +b VARCHAR(255)) +ENGINE = 'InnoDB' +PARTITION BY RANGE (a) +(PARTITION LT1000 VALUES LESS THAN (1000), +PARTITION LT2000 VALUES LESS THAN (2000), +PARTITION MAX VALUES LESS THAN MAXVALUE); +INSERT INTO t1 VALUES (NULL, "First"), (NULL, "Second"), (999, "Last in LT1000"), (NULL, "First in LT2000"), (NULL, "Second in LT2000"), (1999, "Last in LT2000"), (NULL, "First in MAX"), (NULL, "Second in MAX"); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` bigint(20) NOT NULL AUTO_INCREMENT, + `b` varchar(255) DEFAULT NULL, + PRIMARY KEY (`a`) +) ENGINE=InnoDB AUTO_INCREMENT=2002 DEFAULT CHARSET=latin1 +/*!50100 PARTITION BY RANGE (a) +(PARTITION LT1000 VALUES LESS THAN (1000) ENGINE = InnoDB, + PARTITION LT2000 VALUES LESS THAN (2000) ENGINE = InnoDB, + PARTITION MAX VALUES LESS THAN MAXVALUE ENGINE = InnoDB) */ +SELECT * FROM t1 ORDER BY a; +a b +1 First +2 Second +999 Last in LT1000 +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First in MAX +2001 Second in MAX +ALTER TABLE t1 ANALYZE PARTITION MAX; +Table Op Msg_type Msg_text +mysql_test_db.t1 analyze status OK +# Truncate without FLUSH +ALTER TABLE t1 TRUNCATE PARTITION MAX; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE MAX (1)"); +SELECT * FROM t1 WHERE a >= 2000; +a b +2000 First after TRUNCATE MAX (1) +# Truncate with FLUSH after +ALTER TABLE t1 TRUNCATE PARTITION MAX; +FLUSH TABLES; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE MAX (2)"); +SELECT * FROM t1 WHERE a >= 2000; +a b +2000 First after TRUNCATE MAX (2) +# Truncate with FLUSH before +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION MAX; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE MAX (3)"); +SELECT * FROM t1 WHERE a >= 2000; +a b +2000 First after TRUNCATE MAX (3) +# Truncate with FLUSH after INSERT +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION MAX; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE MAX (4)"); +SELECT * FROM t1 WHERE a >= 2000; +a b +2000 First after TRUNCATE MAX (4) +# Truncate without FLUSH +ALTER TABLE t1 TRUNCATE PARTITION LT1000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT1000 (1)"); +SELECT * FROM t1 ORDER BY a; +a b +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +# Truncate with FLUSH after +ALTER TABLE t1 TRUNCATE PARTITION LT1000; +FLUSH TABLES; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT1000 (2)"); +SELECT * FROM t1 ORDER BY a; +a b +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +# Truncate with FLUSH before +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION LT1000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT1000 (3)"); +SELECT * FROM t1 ORDER BY a; +a b +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +# Truncate with FLUSH after INSERT +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION LT1000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT1000 (4)"); +SELECT * FROM t1 ORDER BY a; +a b +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +# Truncate without FLUSH +ALTER TABLE t1 TRUNCATE PARTITION LT2000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT2000 (1)"); +SELECT * FROM t1 ORDER BY a; +a b +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +2005 First after TRUNCATE LT2000 (1) +# Truncate with FLUSH after +ALTER TABLE t1 TRUNCATE PARTITION LT2000; +FLUSH TABLES; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT2000 (2)"); +SELECT * FROM t1 ORDER BY a; +a b +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +2005 First after TRUNCATE LT2000 (1) +2006 First after TRUNCATE LT2000 (2) +# Truncate with FLUSH before +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION LT2000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT2000 (3)"); +SELECT * FROM t1 ORDER BY a; +a b +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +2005 First after TRUNCATE LT2000 (1) +2006 First after TRUNCATE LT2000 (2) +2007 First after TRUNCATE LT2000 (3) +# Truncate with FLUSH after INSERT +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION LT2000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT2000 (4)"); +SELECT * FROM t1 ORDER BY a; +a b +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +2005 First after TRUNCATE LT2000 (1) +2006 First after TRUNCATE LT2000 (2) +2007 First after TRUNCATE LT2000 (3) +2008 First after TRUNCATE LT2000 (4) +DROP TABLE t1; # Cleaning up before exit USE test; DROP DATABASE MySQL_Test_DB; diff --git a/mysql-test/suite/parts/r/partition_mgm_lc1_memory.result b/mysql-test/suite/parts/r/partition_mgm_lc1_memory.result index 6f34054428c..435a0d8313e 100644 --- a/mysql-test/suite/parts/r/partition_mgm_lc1_memory.result +++ b/mysql-test/suite/parts/r/partition_mgm_lc1_memory.result @@ -882,6 +882,170 @@ TableA CREATE TABLE `tablea` ( ) ENGINE=MEMORY DEFAULT CHARSET=latin1 # Cleaning up after LIST PARTITIONING test DROP TABLE TableA; +# Testing TRUNCATE PARTITION +CREATE TABLE t1 +(a BIGINT AUTO_INCREMENT PRIMARY KEY, +b VARCHAR(255)) +ENGINE = 'Memory' +PARTITION BY RANGE (a) +(PARTITION LT1000 VALUES LESS THAN (1000), +PARTITION LT2000 VALUES LESS THAN (2000), +PARTITION MAX VALUES LESS THAN MAXVALUE); +INSERT INTO t1 VALUES (NULL, "First"), (NULL, "Second"), (999, "Last in LT1000"), (NULL, "First in LT2000"), (NULL, "Second in LT2000"), (1999, "Last in LT2000"), (NULL, "First in MAX"), (NULL, "Second in MAX"); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` bigint(20) NOT NULL AUTO_INCREMENT, + `b` varchar(255) DEFAULT NULL, + PRIMARY KEY (`a`) +) ENGINE=MEMORY AUTO_INCREMENT=2002 DEFAULT CHARSET=latin1 +/*!50100 PARTITION BY RANGE (a) +(PARTITION LT1000 VALUES LESS THAN (1000) ENGINE = MEMORY, + PARTITION LT2000 VALUES LESS THAN (2000) ENGINE = MEMORY, + PARTITION MAX VALUES LESS THAN MAXVALUE ENGINE = MEMORY) */ +SELECT * FROM t1 ORDER BY a; +a b +1 First +2 Second +999 Last in LT1000 +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First in MAX +2001 Second in MAX +ALTER TABLE t1 ANALYZE PARTITION MAX; +Table Op Msg_type Msg_text +mysql_test_db.t1 analyze note The storage engine for the table doesn't support analyze +# Truncate without FLUSH +ALTER TABLE t1 TRUNCATE PARTITION MAX; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE MAX (1)"); +SELECT * FROM t1 WHERE a >= 2000; +a b +2000 First after TRUNCATE MAX (1) +# Truncate with FLUSH after +ALTER TABLE t1 TRUNCATE PARTITION MAX; +FLUSH TABLES; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE MAX (2)"); +SELECT * FROM t1 WHERE a >= 2000; +a b +2000 First after TRUNCATE MAX (2) +# Truncate with FLUSH before +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION MAX; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE MAX (3)"); +SELECT * FROM t1 WHERE a >= 2000; +a b +2000 First after TRUNCATE MAX (3) +# Truncate with FLUSH after INSERT +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION MAX; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE MAX (4)"); +SELECT * FROM t1 WHERE a >= 2000; +a b +2000 First after TRUNCATE MAX (4) +# Truncate without FLUSH +ALTER TABLE t1 TRUNCATE PARTITION LT1000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT1000 (1)"); +SELECT * FROM t1 ORDER BY a; +a b +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +# Truncate with FLUSH after +ALTER TABLE t1 TRUNCATE PARTITION LT1000; +FLUSH TABLES; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT1000 (2)"); +SELECT * FROM t1 ORDER BY a; +a b +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +# Truncate with FLUSH before +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION LT1000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT1000 (3)"); +SELECT * FROM t1 ORDER BY a; +a b +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +# Truncate with FLUSH after INSERT +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION LT1000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT1000 (4)"); +SELECT * FROM t1 ORDER BY a; +a b +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +# Truncate without FLUSH +ALTER TABLE t1 TRUNCATE PARTITION LT2000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT2000 (1)"); +SELECT * FROM t1 ORDER BY a; +a b +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +2005 First after TRUNCATE LT2000 (1) +# Truncate with FLUSH after +ALTER TABLE t1 TRUNCATE PARTITION LT2000; +FLUSH TABLES; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT2000 (2)"); +SELECT * FROM t1 ORDER BY a; +a b +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +2005 First after TRUNCATE LT2000 (1) +2006 First after TRUNCATE LT2000 (2) +# Truncate with FLUSH before +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION LT2000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT2000 (3)"); +SELECT * FROM t1 ORDER BY a; +a b +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +2005 First after TRUNCATE LT2000 (1) +2006 First after TRUNCATE LT2000 (2) +2007 First after TRUNCATE LT2000 (3) +# Truncate with FLUSH after INSERT +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION LT2000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT2000 (4)"); +SELECT * FROM t1 ORDER BY a; +a b +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +2005 First after TRUNCATE LT2000 (1) +2006 First after TRUNCATE LT2000 (2) +2007 First after TRUNCATE LT2000 (3) +2008 First after TRUNCATE LT2000 (4) +DROP TABLE t1; # Cleaning up before exit USE test; DROP DATABASE MySQL_Test_DB; diff --git a/mysql-test/suite/parts/r/partition_mgm_lc1_myisam.result b/mysql-test/suite/parts/r/partition_mgm_lc1_myisam.result index ac230e29c66..3a90ce4d73c 100644 --- a/mysql-test/suite/parts/r/partition_mgm_lc1_myisam.result +++ b/mysql-test/suite/parts/r/partition_mgm_lc1_myisam.result @@ -882,6 +882,170 @@ TableA CREATE TABLE `tablea` ( ) ENGINE=MyISAM DEFAULT CHARSET=latin1 # Cleaning up after LIST PARTITIONING test DROP TABLE TableA; +# Testing TRUNCATE PARTITION +CREATE TABLE t1 +(a BIGINT AUTO_INCREMENT PRIMARY KEY, +b VARCHAR(255)) +ENGINE = 'MyISAM' +PARTITION BY RANGE (a) +(PARTITION LT1000 VALUES LESS THAN (1000), +PARTITION LT2000 VALUES LESS THAN (2000), +PARTITION MAX VALUES LESS THAN MAXVALUE); +INSERT INTO t1 VALUES (NULL, "First"), (NULL, "Second"), (999, "Last in LT1000"), (NULL, "First in LT2000"), (NULL, "Second in LT2000"), (1999, "Last in LT2000"), (NULL, "First in MAX"), (NULL, "Second in MAX"); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` bigint(20) NOT NULL AUTO_INCREMENT, + `b` varchar(255) DEFAULT NULL, + PRIMARY KEY (`a`) +) ENGINE=MyISAM AUTO_INCREMENT=2002 DEFAULT CHARSET=latin1 +/*!50100 PARTITION BY RANGE (a) +(PARTITION LT1000 VALUES LESS THAN (1000) ENGINE = MyISAM, + PARTITION LT2000 VALUES LESS THAN (2000) ENGINE = MyISAM, + PARTITION MAX VALUES LESS THAN MAXVALUE ENGINE = MyISAM) */ +SELECT * FROM t1 ORDER BY a; +a b +1 First +2 Second +999 Last in LT1000 +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First in MAX +2001 Second in MAX +ALTER TABLE t1 ANALYZE PARTITION MAX; +Table Op Msg_type Msg_text +mysql_test_db.t1 analyze status OK +# Truncate without FLUSH +ALTER TABLE t1 TRUNCATE PARTITION MAX; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE MAX (1)"); +SELECT * FROM t1 WHERE a >= 2000; +a b +2000 First after TRUNCATE MAX (1) +# Truncate with FLUSH after +ALTER TABLE t1 TRUNCATE PARTITION MAX; +FLUSH TABLES; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE MAX (2)"); +SELECT * FROM t1 WHERE a >= 2000; +a b +2000 First after TRUNCATE MAX (2) +# Truncate with FLUSH before +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION MAX; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE MAX (3)"); +SELECT * FROM t1 WHERE a >= 2000; +a b +2000 First after TRUNCATE MAX (3) +# Truncate with FLUSH after INSERT +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION MAX; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE MAX (4)"); +SELECT * FROM t1 WHERE a >= 2000; +a b +2000 First after TRUNCATE MAX (4) +# Truncate without FLUSH +ALTER TABLE t1 TRUNCATE PARTITION LT1000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT1000 (1)"); +SELECT * FROM t1 ORDER BY a; +a b +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +# Truncate with FLUSH after +ALTER TABLE t1 TRUNCATE PARTITION LT1000; +FLUSH TABLES; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT1000 (2)"); +SELECT * FROM t1 ORDER BY a; +a b +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +# Truncate with FLUSH before +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION LT1000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT1000 (3)"); +SELECT * FROM t1 ORDER BY a; +a b +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +# Truncate with FLUSH after INSERT +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION LT1000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT1000 (4)"); +SELECT * FROM t1 ORDER BY a; +a b +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +# Truncate without FLUSH +ALTER TABLE t1 TRUNCATE PARTITION LT2000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT2000 (1)"); +SELECT * FROM t1 ORDER BY a; +a b +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +2005 First after TRUNCATE LT2000 (1) +# Truncate with FLUSH after +ALTER TABLE t1 TRUNCATE PARTITION LT2000; +FLUSH TABLES; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT2000 (2)"); +SELECT * FROM t1 ORDER BY a; +a b +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +2005 First after TRUNCATE LT2000 (1) +2006 First after TRUNCATE LT2000 (2) +# Truncate with FLUSH before +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION LT2000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT2000 (3)"); +SELECT * FROM t1 ORDER BY a; +a b +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +2005 First after TRUNCATE LT2000 (1) +2006 First after TRUNCATE LT2000 (2) +2007 First after TRUNCATE LT2000 (3) +# Truncate with FLUSH after INSERT +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION LT2000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT2000 (4)"); +SELECT * FROM t1 ORDER BY a; +a b +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +2005 First after TRUNCATE LT2000 (1) +2006 First after TRUNCATE LT2000 (2) +2007 First after TRUNCATE LT2000 (3) +2008 First after TRUNCATE LT2000 (4) +DROP TABLE t1; # Cleaning up before exit USE test; DROP DATABASE MySQL_Test_DB; diff --git a/mysql-test/suite/parts/r/partition_mgm_lc1_ndb.result b/mysql-test/suite/parts/r/partition_mgm_lc1_ndb.result index 0a53e1b4a9b..1d221caa163 100644 --- a/mysql-test/suite/parts/r/partition_mgm_lc1_ndb.result +++ b/mysql-test/suite/parts/r/partition_mgm_lc1_ndb.result @@ -219,6 +219,18 @@ TableA CREATE TABLE `tablea` ( ) ENGINE=ndbcluster DEFAULT CHARSET=latin1 # Cleaning up after KEY PARTITIONING test DROP TABLE TableA; +# Verify that TRUNCATE PARTITION gives error +CREATE TABLE t1 +(a BIGINT AUTO_INCREMENT PRIMARY KEY, +b VARCHAR(255)) +ENGINE = 'NDBCluster' +PARTITION BY KEY (a) +(PARTITION LT1000, +PARTITION LT2000, +PARTITION MAX); +INSERT INTO t1 VALUES (NULL, "First"), (NULL, "Second"), (999, "Last in LT1000"), (NULL, "First in LT2000"), (NULL, "Second in LT2000"), (1999, "Last in LT2000"), (NULL, "First in MAX"), (NULL, "Second in MAX"); +ALTER TABLE t1 TRUNCATE PARTITION MAX; +Got one of the listed errors # Cleaning up before exit USE test; DROP DATABASE MySQL_Test_DB; diff --git a/mysql-test/suite/parts/r/partition_mgm_lc2_archive.result b/mysql-test/suite/parts/r/partition_mgm_lc2_archive.result index fc0390c238d..6e8abfef06d 100644 --- a/mysql-test/suite/parts/r/partition_mgm_lc2_archive.result +++ b/mysql-test/suite/parts/r/partition_mgm_lc2_archive.result @@ -882,6 +882,18 @@ TableA CREATE TABLE `TableA` ( ) ENGINE=ARCHIVE DEFAULT CHARSET=latin1 # Cleaning up after LIST PARTITIONING test DROP TABLE TableA; +# Verify that TRUNCATE PARTITION gives error +CREATE TABLE t1 +(a BIGINT AUTO_INCREMENT PRIMARY KEY, +b VARCHAR(255)) +ENGINE = 'Archive' +PARTITION BY KEY (a) +(PARTITION LT1000, +PARTITION LT2000, +PARTITION MAX); +INSERT INTO t1 VALUES (NULL, "First"), (NULL, "Second"), (999, "Last in LT1000"), (NULL, "First in LT2000"), (NULL, "Second in LT2000"), (1999, "Last in LT2000"), (NULL, "First in MAX"), (NULL, "Second in MAX"); +ALTER TABLE t1 TRUNCATE PARTITION MAX; +Got one of the listed errors # Cleaning up before exit USE test; DROP DATABASE MySQL_Test_DB; diff --git a/mysql-test/suite/parts/r/partition_mgm_lc2_innodb.result b/mysql-test/suite/parts/r/partition_mgm_lc2_innodb.result index da111137068..8e42bc9eb62 100644 --- a/mysql-test/suite/parts/r/partition_mgm_lc2_innodb.result +++ b/mysql-test/suite/parts/r/partition_mgm_lc2_innodb.result @@ -882,6 +882,170 @@ TableA CREATE TABLE `TableA` ( ) ENGINE=InnoDB DEFAULT CHARSET=latin1 # Cleaning up after LIST PARTITIONING test DROP TABLE TableA; +# Testing TRUNCATE PARTITION +CREATE TABLE t1 +(a BIGINT AUTO_INCREMENT PRIMARY KEY, +b VARCHAR(255)) +ENGINE = 'InnoDB' +PARTITION BY RANGE (a) +(PARTITION LT1000 VALUES LESS THAN (1000), +PARTITION LT2000 VALUES LESS THAN (2000), +PARTITION MAX VALUES LESS THAN MAXVALUE); +INSERT INTO t1 VALUES (NULL, "First"), (NULL, "Second"), (999, "Last in LT1000"), (NULL, "First in LT2000"), (NULL, "Second in LT2000"), (1999, "Last in LT2000"), (NULL, "First in MAX"), (NULL, "Second in MAX"); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` bigint(20) NOT NULL AUTO_INCREMENT, + `b` varchar(255) DEFAULT NULL, + PRIMARY KEY (`a`) +) ENGINE=InnoDB AUTO_INCREMENT=2002 DEFAULT CHARSET=latin1 +/*!50100 PARTITION BY RANGE (a) +(PARTITION LT1000 VALUES LESS THAN (1000) ENGINE = InnoDB, + PARTITION LT2000 VALUES LESS THAN (2000) ENGINE = InnoDB, + PARTITION MAX VALUES LESS THAN MAXVALUE ENGINE = InnoDB) */ +SELECT * FROM t1 ORDER BY a; +a b +1 First +2 Second +999 Last in LT1000 +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First in MAX +2001 Second in MAX +ALTER TABLE t1 ANALYZE PARTITION MAX; +Table Op Msg_type Msg_text +mysql_test_db.t1 analyze status OK +# Truncate without FLUSH +ALTER TABLE t1 TRUNCATE PARTITION MAX; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE MAX (1)"); +SELECT * FROM t1 WHERE a >= 2000; +a b +2000 First after TRUNCATE MAX (1) +# Truncate with FLUSH after +ALTER TABLE t1 TRUNCATE PARTITION MAX; +FLUSH TABLES; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE MAX (2)"); +SELECT * FROM t1 WHERE a >= 2000; +a b +2000 First after TRUNCATE MAX (2) +# Truncate with FLUSH before +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION MAX; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE MAX (3)"); +SELECT * FROM t1 WHERE a >= 2000; +a b +2000 First after TRUNCATE MAX (3) +# Truncate with FLUSH after INSERT +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION MAX; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE MAX (4)"); +SELECT * FROM t1 WHERE a >= 2000; +a b +2000 First after TRUNCATE MAX (4) +# Truncate without FLUSH +ALTER TABLE t1 TRUNCATE PARTITION LT1000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT1000 (1)"); +SELECT * FROM t1 ORDER BY a; +a b +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +# Truncate with FLUSH after +ALTER TABLE t1 TRUNCATE PARTITION LT1000; +FLUSH TABLES; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT1000 (2)"); +SELECT * FROM t1 ORDER BY a; +a b +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +# Truncate with FLUSH before +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION LT1000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT1000 (3)"); +SELECT * FROM t1 ORDER BY a; +a b +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +# Truncate with FLUSH after INSERT +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION LT1000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT1000 (4)"); +SELECT * FROM t1 ORDER BY a; +a b +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +# Truncate without FLUSH +ALTER TABLE t1 TRUNCATE PARTITION LT2000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT2000 (1)"); +SELECT * FROM t1 ORDER BY a; +a b +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +2005 First after TRUNCATE LT2000 (1) +# Truncate with FLUSH after +ALTER TABLE t1 TRUNCATE PARTITION LT2000; +FLUSH TABLES; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT2000 (2)"); +SELECT * FROM t1 ORDER BY a; +a b +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +2005 First after TRUNCATE LT2000 (1) +2006 First after TRUNCATE LT2000 (2) +# Truncate with FLUSH before +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION LT2000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT2000 (3)"); +SELECT * FROM t1 ORDER BY a; +a b +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +2005 First after TRUNCATE LT2000 (1) +2006 First after TRUNCATE LT2000 (2) +2007 First after TRUNCATE LT2000 (3) +# Truncate with FLUSH after INSERT +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION LT2000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT2000 (4)"); +SELECT * FROM t1 ORDER BY a; +a b +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +2005 First after TRUNCATE LT2000 (1) +2006 First after TRUNCATE LT2000 (2) +2007 First after TRUNCATE LT2000 (3) +2008 First after TRUNCATE LT2000 (4) +DROP TABLE t1; # Cleaning up before exit USE test; DROP DATABASE MySQL_Test_DB; diff --git a/mysql-test/suite/parts/r/partition_mgm_lc2_memory.result b/mysql-test/suite/parts/r/partition_mgm_lc2_memory.result index a1716ea36c8..24047912ab1 100644 --- a/mysql-test/suite/parts/r/partition_mgm_lc2_memory.result +++ b/mysql-test/suite/parts/r/partition_mgm_lc2_memory.result @@ -882,6 +882,170 @@ TableA CREATE TABLE `TableA` ( ) ENGINE=MEMORY DEFAULT CHARSET=latin1 # Cleaning up after LIST PARTITIONING test DROP TABLE TableA; +# Testing TRUNCATE PARTITION +CREATE TABLE t1 +(a BIGINT AUTO_INCREMENT PRIMARY KEY, +b VARCHAR(255)) +ENGINE = 'Memory' +PARTITION BY RANGE (a) +(PARTITION LT1000 VALUES LESS THAN (1000), +PARTITION LT2000 VALUES LESS THAN (2000), +PARTITION MAX VALUES LESS THAN MAXVALUE); +INSERT INTO t1 VALUES (NULL, "First"), (NULL, "Second"), (999, "Last in LT1000"), (NULL, "First in LT2000"), (NULL, "Second in LT2000"), (1999, "Last in LT2000"), (NULL, "First in MAX"), (NULL, "Second in MAX"); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` bigint(20) NOT NULL AUTO_INCREMENT, + `b` varchar(255) DEFAULT NULL, + PRIMARY KEY (`a`) +) ENGINE=MEMORY AUTO_INCREMENT=2002 DEFAULT CHARSET=latin1 +/*!50100 PARTITION BY RANGE (a) +(PARTITION LT1000 VALUES LESS THAN (1000) ENGINE = MEMORY, + PARTITION LT2000 VALUES LESS THAN (2000) ENGINE = MEMORY, + PARTITION MAX VALUES LESS THAN MAXVALUE ENGINE = MEMORY) */ +SELECT * FROM t1 ORDER BY a; +a b +1 First +2 Second +999 Last in LT1000 +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First in MAX +2001 Second in MAX +ALTER TABLE t1 ANALYZE PARTITION MAX; +Table Op Msg_type Msg_text +mysql_test_db.t1 analyze note The storage engine for the table doesn't support analyze +# Truncate without FLUSH +ALTER TABLE t1 TRUNCATE PARTITION MAX; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE MAX (1)"); +SELECT * FROM t1 WHERE a >= 2000; +a b +2000 First after TRUNCATE MAX (1) +# Truncate with FLUSH after +ALTER TABLE t1 TRUNCATE PARTITION MAX; +FLUSH TABLES; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE MAX (2)"); +SELECT * FROM t1 WHERE a >= 2000; +a b +2000 First after TRUNCATE MAX (2) +# Truncate with FLUSH before +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION MAX; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE MAX (3)"); +SELECT * FROM t1 WHERE a >= 2000; +a b +2000 First after TRUNCATE MAX (3) +# Truncate with FLUSH after INSERT +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION MAX; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE MAX (4)"); +SELECT * FROM t1 WHERE a >= 2000; +a b +2000 First after TRUNCATE MAX (4) +# Truncate without FLUSH +ALTER TABLE t1 TRUNCATE PARTITION LT1000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT1000 (1)"); +SELECT * FROM t1 ORDER BY a; +a b +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +# Truncate with FLUSH after +ALTER TABLE t1 TRUNCATE PARTITION LT1000; +FLUSH TABLES; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT1000 (2)"); +SELECT * FROM t1 ORDER BY a; +a b +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +# Truncate with FLUSH before +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION LT1000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT1000 (3)"); +SELECT * FROM t1 ORDER BY a; +a b +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +# Truncate with FLUSH after INSERT +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION LT1000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT1000 (4)"); +SELECT * FROM t1 ORDER BY a; +a b +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +# Truncate without FLUSH +ALTER TABLE t1 TRUNCATE PARTITION LT2000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT2000 (1)"); +SELECT * FROM t1 ORDER BY a; +a b +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +2005 First after TRUNCATE LT2000 (1) +# Truncate with FLUSH after +ALTER TABLE t1 TRUNCATE PARTITION LT2000; +FLUSH TABLES; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT2000 (2)"); +SELECT * FROM t1 ORDER BY a; +a b +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +2005 First after TRUNCATE LT2000 (1) +2006 First after TRUNCATE LT2000 (2) +# Truncate with FLUSH before +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION LT2000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT2000 (3)"); +SELECT * FROM t1 ORDER BY a; +a b +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +2005 First after TRUNCATE LT2000 (1) +2006 First after TRUNCATE LT2000 (2) +2007 First after TRUNCATE LT2000 (3) +# Truncate with FLUSH after INSERT +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION LT2000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT2000 (4)"); +SELECT * FROM t1 ORDER BY a; +a b +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +2005 First after TRUNCATE LT2000 (1) +2006 First after TRUNCATE LT2000 (2) +2007 First after TRUNCATE LT2000 (3) +2008 First after TRUNCATE LT2000 (4) +DROP TABLE t1; # Cleaning up before exit USE test; DROP DATABASE MySQL_Test_DB; diff --git a/mysql-test/suite/parts/r/partition_mgm_lc2_myisam.result b/mysql-test/suite/parts/r/partition_mgm_lc2_myisam.result index 6bdfa149de0..7a61a811ea3 100644 --- a/mysql-test/suite/parts/r/partition_mgm_lc2_myisam.result +++ b/mysql-test/suite/parts/r/partition_mgm_lc2_myisam.result @@ -882,6 +882,170 @@ TableA CREATE TABLE `TableA` ( ) ENGINE=MyISAM DEFAULT CHARSET=latin1 # Cleaning up after LIST PARTITIONING test DROP TABLE TableA; +# Testing TRUNCATE PARTITION +CREATE TABLE t1 +(a BIGINT AUTO_INCREMENT PRIMARY KEY, +b VARCHAR(255)) +ENGINE = 'MyISAM' +PARTITION BY RANGE (a) +(PARTITION LT1000 VALUES LESS THAN (1000), +PARTITION LT2000 VALUES LESS THAN (2000), +PARTITION MAX VALUES LESS THAN MAXVALUE); +INSERT INTO t1 VALUES (NULL, "First"), (NULL, "Second"), (999, "Last in LT1000"), (NULL, "First in LT2000"), (NULL, "Second in LT2000"), (1999, "Last in LT2000"), (NULL, "First in MAX"), (NULL, "Second in MAX"); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` bigint(20) NOT NULL AUTO_INCREMENT, + `b` varchar(255) DEFAULT NULL, + PRIMARY KEY (`a`) +) ENGINE=MyISAM AUTO_INCREMENT=2002 DEFAULT CHARSET=latin1 +/*!50100 PARTITION BY RANGE (a) +(PARTITION LT1000 VALUES LESS THAN (1000) ENGINE = MyISAM, + PARTITION LT2000 VALUES LESS THAN (2000) ENGINE = MyISAM, + PARTITION MAX VALUES LESS THAN MAXVALUE ENGINE = MyISAM) */ +SELECT * FROM t1 ORDER BY a; +a b +1 First +2 Second +999 Last in LT1000 +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First in MAX +2001 Second in MAX +ALTER TABLE t1 ANALYZE PARTITION MAX; +Table Op Msg_type Msg_text +mysql_test_db.t1 analyze status OK +# Truncate without FLUSH +ALTER TABLE t1 TRUNCATE PARTITION MAX; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE MAX (1)"); +SELECT * FROM t1 WHERE a >= 2000; +a b +2000 First after TRUNCATE MAX (1) +# Truncate with FLUSH after +ALTER TABLE t1 TRUNCATE PARTITION MAX; +FLUSH TABLES; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE MAX (2)"); +SELECT * FROM t1 WHERE a >= 2000; +a b +2000 First after TRUNCATE MAX (2) +# Truncate with FLUSH before +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION MAX; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE MAX (3)"); +SELECT * FROM t1 WHERE a >= 2000; +a b +2000 First after TRUNCATE MAX (3) +# Truncate with FLUSH after INSERT +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION MAX; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE MAX (4)"); +SELECT * FROM t1 WHERE a >= 2000; +a b +2000 First after TRUNCATE MAX (4) +# Truncate without FLUSH +ALTER TABLE t1 TRUNCATE PARTITION LT1000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT1000 (1)"); +SELECT * FROM t1 ORDER BY a; +a b +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +# Truncate with FLUSH after +ALTER TABLE t1 TRUNCATE PARTITION LT1000; +FLUSH TABLES; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT1000 (2)"); +SELECT * FROM t1 ORDER BY a; +a b +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +# Truncate with FLUSH before +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION LT1000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT1000 (3)"); +SELECT * FROM t1 ORDER BY a; +a b +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +# Truncate with FLUSH after INSERT +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION LT1000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT1000 (4)"); +SELECT * FROM t1 ORDER BY a; +a b +1000 First in LT2000 +1001 Second in LT2000 +1999 Last in LT2000 +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +# Truncate without FLUSH +ALTER TABLE t1 TRUNCATE PARTITION LT2000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT2000 (1)"); +SELECT * FROM t1 ORDER BY a; +a b +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +2005 First after TRUNCATE LT2000 (1) +# Truncate with FLUSH after +ALTER TABLE t1 TRUNCATE PARTITION LT2000; +FLUSH TABLES; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT2000 (2)"); +SELECT * FROM t1 ORDER BY a; +a b +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +2005 First after TRUNCATE LT2000 (1) +2006 First after TRUNCATE LT2000 (2) +# Truncate with FLUSH before +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION LT2000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT2000 (3)"); +SELECT * FROM t1 ORDER BY a; +a b +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +2005 First after TRUNCATE LT2000 (1) +2006 First after TRUNCATE LT2000 (2) +2007 First after TRUNCATE LT2000 (3) +# Truncate with FLUSH after INSERT +FLUSH TABLES; +ALTER TABLE t1 TRUNCATE PARTITION LT2000; +INSERT INTO t1 VALUES (NULL, "First after TRUNCATE LT2000 (4)"); +SELECT * FROM t1 ORDER BY a; +a b +2000 First after TRUNCATE MAX (4) +2001 First after TRUNCATE LT1000 (1) +2002 First after TRUNCATE LT1000 (2) +2003 First after TRUNCATE LT1000 (3) +2004 First after TRUNCATE LT1000 (4) +2005 First after TRUNCATE LT2000 (1) +2006 First after TRUNCATE LT2000 (2) +2007 First after TRUNCATE LT2000 (3) +2008 First after TRUNCATE LT2000 (4) +DROP TABLE t1; # Cleaning up before exit USE test; DROP DATABASE MySQL_Test_DB; diff --git a/mysql-test/suite/parts/r/partition_mgm_lc2_ndb.result b/mysql-test/suite/parts/r/partition_mgm_lc2_ndb.result index 8b9c5be1fb6..2f5dfe5e08e 100644 --- a/mysql-test/suite/parts/r/partition_mgm_lc2_ndb.result +++ b/mysql-test/suite/parts/r/partition_mgm_lc2_ndb.result @@ -219,6 +219,18 @@ TableA CREATE TABLE `TableA` ( ) ENGINE=ndbcluster DEFAULT CHARSET=latin1 # Cleaning up after KEY PARTITIONING test DROP TABLE TableA; +# Verify that TRUNCATE PARTITION gives error +CREATE TABLE t1 +(a BIGINT AUTO_INCREMENT PRIMARY KEY, +b VARCHAR(255)) +ENGINE = 'NDBCluster' +PARTITION BY KEY (a) +(PARTITION LT1000, +PARTITION LT2000, +PARTITION MAX); +INSERT INTO t1 VALUES (NULL, "First"), (NULL, "Second"), (999, "Last in LT1000"), (NULL, "First in LT2000"), (NULL, "Second in LT2000"), (1999, "Last in LT2000"), (NULL, "First in MAX"), (NULL, "Second in MAX"); +ALTER TABLE t1 TRUNCATE PARTITION MAX; +Got one of the listed errors # Cleaning up before exit USE test; DROP DATABASE MySQL_Test_DB; diff --git a/mysql-test/suite/parts/t/partition_mgm_lc0_archive.test b/mysql-test/suite/parts/t/partition_mgm_lc0_archive.test index 5b4f2568d46..313da329a9f 100644 --- a/mysql-test/suite/parts/t/partition_mgm_lc0_archive.test +++ b/mysql-test/suite/parts/t/partition_mgm_lc0_archive.test @@ -35,6 +35,7 @@ ##### Storage engine to be tested --source include/have_archive.inc let $engine= 'Archive'; +let $no_truncate= 1; #------------------------------------------------------------------------------# # Execute the tests to be applied to all storage engines diff --git a/mysql-test/suite/parts/t/partition_mgm_lc0_ndb.test b/mysql-test/suite/parts/t/partition_mgm_lc0_ndb.test index 686c69cca25..736e45067bc 100644 --- a/mysql-test/suite/parts/t/partition_mgm_lc0_ndb.test +++ b/mysql-test/suite/parts/t/partition_mgm_lc0_ndb.test @@ -41,6 +41,8 @@ let $can_only_key= 1; # Allow hash/list/range partitioning with ndb #SET new=on; let $engine= 'NDBCluster'; +# NDB does not yet support TRUNCATE PARTITION +let $no_truncate= 1; #------------------------------------------------------------------------------# # Execute the tests to be applied to all storage engines diff --git a/mysql-test/suite/parts/t/partition_mgm_lc1_archive.test b/mysql-test/suite/parts/t/partition_mgm_lc1_archive.test index 2bc643db75f..58eef828f06 100644 --- a/mysql-test/suite/parts/t/partition_mgm_lc1_archive.test +++ b/mysql-test/suite/parts/t/partition_mgm_lc1_archive.test @@ -32,6 +32,7 @@ ##### Storage engine to be tested --source include/have_archive.inc let $engine= 'Archive'; +let $no_truncate= 1; #------------------------------------------------------------------------------# # Execute the tests to be applied to all storage engines diff --git a/mysql-test/suite/parts/t/partition_mgm_lc1_ndb.test b/mysql-test/suite/parts/t/partition_mgm_lc1_ndb.test index a70b9b5c41c..ac425eb84ff 100644 --- a/mysql-test/suite/parts/t/partition_mgm_lc1_ndb.test +++ b/mysql-test/suite/parts/t/partition_mgm_lc1_ndb.test @@ -38,6 +38,8 @@ let $can_only_key= 1; # Allow hash/list/range partitioning with ndb #SET new=on; let $engine= 'NDBCluster'; +# NDB does not yet support TRUNCATE PARTITION +let $no_truncate= 1; #------------------------------------------------------------------------------# # Execute the tests to be applied to all storage engines diff --git a/mysql-test/suite/parts/t/partition_mgm_lc2_archive.test b/mysql-test/suite/parts/t/partition_mgm_lc2_archive.test index d0e2591804d..92036178e59 100644 --- a/mysql-test/suite/parts/t/partition_mgm_lc2_archive.test +++ b/mysql-test/suite/parts/t/partition_mgm_lc2_archive.test @@ -32,6 +32,7 @@ ##### Storage engine to be tested --source include/have_archive.inc let $engine= 'Archive'; +let $no_truncate= 1; #------------------------------------------------------------------------------# # Execute the tests to be applied to all storage engines diff --git a/mysql-test/suite/parts/t/partition_mgm_lc2_ndb.test b/mysql-test/suite/parts/t/partition_mgm_lc2_ndb.test index 67fdfdde70b..725ba3b5e74 100644 --- a/mysql-test/suite/parts/t/partition_mgm_lc2_ndb.test +++ b/mysql-test/suite/parts/t/partition_mgm_lc2_ndb.test @@ -37,6 +37,8 @@ let $can_only_key= 1; # Allow hash/list/range partitioning with ndb #SET new=on; let $engine= 'NDBCluster'; +# NDB does not yet support TRUNCATE PARTITION +let $no_truncate= 1; #------------------------------------------------------------------------------# # Execute the tests to be applied to all storage engines diff --git a/mysql-test/t/partition_truncate.test b/mysql-test/t/partition_truncate.test new file mode 100644 index 00000000000..93b9cf62d14 --- /dev/null +++ b/mysql-test/t/partition_truncate.test @@ -0,0 +1,26 @@ +# +# Simple tests to verify truncate partition syntax +# +--source include/have_partition.inc +--disable_warnings +drop table if exists t1, t2, t3, t4; +--enable_warnings + +create table t1 (a int) +partition by list (a) +(partition p1 values in (0)); +--error ER_WRONG_PARTITION_NAME +alter table t1 truncate partition p1,p1; +--error ER_WRONG_PARTITION_NAME +alter table t1 truncate partition p0; +drop table t1; + +create table t1 (a int) +partition by list (a) +subpartition by hash (a) +subpartitions 1 +(partition p1 values in (1) + (subpartition sp1)); +--error ER_WRONG_PARTITION_NAME +alter table t1 truncate partition sp1; +drop table t1; diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index ac8c46ec4e3..83a2a3064d5 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -1062,7 +1062,7 @@ int ha_partition::handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt, it should only do named partitions, otherwise all partitions */ if (!(thd->lex->alter_info.flags & ALTER_ADMIN_PARTITION) || - part_elem->part_state == PART_CHANGED) + part_elem->part_state == PART_ADMIN) { if (m_is_sub_partitioned) { @@ -1123,6 +1123,7 @@ int ha_partition::handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt, DBUG_RETURN(error); } } + part_elem->part_state= PART_NORMAL; } } while (++i < no_parts); DBUG_RETURN(FALSE); @@ -3202,6 +3203,9 @@ int ha_partition::delete_row(const uchar *buf) Called from sql_delete.cc by mysql_delete(). Called from sql_select.cc by JOIN::reinit(). Called from sql_union.cc by st_select_lex_unit::exec(). + + Also used for handle ALTER TABLE t TRUNCATE PARTITION ... + NOTE: auto increment value will be truncated in that partition as well! */ int ha_partition::delete_all_rows() @@ -3214,11 +3218,84 @@ int ha_partition::delete_all_rows() if (thd->lex->sql_command == SQLCOM_TRUNCATE) { + Alter_info *alter_info= &thd->lex->alter_info; HA_DATA_PARTITION *ha_data= (HA_DATA_PARTITION*) table_share->ha_data; + /* TRUNCATE also means resetting auto_increment */ lock_auto_increment(); ha_data->next_auto_inc_val= 0; ha_data->auto_inc_initialized= FALSE; unlock_auto_increment(); + if (alter_info->flags & ALTER_ADMIN_PARTITION) + { + /* ALTER TABLE t TRUNCATE PARTITION ... */ + List_iterator part_it(m_part_info->partitions); + int saved_error= 0; + uint no_parts= m_part_info->no_parts; + uint no_subparts= m_part_info->no_subparts; + uint i= 0; + uint no_parts_set= alter_info->partition_names.elements; + uint no_parts_found= set_part_state(alter_info, m_part_info, + PART_ADMIN); + if (no_parts_set != no_parts_found && + (!(alter_info->flags & ALTER_ALL_PARTITION))) + DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND); + + /* + Cannot return HA_ERR_WRONG_COMMAND here without correct pruning + since that whould delete the whole table row by row in sql_delete.cc + */ + bitmap_clear_all(&m_part_info->used_partitions); + do + { + partition_element *part_elem= part_it++; + if (part_elem->part_state == PART_ADMIN) + { + if (m_is_sub_partitioned) + { + List_iterator + subpart_it(part_elem->subpartitions); + partition_element *sub_elem; + uint j= 0, part; + do + { + sub_elem= subpart_it++; + part= i * no_subparts + j; + bitmap_set_bit(&m_part_info->used_partitions, part); + if (!saved_error) + { + DBUG_PRINT("info", ("truncate subpartition %u (%s)", + part, sub_elem->partition_name)); + if ((error= m_file[part]->ha_delete_all_rows())) + saved_error= error; + /* If not reset_auto_increment is supported, just accept it */ + if (!saved_error && + (error= m_file[part]->ha_reset_auto_increment(0)) && + error != HA_ERR_WRONG_COMMAND) + saved_error= error; + } + } while (++j < no_subparts); + } + else + { + DBUG_PRINT("info", ("truncate partition %u (%s)", i, + part_elem->partition_name)); + bitmap_set_bit(&m_part_info->used_partitions, i); + if (!saved_error) + { + if ((error= m_file[i]->ha_delete_all_rows()) && !saved_error) + saved_error= error; + /* If not reset_auto_increment is supported, just accept it */ + if (!saved_error && + (error= m_file[i]->ha_reset_auto_increment(0)) && + error != HA_ERR_WRONG_COMMAND) + saved_error= error; + } + } + part_elem->part_state= PART_NORMAL; + } + } while (++i < no_parts); + DBUG_RETURN(saved_error); + } truncate= TRUE; } file= m_file; @@ -5842,12 +5919,14 @@ enum row_type ha_partition::get_row_type() const void ha_partition::print_error(int error, myf errflag) { + THD *thd= ha_thd(); DBUG_ENTER("ha_partition::print_error"); /* Should probably look for my own errors first */ DBUG_PRINT("enter", ("error: %d", error)); - if (error == HA_ERR_NO_PARTITION_FOUND) + if (error == HA_ERR_NO_PARTITION_FOUND && + thd->lex->sql_command != SQLCOM_TRUNCATE) m_part_info->print_no_partition_found(table); else m_file[m_last_part]->print_error(error, errflag); diff --git a/sql/handler.cc b/sql/handler.cc index 0e83d2911f2..97e49ef2157 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -2742,6 +2742,9 @@ void handler::print_error(int error, myf errflag) case HA_ERR_TABLE_NEEDS_UPGRADE: textno=ER_TABLE_NEEDS_UPGRADE; break; + case HA_ERR_NO_PARTITION_FOUND: + textno=ER_WRONG_PARTITION_NAME; + break; case HA_ERR_TABLE_READONLY: textno= ER_OPEN_AS_READONLY; break; diff --git a/sql/partition_element.h b/sql/partition_element.h index 905bc38165b..bede5264c71 100644 --- a/sql/partition_element.h +++ b/sql/partition_element.h @@ -32,7 +32,8 @@ enum partition_state { PART_REORGED_DROPPED= 5, PART_CHANGED= 6, PART_IS_CHANGED= 7, - PART_IS_ADDED= 8 + PART_IS_ADDED= 8, + PART_ADMIN= 9 }; /* diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index d2f90fa9288..ee272dad341 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1075,6 +1075,7 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) { handlerton *table_type= table->s->db_type(); TABLE_SHARE *share= table->s; + /* Note that a temporary table cannot be partitioned */ if (!ha_check_storage_engine_flag(table_type, HTON_CAN_RECREATE)) goto trunc_by_del; @@ -1113,8 +1114,22 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) table_list->db, table_list->table_name); DBUG_RETURN(TRUE); } - if (!ha_check_storage_engine_flag(ha_resolve_by_legacy_type(thd, table_type), - HTON_CAN_RECREATE)) +#ifdef WITH_PARTITION_STORAGE_ENGINE + /* + TODO: Add support for TRUNCATE PARTITION for NDB and other engines + supporting native partitioning + */ + if (table_type != DB_TYPE_PARTITION_DB && + thd->lex->alter_info.flags & ALTER_ADMIN_PARTITION) + { + my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0)); + DBUG_RETURN(TRUE); + } +#endif + if (!ha_check_storage_engine_flag(ha_resolve_by_legacy_type(thd, + table_type), + HTON_CAN_RECREATE) || + thd->lex->alter_info.flags & ALTER_ADMIN_PARTITION) goto trunc_by_del; if (lock_and_wait_for_table_name(thd, table_list)) diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 61766e5c509..2fbdde9c4aa 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -4158,6 +4158,8 @@ uint set_part_state(Alter_info *alter_info, partition_info *tab_part_info, DBUG_PRINT("info", ("Setting part_state to %u for partition %s", part_state, part_elem->partition_name)); } + else + part_elem->part_state= PART_NORMAL; } while (++part_count < tab_part_info->no_parts); return no_parts_found; } diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 81d00f46000..4a1050b897d 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -4534,7 +4534,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, uint no_parts_found; uint no_parts_opt= alter_info->partition_names.elements; no_parts_found= set_part_state(alter_info, table->table->part_info, - PART_CHANGED); + PART_ADMIN); if (no_parts_found != no_parts_opt && (!(alter_info->flags & ALTER_ALL_PARTITION))) { diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 4ed9946a334..72ec879a7c8 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -5671,7 +5671,7 @@ alter_commands: all_or_alt_part_name_list { LEX *lex= Lex; - lex->sql_command = SQLCOM_OPTIMIZE; + lex->sql_command= SQLCOM_OPTIMIZE; lex->alter_info.flags|= ALTER_ADMIN_PARTITION; lex->no_write_to_binlog= $3; lex->check_opt.init(); @@ -5681,7 +5681,7 @@ alter_commands: all_or_alt_part_name_list { LEX *lex= Lex; - lex->sql_command = SQLCOM_ANALYZE; + lex->sql_command= SQLCOM_ANALYZE; lex->alter_info.flags|= ALTER_ADMIN_PARTITION; lex->no_write_to_binlog= $3; lex->check_opt.init(); @@ -5689,7 +5689,7 @@ alter_commands: | CHECK_SYM PARTITION_SYM all_or_alt_part_name_list { LEX *lex= Lex; - lex->sql_command = SQLCOM_CHECK; + lex->sql_command= SQLCOM_CHECK; lex->alter_info.flags|= ALTER_ADMIN_PARTITION; lex->check_opt.init(); } @@ -5698,7 +5698,7 @@ alter_commands: all_or_alt_part_name_list { LEX *lex= Lex; - lex->sql_command = SQLCOM_REPAIR; + lex->sql_command= SQLCOM_REPAIR; lex->alter_info.flags|= ALTER_ADMIN_PARTITION; lex->no_write_to_binlog= $3; lex->check_opt.init(); @@ -5711,6 +5711,13 @@ alter_commands: lex->no_write_to_binlog= $3; lex->alter_info.no_parts= $4; } + | TRUNCATE_SYM PARTITION_SYM all_or_alt_part_name_list + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_TRUNCATE; + lex->alter_info.flags|= ALTER_ADMIN_PARTITION; + lex->check_opt.init(); + } | reorg_partition_rule ; @@ -9758,6 +9765,7 @@ truncate: { LEX* lex= Lex; lex->sql_command= SQLCOM_TRUNCATE; + lex->alter_info.reset(); lex->select_lex.options= 0; lex->select_lex.sql_cache= SELECT_LEX::SQL_CACHE_UNSPECIFIED; lex->select_lex.init_order(); From 7aa8cd7a117817c9627932b86be2e8e047bd033b Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Mon, 28 Sep 2009 10:21:25 +0300 Subject: [PATCH 3/9] Ported WL#3220 to mysql-next-mr. --- mysql-test/mysql-test-run.pl | 1 - mysql-test/r/bench_count_distinct.result | 2 +- mysql-test/r/group_min_max.result | 270 ++++- mysql-test/t/group_min_max.test | 125 ++- sql/field.h | 9 +- sql/item_sum.cc | 1201 +++++++++++----------- sql/item_sum.h | 535 ++++++---- sql/opt_range.cc | 188 +++- sql/opt_range.h | 17 +- sql/opt_sum.cc | 10 +- sql/sql_class.h | 1 + sql/sql_select.cc | 130 ++- sql/sql_select.h | 7 + sql/sql_yacc.yy | 6 +- 14 files changed, 1620 insertions(+), 882 deletions(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 83364db0eeb..17102196f42 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -5085,7 +5085,6 @@ sub valgrind_arguments { else { mtr_add_arg($args, "--tool=memcheck"); # From >= 2.1.2 needs this option - mtr_add_arg($args, "--alignment=8"); mtr_add_arg($args, "--leak-check=yes"); mtr_add_arg($args, "--num-callers=16"); mtr_add_arg($args, "--suppressions=%s/valgrind.supp", $glob_mysql_test_dir) diff --git a/mysql-test/r/bench_count_distinct.result b/mysql-test/r/bench_count_distinct.result index 79e12afd237..8b67e4be38a 100644 --- a/mysql-test/r/bench_count_distinct.result +++ b/mysql-test/r/bench_count_distinct.result @@ -5,7 +5,7 @@ count(distinct n) 100 explain extended select count(distinct n) from t1; id select_type table type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 index NULL n 4 NULL 200 100.00 Using index +1 SIMPLE t1 range NULL n 4 NULL 10 100.00 Using index for group-by Warnings: Note 1003 select count(distinct `test`.`t1`.`n`) AS `count(distinct n)` from `test`.`t1` drop table t1; diff --git a/mysql-test/r/group_min_max.result b/mysql-test/r/group_min_max.result index ac9a53ca238..c4841ee57a0 100644 --- a/mysql-test/r/group_min_max.result +++ b/mysql-test/r/group_min_max.result @@ -1800,23 +1800,23 @@ b a explain select count(distinct a1,a2,b) from t1 where (a2 >= 'b') and (b = 'a'); id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 index NULL idx_t1_2 147 NULL 128 Using where; Using index +1 SIMPLE t1 range NULL idx_t1_1 147 NULL 17 Using where; Using index for group-by explain select count(distinct a1,a2,b,c) from t1 where (a2 >= 'b') and (b = 'a') and (c = 'i121'); id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 index NULL idx_t1_1 163 NULL 128 Using where; Using index +1 SIMPLE t1 range NULL idx_t1_1 163 NULL 65 Using where; Using index for group-by (scanning) explain extended select count(distinct a1,a2,b) from t1 where (a1 > 'a') and (a2 > 'a') and (b = 'c'); id select_type table type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 index idx_t1_0,idx_t1_1,idx_t1_2 idx_t1_2 147 NULL 128 75.00 Using where; Using index +1 SIMPLE t1 range idx_t1_0,idx_t1_1,idx_t1_2 idx_t1_1 147 NULL 14 100.00 Using where; Using index for group-by Warnings: Note 1003 select count(distinct `test`.`t1`.`a1`,`test`.`t1`.`a2`,`test`.`t1`.`b`) AS `count(distinct a1,a2,b)` from `test`.`t1` where ((`test`.`t1`.`b` = 'c') and (`test`.`t1`.`a1` > 'a') and (`test`.`t1`.`a2` > 'a')) explain select count(distinct b) from t1 where (a2 >= 'b') and (b = 'a'); id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 index NULL idx_t1_2 147 NULL 128 Using where; Using index -explain extended select ord(a1) + count(distinct a1,a2,b) from t1 where (a1 > 'a') and (a2 > 'a'); +explain extended select 98 + count(distinct a1,a2,b) from t1 where (a1 > 'a') and (a2 > 'a'); id select_type table type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 index idx_t1_0,idx_t1_1,idx_t1_2 idx_t1_2 147 NULL 128 75.00 Using where; Using index +1 SIMPLE t1 range idx_t1_0,idx_t1_1,idx_t1_2 idx_t1_1 147 NULL 14 100.00 Using where; Using index for group-by Warnings: -Note 1003 select (ord(`test`.`t1`.`a1`) + count(distinct `test`.`t1`.`a1`,`test`.`t1`.`a2`,`test`.`t1`.`b`)) AS `ord(a1) + count(distinct a1,a2,b)` from `test`.`t1` where ((`test`.`t1`.`a1` > 'a') and (`test`.`t1`.`a2` > 'a')) +Note 1003 select (98 + count(distinct `test`.`t1`.`a1`,`test`.`t1`.`a2`,`test`.`t1`.`b`)) AS `98 + count(distinct a1,a2,b)` from `test`.`t1` where ((`test`.`t1`.`a1` > 'a') and (`test`.`t1`.`a2` > 'a')) select count(distinct a1,a2,b) from t1 where (a2 >= 'b') and (b = 'a'); count(distinct a1,a2,b) 4 @@ -1829,8 +1829,8 @@ count(distinct a1,a2,b) select count(distinct b) from t1 where (a2 >= 'b') and (b = 'a'); count(distinct b) 1 -select ord(a1) + count(distinct a1,a2,b) from t1 where (a1 > 'a') and (a2 > 'a'); -ord(a1) + count(distinct a1,a2,b) +select 98 + count(distinct a1,a2,b) from t1 where (a1 > 'a') and (a2 > 'a'); +98 + count(distinct a1,a2,b) 104 explain select a1,a2,b, concat(min(c), max(c)) from t1 where a1 < 'd' group by a1,a2,b; id select_type table type possible_keys key key_len ref rows Extra @@ -2514,3 +2514,257 @@ a MAX(b) 2 1 DROP TABLE t; End of 5.1 tests +# +# WL#3220 (Loose index scan for COUNT DISTINCT) +# +CREATE TABLE t1 (a INT, b INT, c INT, KEY (a,b)); +INSERT INTO t1 VALUES (1,1,1), (1,2,1), (1,3,1), (1,4,1); +INSERT INTO t1 SELECT a, b + 4, 1 FROM t1; +INSERT INTO t1 SELECT a + 1, b, 1 FROM t1; +CREATE TABLE t2 (a INT, b INT, c INT, d INT, e INT, f INT, KEY (a,b,c)); +INSERT INTO t2 VALUES (1,1,1,1,1,1), (1,2,1,1,1,1), (1,3,1,1,1,1), +(1,4,1,1,1,1); +INSERT INTO t2 SELECT a, b + 4, c,d,e,f FROM t2; +INSERT INTO t2 SELECT a + 1, b, c,d,e,f FROM t2; +EXPLAIN SELECT COUNT(DISTINCT a) FROM t1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range NULL a 5 NULL 9 Using index for group-by +SELECT COUNT(DISTINCT a) FROM t1; +COUNT(DISTINCT a) +2 +EXPLAIN SELECT COUNT(DISTINCT a,b) FROM t1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range NULL a 10 NULL 9 Using index for group-by +SELECT COUNT(DISTINCT a,b) FROM t1; +COUNT(DISTINCT a,b) +16 +EXPLAIN SELECT COUNT(DISTINCT b,a) FROM t1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range NULL a 10 NULL 9 Using index for group-by +SELECT COUNT(DISTINCT b,a) FROM t1; +COUNT(DISTINCT b,a) +16 +EXPLAIN SELECT COUNT(DISTINCT b) FROM t1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index NULL a 10 NULL 16 Using index +SELECT COUNT(DISTINCT b) FROM t1; +COUNT(DISTINCT b) +8 +EXPLAIN SELECT COUNT(DISTINCT a) FROM t1 GROUP BY a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range NULL a 5 NULL 9 Using index for group-by +SELECT COUNT(DISTINCT a) FROM t1 GROUP BY a; +COUNT(DISTINCT a) +1 +1 +EXPLAIN SELECT COUNT(DISTINCT b) FROM t1 GROUP BY a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range NULL a 10 NULL 9 Using index for group-by +SELECT COUNT(DISTINCT b) FROM t1 GROUP BY a; +COUNT(DISTINCT b) +8 +8 +EXPLAIN SELECT COUNT(DISTINCT a) FROM t1 GROUP BY b; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index NULL a 10 NULL 16 Using index; Using filesort +SELECT COUNT(DISTINCT a) FROM t1 GROUP BY b; +COUNT(DISTINCT a) +2 +2 +2 +2 +2 +2 +2 +2 +EXPLAIN SELECT DISTINCT COUNT(DISTINCT a) FROM t1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index NULL a 10 NULL 16 Using index +SELECT DISTINCT COUNT(DISTINCT a) FROM t1; +COUNT(DISTINCT a) +2 +EXPLAIN SELECT COUNT(DISTINCT a, b + 0) FROM t1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index NULL a 10 NULL 16 Using index +SELECT COUNT(DISTINCT a, b + 0) FROM t1; +COUNT(DISTINCT a, b + 0) +16 +EXPLAIN SELECT COUNT(DISTINCT a) FROM t1 HAVING COUNT(DISTINCT b) < 10; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range NULL a 10 NULL 9 Using index for group-by +SELECT COUNT(DISTINCT a) FROM t1 HAVING COUNT(DISTINCT b) < 10; +COUNT(DISTINCT a) +EXPLAIN SELECT COUNT(DISTINCT a) FROM t1 HAVING COUNT(DISTINCT c) < 10; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 16 +SELECT COUNT(DISTINCT a) FROM t1 HAVING COUNT(DISTINCT c) < 10; +COUNT(DISTINCT a) +2 +EXPLAIN SELECT 1 FROM t1 HAVING COUNT(DISTINCT a) < 10; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range NULL a 5 NULL 9 Using index for group-by +SELECT 1 FROM t1 HAVING COUNT(DISTINCT a) < 10; +1 +1 +EXPLAIN SELECT 1 FROM t1 GROUP BY a HAVING COUNT(DISTINCT b) > 1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range NULL a 10 NULL 9 Using index for group-by +SELECT 1 FROM t1 GROUP BY a HAVING COUNT(DISTINCT b) > 1; +1 +1 +1 +EXPLAIN SELECT COUNT(DISTINCT t1_1.a) FROM t1 t1_1, t1 t1_2 GROUP BY t1_1.a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1_1 index NULL a 10 NULL 16 Using index; Using temporary; Using filesort +1 SIMPLE t1_2 index NULL a 10 NULL 16 Using index; Using join buffer +SELECT COUNT(DISTINCT t1_1.a) FROM t1 t1_1, t1 t1_2 GROUP BY t1_1.a; +COUNT(DISTINCT t1_1.a) +1 +1 +EXPLAIN SELECT COUNT(DISTINCT a), 12 FROM t1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range NULL a 5 NULL 9 Using index for group-by +SELECT COUNT(DISTINCT a), 12 FROM t1; +COUNT(DISTINCT a) 12 +2 12 +EXPLAIN SELECT COUNT(DISTINCT a, b, c) FROM t2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 range NULL a 15 NULL 9 Using index for group-by +SELECT COUNT(DISTINCT a, b, c) FROM t2; +COUNT(DISTINCT a, b, c) +16 +EXPLAIN SELECT COUNT(DISTINCT a), SUM(DISTINCT a), AVG(DISTINCT a) FROM t2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 range NULL a 5 NULL 9 Using index for group-by +SELECT COUNT(DISTINCT a), SUM(DISTINCT a), AVG(DISTINCT a) FROM t2; +COUNT(DISTINCT a) SUM(DISTINCT a) AVG(DISTINCT a) +2 3 1.5000 +EXPLAIN SELECT COUNT(DISTINCT a), SUM(DISTINCT a), AVG(DISTINCT f) FROM t2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 ALL NULL NULL NULL NULL 16 +SELECT COUNT(DISTINCT a), SUM(DISTINCT a), AVG(DISTINCT f) FROM t2; +COUNT(DISTINCT a) SUM(DISTINCT a) AVG(DISTINCT f) +2 3 1.0000 +EXPLAIN SELECT COUNT(DISTINCT a, b), COUNT(DISTINCT b, a) FROM t2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 range NULL a 10 NULL 9 Using index for group-by +SELECT COUNT(DISTINCT a, b), COUNT(DISTINCT b, a) FROM t2; +COUNT(DISTINCT a, b) COUNT(DISTINCT b, a) +16 16 +EXPLAIN SELECT COUNT(DISTINCT a, b), COUNT(DISTINCT b, f) FROM t2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 ALL NULL NULL NULL NULL 16 +SELECT COUNT(DISTINCT a, b), COUNT(DISTINCT b, f) FROM t2; +COUNT(DISTINCT a, b) COUNT(DISTINCT b, f) +16 8 +EXPLAIN SELECT COUNT(DISTINCT a, b), COUNT(DISTINCT b, d) FROM t2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 ALL NULL NULL NULL NULL 16 +SELECT COUNT(DISTINCT a, b), COUNT(DISTINCT b, d) FROM t2; +COUNT(DISTINCT a, b) COUNT(DISTINCT b, d) +16 8 +EXPLAIN SELECT a, c, COUNT(DISTINCT c, a, b) FROM t2 GROUP BY a, b, c; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 range NULL a 15 NULL 9 Using index for group-by +SELECT a, c, COUNT(DISTINCT c, a, b) FROM t2 GROUP BY a, b, c; +a c COUNT(DISTINCT c, a, b) +1 1 1 +1 1 1 +1 1 1 +1 1 1 +1 1 1 +1 1 1 +1 1 1 +2 1 1 +2 1 1 +2 1 1 +2 1 1 +2 1 1 +2 1 1 +2 1 1 +2 1 1 +2 1 1 +EXPLAIN SELECT COUNT(DISTINCT c, a, b) FROM t2 +WHERE a > 5 AND b BETWEEN 10 AND 20 GROUP BY a, b, c; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 range a a 15 NULL 1 Using where; Using index for group-by +SELECT COUNT(DISTINCT c, a, b) FROM t2 +WHERE a > 5 AND b BETWEEN 10 AND 20 GROUP BY a, b, c; +COUNT(DISTINCT c, a, b) +EXPLAIN SELECT COUNT(DISTINCT b), SUM(DISTINCT b) FROM t2 WHERE a = 5 +GROUP BY b; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 ref a a 5 const 1 Using where; Using index +SELECT COUNT(DISTINCT b), SUM(DISTINCT b) FROM t2 WHERE a = 5 +GROUP BY b; +COUNT(DISTINCT b) SUM(DISTINCT b) +EXPLAIN SELECT a, COUNT(DISTINCT b), SUM(DISTINCT b) FROM t2 GROUP BY a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 range NULL a 10 NULL 9 Using index for group-by +SELECT a, COUNT(DISTINCT b), SUM(DISTINCT b) FROM t2 GROUP BY a; +a COUNT(DISTINCT b) SUM(DISTINCT b) +2 8 36 +2 8 36 +EXPLAIN SELECT COUNT(DISTINCT b), SUM(DISTINCT b) FROM t2 GROUP BY a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 range NULL a 10 NULL 9 Using index for group-by +SELECT COUNT(DISTINCT b), SUM(DISTINCT b) FROM t2 GROUP BY a; +COUNT(DISTINCT b) SUM(DISTINCT b) +8 36 +8 36 +EXPLAIN SELECT COUNT(DISTINCT a, b) FROM t2 WHERE c = 13 AND d = 42; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 ALL NULL NULL NULL NULL 16 Using where +SELECT COUNT(DISTINCT a, b) FROM t2 WHERE c = 13 AND d = 42; +COUNT(DISTINCT a, b) +0 +EXPLAIN SELECT a, COUNT(DISTINCT a), SUM(DISTINCT a) FROM t2 +WHERE b = 13 AND c = 42 GROUP BY a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 range NULL a 15 NULL 9 Using where; Using index for group-by +SELECT a, COUNT(DISTINCT a), SUM(DISTINCT a) FROM t2 +WHERE b = 13 AND c = 42 GROUP BY a; +a COUNT(DISTINCT a) SUM(DISTINCT a) +EXPLAIN SELECT COUNT(DISTINCT a, b), SUM(DISTINCT a) FROM t2 WHERE b = 42; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 range NULL a 10 NULL 9 Using where; Using index for group-by +SELECT COUNT(DISTINCT a, b), SUM(DISTINCT a) FROM t2 WHERE b = 42; +COUNT(DISTINCT a, b) SUM(DISTINCT a) +0 NULL +EXPLAIN SELECT SUM(DISTINCT a), MAX(b) FROM t2 GROUP BY a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 range NULL a 5 NULL 9 Using index for group-by +SELECT SUM(DISTINCT a), MAX(b) FROM t2 GROUP BY a; +SUM(DISTINCT a) MAX(b) +1 8 +2 8 +EXPLAIN SELECT 42 * (a + c + COUNT(DISTINCT c, a, b)) FROM t2 GROUP BY a, b, c; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 range NULL a 15 NULL 9 Using index for group-by +SELECT 42 * (a + c + COUNT(DISTINCT c, a, b)) FROM t2 GROUP BY a, b, c; +42 * (a + c + COUNT(DISTINCT c, a, b)) +126 +126 +126 +126 +126 +126 +126 +168 +168 +168 +168 +168 +168 +168 +168 +168 +EXPLAIN SELECT (SUM(DISTINCT a) + MAX(b)) FROM t2 GROUP BY a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 range NULL a 5 NULL 9 Using index for group-by +SELECT (SUM(DISTINCT a) + MAX(b)) FROM t2 GROUP BY a; +(SUM(DISTINCT a) + MAX(b)) +9 +10 +DROP TABLE t1,t2; +# end of WL#3220 tests diff --git a/mysql-test/t/group_min_max.test b/mysql-test/t/group_min_max.test index c09a4fbf490..3ff3b96d829 100644 --- a/mysql-test/t/group_min_max.test +++ b/mysql-test/t/group_min_max.test @@ -570,13 +570,13 @@ explain select count(distinct a1,a2,b) from t1 where (a2 >= 'b') and (b = 'a'); explain select count(distinct a1,a2,b,c) from t1 where (a2 >= 'b') and (b = 'a') and (c = 'i121'); explain extended select count(distinct a1,a2,b) from t1 where (a1 > 'a') and (a2 > 'a') and (b = 'c'); explain select count(distinct b) from t1 where (a2 >= 'b') and (b = 'a'); -explain extended select ord(a1) + count(distinct a1,a2,b) from t1 where (a1 > 'a') and (a2 > 'a'); +explain extended select 98 + count(distinct a1,a2,b) from t1 where (a1 > 'a') and (a2 > 'a'); select count(distinct a1,a2,b) from t1 where (a2 >= 'b') and (b = 'a'); select count(distinct a1,a2,b,c) from t1 where (a2 >= 'b') and (b = 'a') and (c = 'i121'); select count(distinct a1,a2,b) from t1 where (a1 > 'a') and (a2 > 'a') and (b = 'c'); select count(distinct b) from t1 where (a2 >= 'b') and (b = 'a'); -select ord(a1) + count(distinct a1,a2,b) from t1 where (a1 > 'a') and (a2 > 'a'); +select 98 + count(distinct a1,a2,b) from t1 where (a1 > 'a') and (a2 > 'a'); # # Queries with expressions in the select clause @@ -1033,3 +1033,124 @@ SELECT a, MAX(b) FROM t WHERE b GROUP BY a; DROP TABLE t; --echo End of 5.1 tests + + +--echo # +--echo # WL#3220 (Loose index scan for COUNT DISTINCT) +--echo # + +CREATE TABLE t1 (a INT, b INT, c INT, KEY (a,b)); +INSERT INTO t1 VALUES (1,1,1), (1,2,1), (1,3,1), (1,4,1); +INSERT INTO t1 SELECT a, b + 4, 1 FROM t1; +INSERT INTO t1 SELECT a + 1, b, 1 FROM t1; +CREATE TABLE t2 (a INT, b INT, c INT, d INT, e INT, f INT, KEY (a,b,c)); +INSERT INTO t2 VALUES (1,1,1,1,1,1), (1,2,1,1,1,1), (1,3,1,1,1,1), + (1,4,1,1,1,1); +INSERT INTO t2 SELECT a, b + 4, c,d,e,f FROM t2; +INSERT INTO t2 SELECT a + 1, b, c,d,e,f FROM t2; + +EXPLAIN SELECT COUNT(DISTINCT a) FROM t1; +SELECT COUNT(DISTINCT a) FROM t1; + +EXPLAIN SELECT COUNT(DISTINCT a,b) FROM t1; +SELECT COUNT(DISTINCT a,b) FROM t1; + +EXPLAIN SELECT COUNT(DISTINCT b,a) FROM t1; +SELECT COUNT(DISTINCT b,a) FROM t1; + +EXPLAIN SELECT COUNT(DISTINCT b) FROM t1; +SELECT COUNT(DISTINCT b) FROM t1; + +EXPLAIN SELECT COUNT(DISTINCT a) FROM t1 GROUP BY a; +SELECT COUNT(DISTINCT a) FROM t1 GROUP BY a; + +EXPLAIN SELECT COUNT(DISTINCT b) FROM t1 GROUP BY a; +SELECT COUNT(DISTINCT b) FROM t1 GROUP BY a; + +EXPLAIN SELECT COUNT(DISTINCT a) FROM t1 GROUP BY b; +SELECT COUNT(DISTINCT a) FROM t1 GROUP BY b; + +EXPLAIN SELECT DISTINCT COUNT(DISTINCT a) FROM t1; +SELECT DISTINCT COUNT(DISTINCT a) FROM t1; + +EXPLAIN SELECT COUNT(DISTINCT a, b + 0) FROM t1; +SELECT COUNT(DISTINCT a, b + 0) FROM t1; + +EXPLAIN SELECT COUNT(DISTINCT a) FROM t1 HAVING COUNT(DISTINCT b) < 10; +SELECT COUNT(DISTINCT a) FROM t1 HAVING COUNT(DISTINCT b) < 10; + +EXPLAIN SELECT COUNT(DISTINCT a) FROM t1 HAVING COUNT(DISTINCT c) < 10; +SELECT COUNT(DISTINCT a) FROM t1 HAVING COUNT(DISTINCT c) < 10; + +EXPLAIN SELECT 1 FROM t1 HAVING COUNT(DISTINCT a) < 10; +SELECT 1 FROM t1 HAVING COUNT(DISTINCT a) < 10; + +EXPLAIN SELECT 1 FROM t1 GROUP BY a HAVING COUNT(DISTINCT b) > 1; +SELECT 1 FROM t1 GROUP BY a HAVING COUNT(DISTINCT b) > 1; + +EXPLAIN SELECT COUNT(DISTINCT t1_1.a) FROM t1 t1_1, t1 t1_2 GROUP BY t1_1.a; +SELECT COUNT(DISTINCT t1_1.a) FROM t1 t1_1, t1 t1_2 GROUP BY t1_1.a; + +EXPLAIN SELECT COUNT(DISTINCT a), 12 FROM t1; +SELECT COUNT(DISTINCT a), 12 FROM t1; + +EXPLAIN SELECT COUNT(DISTINCT a, b, c) FROM t2; +SELECT COUNT(DISTINCT a, b, c) FROM t2; + +EXPLAIN SELECT COUNT(DISTINCT a), SUM(DISTINCT a), AVG(DISTINCT a) FROM t2; +SELECT COUNT(DISTINCT a), SUM(DISTINCT a), AVG(DISTINCT a) FROM t2; + +EXPLAIN SELECT COUNT(DISTINCT a), SUM(DISTINCT a), AVG(DISTINCT f) FROM t2; +SELECT COUNT(DISTINCT a), SUM(DISTINCT a), AVG(DISTINCT f) FROM t2; + +EXPLAIN SELECT COUNT(DISTINCT a, b), COUNT(DISTINCT b, a) FROM t2; +SELECT COUNT(DISTINCT a, b), COUNT(DISTINCT b, a) FROM t2; + +EXPLAIN SELECT COUNT(DISTINCT a, b), COUNT(DISTINCT b, f) FROM t2; +SELECT COUNT(DISTINCT a, b), COUNT(DISTINCT b, f) FROM t2; + +EXPLAIN SELECT COUNT(DISTINCT a, b), COUNT(DISTINCT b, d) FROM t2; +SELECT COUNT(DISTINCT a, b), COUNT(DISTINCT b, d) FROM t2; + +EXPLAIN SELECT a, c, COUNT(DISTINCT c, a, b) FROM t2 GROUP BY a, b, c; +SELECT a, c, COUNT(DISTINCT c, a, b) FROM t2 GROUP BY a, b, c; + +EXPLAIN SELECT COUNT(DISTINCT c, a, b) FROM t2 + WHERE a > 5 AND b BETWEEN 10 AND 20 GROUP BY a, b, c; +SELECT COUNT(DISTINCT c, a, b) FROM t2 + WHERE a > 5 AND b BETWEEN 10 AND 20 GROUP BY a, b, c; + +EXPLAIN SELECT COUNT(DISTINCT b), SUM(DISTINCT b) FROM t2 WHERE a = 5 + GROUP BY b; +SELECT COUNT(DISTINCT b), SUM(DISTINCT b) FROM t2 WHERE a = 5 + GROUP BY b; + +EXPLAIN SELECT a, COUNT(DISTINCT b), SUM(DISTINCT b) FROM t2 GROUP BY a; +SELECT a, COUNT(DISTINCT b), SUM(DISTINCT b) FROM t2 GROUP BY a; + +EXPLAIN SELECT COUNT(DISTINCT b), SUM(DISTINCT b) FROM t2 GROUP BY a; +SELECT COUNT(DISTINCT b), SUM(DISTINCT b) FROM t2 GROUP BY a; + +EXPLAIN SELECT COUNT(DISTINCT a, b) FROM t2 WHERE c = 13 AND d = 42; +SELECT COUNT(DISTINCT a, b) FROM t2 WHERE c = 13 AND d = 42; + +EXPLAIN SELECT a, COUNT(DISTINCT a), SUM(DISTINCT a) FROM t2 + WHERE b = 13 AND c = 42 GROUP BY a; +SELECT a, COUNT(DISTINCT a), SUM(DISTINCT a) FROM t2 + WHERE b = 13 AND c = 42 GROUP BY a; + +EXPLAIN SELECT COUNT(DISTINCT a, b), SUM(DISTINCT a) FROM t2 WHERE b = 42; +SELECT COUNT(DISTINCT a, b), SUM(DISTINCT a) FROM t2 WHERE b = 42; + +EXPLAIN SELECT SUM(DISTINCT a), MAX(b) FROM t2 GROUP BY a; +SELECT SUM(DISTINCT a), MAX(b) FROM t2 GROUP BY a; + +EXPLAIN SELECT 42 * (a + c + COUNT(DISTINCT c, a, b)) FROM t2 GROUP BY a, b, c; +SELECT 42 * (a + c + COUNT(DISTINCT c, a, b)) FROM t2 GROUP BY a, b, c; + +EXPLAIN SELECT (SUM(DISTINCT a) + MAX(b)) FROM t2 GROUP BY a; +SELECT (SUM(DISTINCT a) + MAX(b)) FROM t2 GROUP BY a; + +DROP TABLE t1,t2; + +--echo # end of WL#3220 tests diff --git a/sql/field.h b/sql/field.h index a9299256f88..ffcf665d45f 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1934,9 +1934,12 @@ public: virtual bool str_needs_quotes() { return TRUE; } my_decimal *val_decimal(my_decimal *); int cmp(const uchar *a, const uchar *b) - { - DBUG_ASSERT(ptr == a); - return Field_bit::key_cmp(b, bytes_in_rec+test(bit_len)); + { + DBUG_ASSERT(ptr == a || ptr == b); + if (ptr == a) + return Field_bit::key_cmp(b, bytes_in_rec+test(bit_len)); + else + return Field_bit::key_cmp(a, bytes_in_rec+test(bit_len)) * -1; } int cmp_binary_offset(uint row_offset) { return cmp_offset(row_offset); } diff --git a/sql/item_sum.cc b/sql/item_sum.cc index ceb553f1c8a..c8576722c69 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -374,6 +374,7 @@ Item_sum::Item_sum(List &list) :arg_count(list.elements), args= NULL; } mark_as_sum_func(); + init_aggregator(); list.empty(); // Fields are used } @@ -405,6 +406,10 @@ Item_sum::Item_sum(THD *thd, Item_sum *item): } memcpy(args, item->args, sizeof(Item*)*arg_count); memcpy(orig_args, item->orig_args, sizeof(Item*)*arg_count); + init_aggregator(); + with_distinct= item->with_distinct; + if (item->aggr) + set_aggregator(item->aggr->Aggrtype()); } @@ -550,13 +555,513 @@ void Item_sum::update_used_tables () } -Item *Item_sum::set_arg(int i, THD *thd, Item *new_val) +Item *Item_sum::set_arg(uint i, THD *thd, Item *new_val) { thd->change_item_tree(args + i, new_val); return new_val; } +int Item_sum::set_aggregator(Aggregator::Aggregator_type aggregator) +{ + switch (aggregator) + { + case Aggregator::DISTINCT_AGGREGATOR: + aggr= new Aggregator_distinct(this); + break; + + case Aggregator::SIMPLE_AGGREGATOR: + aggr= new Aggregator_simple(this); + break; + }; + return aggr ? FALSE : TRUE; +} + + +void Item_sum::cleanup() +{ + if (aggr) + { + delete aggr; + aggr= NULL; + } + Item_result_field::cleanup(); + forced_const= FALSE; +} + + +/** + Compare keys consisting of single field that cannot be compared as binary. + + Used by the Unique class to compare keys. Will do correct comparisons + for all field types. + + @param arg Pointer to the relevant Field class instance + @param key1 left key image + @param key2 right key image + @return comparison result + @retval < 0 if key1 < key2 + @retval = 0 if key1 = key2 + @retval > 0 if key1 > key2 +*/ + +static int simple_str_key_cmp(void* arg, uchar* key1, uchar* key2) +{ + Field *f= (Field*) arg; + return f->cmp(key1, key2); +} + + +/** + Correctly compare composite keys. + + Used by the Unique class to compare keys. Will do correct comparisons + for composite keys with various field types. + + @param arg Pointer to the relevant Aggregator_distinct instance + @param key1 left key image + @param key2 right key image + @return comparison result + @retval <0 if key1 < key2 + @retval =0 if key1 = key2 + @retval >0 if key1 > key2 +*/ + +int Aggregator_distinct::composite_key_cmp(void* arg, uchar* key1, uchar* key2) +{ + Aggregator_distinct *aggr= (Aggregator_distinct *) arg; + Field **field = aggr->table->field; + Field **field_end= field + aggr->table->s->fields; + uint32 *lengths=aggr->field_lengths; + for (; field < field_end; ++field) + { + Field* f = *field; + int len = *lengths++; + int res = f->cmp(key1, key2); + if (res) + return res; + key1 += len; + key2 += len; + } + return 0; +} + + +static enum enum_field_types +calc_tmp_field_type(enum enum_field_types table_field_type, + Item_result result_type) +{ + /* Adjust tmp table type according to the chosen aggregation type */ + switch (result_type) { + case STRING_RESULT: + case REAL_RESULT: + if (table_field_type != MYSQL_TYPE_FLOAT) + table_field_type= MYSQL_TYPE_DOUBLE; + break; + case INT_RESULT: + table_field_type= MYSQL_TYPE_LONGLONG; + /* fallthrough */ + case DECIMAL_RESULT: + if (table_field_type != MYSQL_TYPE_LONGLONG) + table_field_type= MYSQL_TYPE_NEWDECIMAL; + break; + case ROW_RESULT: + default: + DBUG_ASSERT(0); + } + return table_field_type; +} + + +/***************************************************************************/ + +C_MODE_START + +/* Declarations for auxilary C-callbacks */ + +static int simple_raw_key_cmp(void* arg, const void* key1, const void* key2) +{ + return memcmp(key1, key2, *(uint *) arg); +} + + +static int item_sum_distinct_walk(void *element, element_count num_of_dups, + void *item) +{ + return ((Aggregator_distinct*) (item))->unique_walk_function(element); +} + +C_MODE_END + +/***************************************************************************/ +/** + Called before feeding the first row. Used to allocate/setup + the internal structures used for aggregation. + + @param thd Thread descriptor + @return status + @retval FALSE success + @retval TRUE faliure + + Prepares Aggregator_distinct to process the incoming stream. + Creates the temporary table and the Unique class if needed. + Called by Item_sum::aggregator_setup() +*/ + +bool Aggregator_distinct::setup(THD *thd) +{ + endup_done= FALSE; + /* + Setup can be called twice for ROLLUP items. This is a bug. + Please add DBUG_ASSERT(tree == 0) here when it's fixed. + */ + if (tree || table || tmp_table_param) + return FALSE; + + if (item_sum->setup(thd)) + return TRUE; + if (item_sum->sum_func() == Item_sum::COUNT_FUNC || + item_sum->sum_func() == Item_sum::COUNT_DISTINCT_FUNC) + { + List list; + SELECT_LEX *select_lex= thd->lex->current_select; + + if (!(tmp_table_param= new TMP_TABLE_PARAM)) + return TRUE; + + /* Create a table with an unique key over all parameters */ + for (uint i=0; i < item_sum->get_arg_count() ; i++) + { + Item *item=item_sum->get_arg(i); + if (list.push_back(item)) + return TRUE; // End of memory + if (item->const_item() && item->is_null()) + always_null=1; + } + if (always_null) + return FALSE; + count_field_types(select_lex,tmp_table_param,list,0); + tmp_table_param->force_copy_fields= item_sum->force_copy_fields; + DBUG_ASSERT(table == 0); + /* + Make create_tmp_table() convert BIT columns to BIGINT. + This is needed because BIT fields store parts of their data in table's + null bits, and we don't have methods to compare two table records, which + is needed by Unique which is used when HEAP table is used. + */ + { + List_iterator_fast li(list); + Item *item; + while ((item= li++)) + { + if (item->type() == Item::FIELD_ITEM && + ((Item_field*)item)->field->type() == FIELD_TYPE_BIT) + item->marker=4; + } + } + if (!(table= create_tmp_table(thd, tmp_table_param, list, (ORDER*) 0, 1, + 0, + (select_lex->options | thd->options), + HA_POS_ERROR, (char*)""))) + return TRUE; + table->file->extra(HA_EXTRA_NO_ROWS); // Don't update rows + table->no_rows=1; + + if (table->s->db_type() == heap_hton) + { + /* + No blobs, otherwise it would have been MyISAM: set up a compare + function and its arguments to use with Unique. + */ + qsort_cmp2 compare_key; + void* cmp_arg; + Field **field= table->field; + Field **field_end= field + table->s->fields; + bool all_binary= TRUE; + + for (tree_key_length= 0; field < field_end; ++field) + { + Field *f= *field; + enum enum_field_types type= f->type(); + tree_key_length+= f->pack_length(); + if ((type == MYSQL_TYPE_VARCHAR) || + (!f->binary() && (type == MYSQL_TYPE_STRING || + type == MYSQL_TYPE_VAR_STRING))) + { + all_binary= FALSE; + break; + } + } + if (all_binary) + { + cmp_arg= (void*) &tree_key_length; + compare_key= (qsort_cmp2) simple_raw_key_cmp; + } + else + { + if (table->s->fields == 1) + { + /* + If we have only one field, which is the most common use of + count(distinct), it is much faster to use a simpler key + compare method that can take advantage of not having to worry + about other fields. + */ + compare_key= (qsort_cmp2) simple_str_key_cmp; + cmp_arg= (void*) table->field[0]; + /* tree_key_length has been set already */ + } + else + { + uint32 *length; + compare_key= (qsort_cmp2) composite_key_cmp; + cmp_arg= (void*) this; + field_lengths= (uint32*) thd->alloc(table->s->fields * sizeof(uint32)); + for (tree_key_length= 0, length= field_lengths, field= table->field; + field < field_end; ++field, ++length) + { + *length= (*field)->pack_length(); + tree_key_length+= *length; + } + } + } + DBUG_ASSERT(tree == 0); + tree= new Unique(compare_key, cmp_arg, tree_key_length, + thd->variables.max_heap_table_size); + /* + The only time tree_key_length could be 0 is if someone does + count(distinct) on a char(0) field - stupid thing to do, + but this has to be handled - otherwise someone can crash + the server with a DoS attack + */ + if (! tree) + return TRUE; + } + return FALSE; + } + else + { + List field_list; + Create_field field_def; /* field definition */ + Item *arg; + DBUG_ENTER("Item_sum_distinct::setup"); + /* It's legal to call setup() more than once when in a subquery */ + if (tree) + return FALSE; + + /* + Virtual table and the tree are created anew on each re-execution of + PS/SP. Hence all further allocations are performed in the runtime + mem_root. + */ + if (field_list.push_back(&field_def)) + return TRUE; + + item_sum->null_value= item_sum->maybe_null= 1; + item_sum->quick_group= 0; + + DBUG_ASSERT(item_sum->get_arg(0)->fixed); + + arg = item_sum->get_arg(0); + if (arg->const_item()) + { + (void) arg->val_int(); + if (arg->null_value) + always_null=1; + } + + if (always_null) + return FALSE; + + enum enum_field_types field_type; + + field_type= calc_tmp_field_type(arg->field_type(), + arg->result_type()); + field_def.init_for_tmp_table(field_type, + arg->max_length, + arg->decimals, + arg->maybe_null, + arg->unsigned_flag); + + if (! (table= create_virtual_tmp_table(thd, field_list))) + return TRUE; + + /* XXX: check that the case of CHAR(0) works OK */ + tree_key_length= table->s->reclength - table->s->null_bytes; + + /* + Unique handles all unique elements in a tree until they can't fit + in. Then the tree is dumped to the temporary file. We can use + simple_raw_key_cmp because the table contains numbers only; decimals + are converted to binary representation as well. + */ + tree= new Unique(simple_raw_key_cmp, &tree_key_length, tree_key_length, + thd->variables.max_heap_table_size); + + DBUG_RETURN(tree == 0); + } +} + + +/** + Invalidate calculated value and clear the distinct rows. + + Frees space used by the internal data structures. + Removes the accumulated distinct rows. Invalidates the calculated result. +*/ + +void Aggregator_distinct::clear() +{ + endup_done= FALSE; + item_sum->clear(); + if (tree) + tree->reset(); + /* tree and table can be both null only if always_null */ + if (item_sum->sum_func() == Item_sum::COUNT_FUNC || + item_sum->sum_func() == Item_sum::COUNT_DISTINCT_FUNC) + { + if (!tree && table) + { + table->file->extra(HA_EXTRA_NO_CACHE); + table->file->ha_delete_all_rows(); + table->file->extra(HA_EXTRA_WRITE_CACHE); + } + } + else + { + item_sum->null_value= 1; + } +} + + +/** + Process incoming row. + + Add it to Unique/temp hash table if it's unique. Skip the row if + not unique. + Prepare Aggregator_distinct to process the incoming stream. + Create the temporary table and the Unique class if needed. + Called by Item_sum::aggregator_add(). + To actually get the result value in item_sum's buffers + Aggregator_distinct::endup() must be called. + + @return status + @retval FALSE success + @retval TRUE failure +*/ + +bool Aggregator_distinct::add() +{ + if (always_null) + return 0; + + if (item_sum->sum_func() == Item_sum::COUNT_FUNC || + item_sum->sum_func() == Item_sum::COUNT_DISTINCT_FUNC) + { + int error; + copy_fields(tmp_table_param); + copy_funcs(tmp_table_param->items_to_copy); + + for (Field **field=table->field ; *field ; field++) + if ((*field)->is_real_null(0)) + return 0; // Don't count NULL + + if (tree) + { + /* + The first few bytes of record (at least one) are just markers + for deleted and NULLs. We want to skip them since they will + bloat the tree without providing any valuable info. Besides, + key_length used to initialize the tree didn't include space for them. + */ + return tree->unique_add(table->record[0] + table->s->null_bytes); + } + if ((error= table->file->ha_write_row(table->record[0])) && + table->file->is_fatal_error(error, HA_CHECK_DUP)) + return TRUE; + return FALSE; + } + else + { + item_sum->get_arg(0)->save_in_field(table->field[0], FALSE); + if (table->field[0]->is_null()) + return 0; + DBUG_ASSERT(tree); + item_sum->null_value= 0; + /* + '0' values are also stored in the tree. This doesn't matter + for SUM(DISTINCT), but is important for AVG(DISTINCT) + */ + return tree->unique_add(table->field[0]->ptr); + } +} + + +/** + Calculate the aggregate function value. + + Since Distinct_aggregator::add() just collects the distinct rows, + we must go over the distinct rows and feed them to the aggregation + function before returning its value. + This is what endup () does. It also sets the result validity flag + endup_done to TRUE so it will not recalculate the aggregate value + again if the Item_sum hasn't been reset. +*/ + +void Aggregator_distinct::endup() +{ + /* prevent consecutive recalculations */ + if (endup_done) + return; + + /* we are going to calculate the aggregate value afresh */ + item_sum->clear(); + + /* The result will definitely be null : no more calculations needed */ + if (always_null) + return; + + if (item_sum->sum_func() == Item_sum::COUNT_FUNC || + item_sum->sum_func() == Item_sum::COUNT_DISTINCT_FUNC) + { + DBUG_ASSERT(item_sum->fixed == 1); + Item_sum_count *sum= (Item_sum_count *)item_sum; + if (tree && tree->elements == 0) + { + /* everything fits in memory */ + sum->count= (longlong) tree->elements_in_tree(); + endup_done= TRUE; + } + if (!tree) + { + /* there were blobs */ + table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); + sum->count= table->file->stats.records; + endup_done= TRUE; + } + } + else + { + /* + We don't have a tree only if 'setup()' hasn't been called; + this is the case of sql_select.cc:return_zero_rows. + */ + if (tree) + table->field[0]->set_notnull(); + } + + if (tree && !endup_done) + { + /* go over the tree of distinct keys and calculate the aggregate value */ + use_distinct_values= TRUE; + tree->walk(item_sum_distinct_walk, (void*) this); + use_distinct_values= FALSE; + } + /* prevent consecutive recalculations */ + endup_done= TRUE; +} + + String * Item_sum_num::val_str(String *str) { @@ -823,10 +1328,27 @@ void Item_sum_sum::fix_length_and_dec() bool Item_sum_sum::add() { DBUG_ENTER("Item_sum_sum::add"); + bool arg_is_null; if (hybrid_type == DECIMAL_RESULT) { - my_decimal value, *val= args[0]->val_decimal(&value); - if (!args[0]->null_value) + my_decimal value, *val; + if (aggr->use_distinct_values) + { + /* + We are aggregating distinct rows. Get the value from the distinct + table pointer + */ + Aggregator_distinct *daggr= (Aggregator_distinct *)aggr; + val= daggr->table->field[0]->val_decimal (&value); + arg_is_null= daggr->table->field[0]->is_null(); + } + else + { + /* non-distinct aggregation */ + val= args[0]->val_decimal(&value); + arg_is_null= args[0]->null_value; + } + if (!arg_is_null) { my_decimal_add(E_DEC_FATAL_ERROR, dec_buffs + (curr_dec_buff^1), val, dec_buffs + curr_dec_buff); @@ -836,8 +1358,25 @@ bool Item_sum_sum::add() } else { - sum+= args[0]->val_real(); - if (!args[0]->null_value) + double val; + if (aggr->use_distinct_values) + { + /* + We are aggregating distinct rows. Get the value from the distinct + table pointer + */ + Aggregator_distinct *daggr= (Aggregator_distinct *)aggr; + val= daggr->table->field[0]->val_real (); + arg_is_null= daggr->table->field[0]->is_null(); + } + else + { + /* non-distinct aggregation */ + val= args[0]->val_real(); + arg_is_null= args[0]->null_value; + } + sum+= val; + if (!arg_is_null) null_value= 0; } DBUG_RETURN(0); @@ -847,6 +1386,8 @@ bool Item_sum_sum::add() longlong Item_sum_sum::val_int() { DBUG_ASSERT(fixed == 1); + if (aggr) + aggr->endup(); if (hybrid_type == DECIMAL_RESULT) { longlong result; @@ -861,6 +1402,8 @@ longlong Item_sum_sum::val_int() double Item_sum_sum::val_real() { DBUG_ASSERT(fixed == 1); + if (aggr) + aggr->endup(); if (hybrid_type == DECIMAL_RESULT) my_decimal2double(E_DEC_FATAL_ERROR, dec_buffs + curr_dec_buff, &sum); return sum; @@ -869,6 +1412,8 @@ double Item_sum_sum::val_real() String *Item_sum_sum::val_str(String *str) { + if (aggr) + aggr->endup(); if (hybrid_type == DECIMAL_RESULT) return val_string_from_decimal(str); return val_string_from_real(str); @@ -877,311 +1422,54 @@ String *Item_sum_sum::val_str(String *str) my_decimal *Item_sum_sum::val_decimal(my_decimal *val) { + if (aggr) + aggr->endup(); if (hybrid_type == DECIMAL_RESULT) return (dec_buffs + curr_dec_buff); return val_decimal_from_real(val); } -/***************************************************************************/ - -C_MODE_START - -/* Declarations for auxilary C-callbacks */ - -static int simple_raw_key_cmp(void* arg, const void* key1, const void* key2) -{ - return memcmp(key1, key2, *(uint *) arg); -} - - -static int item_sum_distinct_walk(void *element, element_count num_of_dups, - void *item) -{ - return ((Item_sum_distinct*) (item))->unique_walk_function(element); -} - -C_MODE_END - -/* Item_sum_distinct */ - -Item_sum_distinct::Item_sum_distinct(Item *item_arg) - :Item_sum_num(item_arg), tree(0) -{ - /* - quick_group is an optimizer hint, which means that GROUP BY can be - handled with help of index on grouped columns. - By setting quick_group to zero we force creation of temporary table - to perform GROUP BY. - */ - quick_group= 0; -} - - -Item_sum_distinct::Item_sum_distinct(THD *thd, Item_sum_distinct *original) - :Item_sum_num(thd, original), val(original->val), tree(0), - table_field_type(original->table_field_type) -{ - quick_group= 0; -} - - /** - Behaves like an Integer except to fix_length_and_dec(). - Additionally div() converts val with this traits to a val with true - decimal traits along with conversion of integer value to decimal value. - This is to speedup SUM/AVG(DISTINCT) evaluation for 8-32 bit integer - values. + Aggregate a distinct row from the distinct hash table. + + Called for each row into the hash table 'Aggregator_distinct::table'. + Includes the current distinct row into the calculation of the + aggregate value. Uses the Field classes to get the value from the row. + This function is used for AVG/SUM(DISTINCT). For COUNT(DISTINCT) + it's called only when there are no blob arguments and the data don't + fit into memory (so Unique makes persisted trees on disk). + + @param element pointer to the row data. + + @return status + @retval FALSE success + @retval TRUE failure */ -struct Hybrid_type_traits_fast_decimal: public - Hybrid_type_traits_integer -{ - virtual Item_result type() const { return DECIMAL_RESULT; } - virtual void fix_length_and_dec(Item *item, Item *arg) const - { Hybrid_type_traits_decimal::instance()->fix_length_and_dec(item, arg); } - - virtual void div(Hybrid_type *val, ulonglong u) const - { - int2my_decimal(E_DEC_FATAL_ERROR, val->integer, 0, val->dec_buf); - val->used_dec_buf_no= 0; - val->traits= Hybrid_type_traits_decimal::instance(); - val->traits->div(val, u); - } - static const Hybrid_type_traits_fast_decimal *instance(); - Hybrid_type_traits_fast_decimal() {}; -}; - -static const Hybrid_type_traits_fast_decimal fast_decimal_traits_instance; - -const Hybrid_type_traits_fast_decimal - *Hybrid_type_traits_fast_decimal::instance() -{ - return &fast_decimal_traits_instance; -} - -void Item_sum_distinct::fix_length_and_dec() -{ - DBUG_ASSERT(args[0]->fixed); - - table_field_type= args[0]->field_type(); - - /* Adjust tmp table type according to the chosen aggregation type */ - switch (args[0]->result_type()) { - case STRING_RESULT: - case REAL_RESULT: - val.traits= Hybrid_type_traits::instance(); - if (table_field_type != MYSQL_TYPE_FLOAT) - table_field_type= MYSQL_TYPE_DOUBLE; - break; - case INT_RESULT: - /* - Preserving int8, int16, int32 field types gives ~10% performance boost - as the size of result tree becomes significantly smaller. - Another speed up we gain by using longlong for intermediate - calculations. The range of int64 is enough to hold sum 2^32 distinct - integers each <= 2^32. - */ - if (table_field_type == MYSQL_TYPE_INT24 || - (table_field_type >= MYSQL_TYPE_TINY && - table_field_type <= MYSQL_TYPE_LONG)) - { - val.traits= Hybrid_type_traits_fast_decimal::instance(); - break; - } - table_field_type= MYSQL_TYPE_LONGLONG; - /* fallthrough */ - case DECIMAL_RESULT: - val.traits= Hybrid_type_traits_decimal::instance(); - if (table_field_type != MYSQL_TYPE_LONGLONG) - table_field_type= MYSQL_TYPE_NEWDECIMAL; - break; - case ROW_RESULT: - default: - DBUG_ASSERT(0); - } - val.traits->fix_length_and_dec(this, args[0]); -} - - -/** - @todo - check that the case of CHAR(0) works OK -*/ -bool Item_sum_distinct::setup(THD *thd) -{ - List field_list; - Create_field field_def; /* field definition */ - DBUG_ENTER("Item_sum_distinct::setup"); - /* It's legal to call setup() more than once when in a subquery */ - if (tree) - DBUG_RETURN(FALSE); - - /* - Virtual table and the tree are created anew on each re-execution of - PS/SP. Hence all further allocations are performed in the runtime - mem_root. - */ - if (field_list.push_back(&field_def)) - DBUG_RETURN(TRUE); - - null_value= maybe_null= 1; - quick_group= 0; - - DBUG_ASSERT(args[0]->fixed); - - field_def.init_for_tmp_table(table_field_type, args[0]->max_length, - args[0]->decimals, args[0]->maybe_null, - args[0]->unsigned_flag); - - if (! (table= create_virtual_tmp_table(thd, field_list))) - DBUG_RETURN(TRUE); - - /* XXX: check that the case of CHAR(0) works OK */ - tree_key_length= table->s->reclength - table->s->null_bytes; - - /* - Unique handles all unique elements in a tree until they can't fit - in. Then the tree is dumped to the temporary file. We can use - simple_raw_key_cmp because the table contains numbers only; decimals - are converted to binary representation as well. - */ - tree= new Unique(simple_raw_key_cmp, &tree_key_length, tree_key_length, - thd->variables.max_heap_table_size); - - is_evaluated= FALSE; - DBUG_RETURN(tree == 0); -} - - -bool Item_sum_distinct::add() -{ - args[0]->save_in_field(table->field[0], FALSE); - is_evaluated= FALSE; - if (!table->field[0]->is_null()) - { - DBUG_ASSERT(tree); - null_value= 0; - /* - '0' values are also stored in the tree. This doesn't matter - for SUM(DISTINCT), but is important for AVG(DISTINCT) - */ - return tree->unique_add(table->field[0]->ptr); - } - return 0; -} - - -bool Item_sum_distinct::unique_walk_function(void *element) + +bool Aggregator_distinct::unique_walk_function(void *element) { memcpy(table->field[0]->ptr, element, tree_key_length); - ++count; - val.traits->add(&val, table->field[0]); + item_sum->add(); return 0; } -void Item_sum_distinct::clear() +Aggregator_distinct::~Aggregator_distinct() { - DBUG_ENTER("Item_sum_distinct::clear"); - DBUG_ASSERT(tree != 0); /* we always have a tree */ - null_value= 1; - tree->reset(); - is_evaluated= FALSE; - DBUG_VOID_RETURN; -} - -void Item_sum_distinct::cleanup() -{ - Item_sum_num::cleanup(); - delete tree; - tree= 0; - table= 0; - is_evaluated= FALSE; -} - -Item_sum_distinct::~Item_sum_distinct() -{ - delete tree; - /* no need to free the table */ -} - - -void Item_sum_distinct::calculate_val_and_count() -{ - if (!is_evaluated) + if (tree) { - count= 0; - val.traits->set_zero(&val); - /* - We don't have a tree only if 'setup()' hasn't been called; - this is the case of sql_select.cc:return_zero_rows. - */ - if (tree) - { - table->field[0]->set_notnull(); - tree->walk(item_sum_distinct_walk, (void*) this); - } - is_evaluated= TRUE; + delete tree; + tree= NULL; } -} - - -double Item_sum_distinct::val_real() -{ - calculate_val_and_count(); - return val.traits->val_real(&val); -} - - -my_decimal *Item_sum_distinct::val_decimal(my_decimal *to) -{ - calculate_val_and_count(); - if (null_value) - return 0; - return val.traits->val_decimal(&val, to); -} - - -longlong Item_sum_distinct::val_int() -{ - calculate_val_and_count(); - return val.traits->val_int(&val, unsigned_flag); -} - - -String *Item_sum_distinct::val_str(String *str) -{ - calculate_val_and_count(); - if (null_value) - return 0; - return val.traits->val_str(&val, str, decimals); -} - -/* end of Item_sum_distinct */ - -/* Item_sum_avg_distinct */ - -void -Item_sum_avg_distinct::fix_length_and_dec() -{ - Item_sum_distinct::fix_length_and_dec(); - prec_increment= current_thd->variables.div_precincrement; - /* - AVG() will divide val by count. We need to reserve digits - after decimal point as the result can be fractional. - */ - decimals= min(decimals + prec_increment, NOT_FIXED_DEC); -} - - -void -Item_sum_avg_distinct::calculate_val_and_count() -{ - if (!is_evaluated) + if (table) { - Item_sum_distinct::calculate_val_and_count(); - if (count) - val.traits->div(&val, count); - is_evaluated= TRUE; + free_tmp_table(table->in_use, table); + table=NULL; + } + if (tmp_table_param) + { + delete tmp_table_param; + tmp_table_param= NULL; } } @@ -1208,6 +1496,8 @@ bool Item_sum_count::add() longlong Item_sum_count::val_int() { DBUG_ASSERT(fixed == 1); + if (aggr) + aggr->endup(); return (longlong) count; } @@ -1298,6 +1588,8 @@ bool Item_sum_avg::add() double Item_sum_avg::val_real() { DBUG_ASSERT(fixed == 1); + if (aggr) + aggr->endup(); if (!count) { null_value=1; @@ -1312,6 +1604,8 @@ my_decimal *Item_sum_avg::val_decimal(my_decimal *val) my_decimal sum_buff, cnt; const my_decimal *sum_dec; DBUG_ASSERT(fixed == 1); + if (aggr) + aggr->endup(); if (!count) { null_value=1; @@ -1334,6 +1628,8 @@ my_decimal *Item_sum_avg::val_decimal(my_decimal *val) String *Item_sum_avg::val_str(String *str) { + if (aggr) + aggr->endup(); if (hybrid_type == DECIMAL_RESULT) return val_string_from_decimal(str); return val_string_from_real(str); @@ -2029,6 +2325,7 @@ void Item_sum_hybrid::reset_field() void Item_sum_sum::reset_field() { + DBUG_ASSERT (aggr->Aggrtype() != Aggregator::DISTINCT_AGGREGATOR); if (hybrid_type == DECIMAL_RESULT) { my_decimal value, *arg_val= args[0]->val_decimal(&value); @@ -2053,6 +2350,7 @@ void Item_sum_count::reset_field() { uchar *res=result_field->ptr; longlong nr=0; + DBUG_ASSERT (aggr->Aggrtype() != Aggregator::DISTINCT_AGGREGATOR); if (!args[0]->maybe_null || !args[0]->is_null()) nr=1; @@ -2063,6 +2361,7 @@ void Item_sum_count::reset_field() void Item_sum_avg::reset_field() { uchar *res=result_field->ptr; + DBUG_ASSERT (aggr->Aggrtype() != Aggregator::DISTINCT_AGGREGATOR); if (hybrid_type == DECIMAL_RESULT) { longlong tmp; @@ -2116,6 +2415,7 @@ void Item_sum_bit::update_field() void Item_sum_sum::update_field() { + DBUG_ASSERT (aggr->Aggrtype() != Aggregator::DISTINCT_AGGREGATOR); if (hybrid_type == DECIMAL_RESULT) { my_decimal value, *arg_val= args[0]->val_decimal(&value); @@ -2168,6 +2468,9 @@ void Item_sum_avg::update_field() { longlong field_count; uchar *res=result_field->ptr; + + DBUG_ASSERT (aggr->Aggrtype() != Aggregator::DISTINCT_AGGREGATOR); + if (hybrid_type == DECIMAL_RESULT) { my_decimal value, *arg_val= args[0]->val_decimal(&value); @@ -2469,318 +2772,6 @@ double Item_variance_field::val_real() } -/**************************************************************************** -** COUNT(DISTINCT ...) -****************************************************************************/ - -int simple_str_key_cmp(void* arg, uchar* key1, uchar* key2) -{ - Field *f= (Field*) arg; - return f->cmp(key1, key2); -} - -/** - Did not make this one static - at least gcc gets confused when - I try to declare a static function as a friend. If you can figure - out the syntax to make a static function a friend, make this one - static -*/ - -int composite_key_cmp(void* arg, uchar* key1, uchar* key2) -{ - Item_sum_count_distinct* item = (Item_sum_count_distinct*)arg; - Field **field = item->table->field; - Field **field_end= field + item->table->s->fields; - uint32 *lengths=item->field_lengths; - for (; field < field_end; ++field) - { - Field* f = *field; - int len = *lengths++; - int res = f->cmp(key1, key2); - if (res) - return res; - key1 += len; - key2 += len; - } - return 0; -} - - -C_MODE_START - -static int count_distinct_walk(void *elem, element_count count, void *arg) -{ - (*((ulonglong*)arg))++; - return 0; -} - -C_MODE_END - - -void Item_sum_count_distinct::cleanup() -{ - DBUG_ENTER("Item_sum_count_distinct::cleanup"); - Item_sum_int::cleanup(); - - /* Free objects only if we own them. */ - if (!original) - { - /* - We need to delete the table and the tree in cleanup() as - they were allocated in the runtime memroot. Using the runtime - memroot reduces memory footprint for PS/SP and simplifies setup(). - */ - delete tree; - tree= 0; - is_evaluated= FALSE; - if (table) - { - free_tmp_table(table->in_use, table); - table= 0; - } - delete tmp_table_param; - tmp_table_param= 0; - } - always_null= FALSE; - DBUG_VOID_RETURN; -} - - -/** - This is used by rollup to create a separate usable copy of - the function. -*/ - -void Item_sum_count_distinct::make_unique() -{ - table=0; - original= 0; - force_copy_fields= 1; - tree= 0; - is_evaluated= FALSE; - tmp_table_param= 0; - always_null= FALSE; -} - - -Item_sum_count_distinct::~Item_sum_count_distinct() -{ - cleanup(); -} - - -bool Item_sum_count_distinct::setup(THD *thd) -{ - List list; - SELECT_LEX *select_lex= thd->lex->current_select; - - /* - Setup can be called twice for ROLLUP items. This is a bug. - Please add DBUG_ASSERT(tree == 0) here when it's fixed. - It's legal to call setup() more than once when in a subquery - */ - if (tree || table || tmp_table_param) - return FALSE; - - if (!(tmp_table_param= new TMP_TABLE_PARAM)) - return TRUE; - - /* Create a table with an unique key over all parameters */ - for (uint i=0; i < arg_count ; i++) - { - Item *item=args[i]; - if (list.push_back(item)) - return TRUE; // End of memory - if (item->const_item() && item->is_null()) - always_null= 1; - } - if (always_null) - return FALSE; - count_field_types(select_lex, tmp_table_param, list, 0); - tmp_table_param->force_copy_fields= force_copy_fields; - DBUG_ASSERT(table == 0); - /* - Make create_tmp_table() convert BIT columns to BIGINT. - This is needed because BIT fields store parts of their data in table's - null bits, and we don't have methods to compare two table records, which - is needed by Unique which is used when HEAP table is used. - */ - { - List_iterator_fast li(list); - Item *item; - while ((item= li++)) - { - if (item->type() == Item::FIELD_ITEM && - ((Item_field*)item)->field->type() == FIELD_TYPE_BIT) - item->marker=4; - } - } - - if (!(table= create_tmp_table(thd, tmp_table_param, list, (ORDER*) 0, 1, - 0, - (select_lex->options | thd->options), - HA_POS_ERROR, (char*)""))) - return TRUE; - table->file->extra(HA_EXTRA_NO_ROWS); // Don't update rows - table->no_rows=1; - - if (table->s->db_type() == heap_hton) - { - /* - No blobs, otherwise it would have been MyISAM: set up a compare - function and its arguments to use with Unique. - */ - qsort_cmp2 compare_key; - void* cmp_arg; - Field **field= table->field; - Field **field_end= field + table->s->fields; - bool all_binary= TRUE; - - for (tree_key_length= 0; field < field_end; ++field) - { - Field *f= *field; - enum enum_field_types f_type= f->type(); - tree_key_length+= f->pack_length(); - if ((f_type == MYSQL_TYPE_VARCHAR) || - (!f->binary() && (f_type == MYSQL_TYPE_STRING || - f_type == MYSQL_TYPE_VAR_STRING))) - { - all_binary= FALSE; - break; - } - } - if (all_binary) - { - cmp_arg= (void*) &tree_key_length; - compare_key= (qsort_cmp2) simple_raw_key_cmp; - } - else - { - if (table->s->fields == 1) - { - /* - If we have only one field, which is the most common use of - count(distinct), it is much faster to use a simpler key - compare method that can take advantage of not having to worry - about other fields. - */ - compare_key= (qsort_cmp2) simple_str_key_cmp; - cmp_arg= (void*) table->field[0]; - /* tree_key_length has been set already */ - } - else - { - uint32 *length; - compare_key= (qsort_cmp2) composite_key_cmp; - cmp_arg= (void*) this; - field_lengths= (uint32*) thd->alloc(table->s->fields * sizeof(uint32)); - for (tree_key_length= 0, length= field_lengths, field= table->field; - field < field_end; ++field, ++length) - { - *length= (*field)->pack_length(); - tree_key_length+= *length; - } - } - } - DBUG_ASSERT(tree == 0); - tree= new Unique(compare_key, cmp_arg, tree_key_length, - thd->variables.max_heap_table_size); - /* - The only time tree_key_length could be 0 is if someone does - count(distinct) on a char(0) field - stupid thing to do, - but this has to be handled - otherwise someone can crash - the server with a DoS attack - */ - is_evaluated= FALSE; - if (! tree) - return TRUE; - } - return FALSE; -} - - -Item *Item_sum_count_distinct::copy_or_same(THD* thd) -{ - return new (thd->mem_root) Item_sum_count_distinct(thd, this); -} - - -void Item_sum_count_distinct::clear() -{ - /* tree and table can be both null only if always_null */ - is_evaluated= FALSE; - if (tree) - { - tree->reset(); - } - else if (table) - { - table->file->extra(HA_EXTRA_NO_CACHE); - table->file->ha_delete_all_rows(); - table->file->extra(HA_EXTRA_WRITE_CACHE); - } -} - -bool Item_sum_count_distinct::add() -{ - int error; - if (always_null) - return 0; - copy_fields(tmp_table_param); - copy_funcs(tmp_table_param->items_to_copy); - - for (Field **field=table->field ; *field ; field++) - if ((*field)->is_real_null(0)) - return 0; // Don't count NULL - - is_evaluated= FALSE; - if (tree) - { - /* - The first few bytes of record (at least one) are just markers - for deleted and NULLs. We want to skip them since they will - bloat the tree without providing any valuable info. Besides, - key_length used to initialize the tree didn't include space for them. - */ - return tree->unique_add(table->record[0] + table->s->null_bytes); - } - if ((error= table->file->ha_write_row(table->record[0])) && - table->file->is_fatal_error(error, HA_CHECK_DUP)) - return TRUE; - return FALSE; -} - - -longlong Item_sum_count_distinct::val_int() -{ - int error; - DBUG_ASSERT(fixed == 1); - if (!table) // Empty query - return LL(0); - if (tree) - { - if (is_evaluated) - return count; - - if (tree->elements == 0) - return (longlong) tree->elements_in_tree(); // everything fits in memory - count= 0; - tree->walk(count_distinct_walk, (void*) &count); - is_evaluated= TRUE; - return (longlong) count; - } - - error= table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); - - if(error) - { - table->file->print_error(error, MYF(0)); - } - - return table->file->stats.records; -} - - /**************************************************************************** ** Functions to handle dynamic loadable aggregates ** Original source by: Alexis Mikhailov diff --git a/sql/item_sum.h b/sql/item_sum.h index 8a20e2dd165..1dd1c5b5dd5 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -22,7 +22,87 @@ #include -/* +class Item_sum; +class Aggregator_distinct; +class Aggregator_simple; + +/** + The abstract base class for the Aggregator_* classes. + It implements the data collection functions (setup/add/clear) + as either pass-through to the real functionality or + as collectors into an Unique (for distinct) structure. + + Note that update_field/reset_field are not in that + class, because they're simply not called when + GROUP BY/DISTINCT can be handled with help of index on grouped + fields (quick_group = 0); +*/ + +class Aggregator : public Sql_alloc +{ + friend class Item_sum; + friend class Item_sum_sum; + friend class Item_sum_count; + friend class Item_sum_avg; + + /* + All members are protected as this class is not usable outside of an + Item_sum descendant. + */ +protected: + /* the aggregate function class to act on */ + Item_sum *item_sum; + + /** + When feeding back the data in endup() from Unique/temp table back to + Item_sum::add() methods we must read the data from Unique (and not + recalculate the functions that are given as arguments to the aggregate + function. + This flag is to tell the add() methods to take the data from the Unique + instead by calling the relevant val_..() method + */ + + bool use_distinct_values; + +public: + Aggregator (Item_sum *arg): item_sum(arg), use_distinct_values(FALSE) {} + virtual ~Aggregator () {} /* Keep gcc happy */ + + enum Aggregator_type { SIMPLE_AGGREGATOR, DISTINCT_AGGREGATOR }; + virtual Aggregator_type Aggrtype() = 0; + + /** + Called before adding the first row. + Allocates and sets up the internal aggregation structures used, + e.g. the Unique instance used to calculate distinct. + */ + virtual bool setup(THD *) = 0; + + /** + Called when we need to wipe out all the data from the aggregator : + all the values acumulated and all the state. + Cleans up the internal structures and resets them to their initial state. + */ + virtual void clear() = 0; + + /** + Called when there's a new value to be aggregated. + Updates the internal state of the aggregator to reflect the new value. + */ + virtual bool add() = 0; + + /** + Called when there are no more data and the final value is to be retrieved. + Finalises the state of the aggregator, so the final result can be retrieved. + */ + virtual void endup() = 0; + +}; + + +class st_select_lex; + +/** Class Item_sum is the base class used for special expressions that SQL calls 'set functions'. These expressions are formed with the help of aggregate functions such as SUM, MAX, GROUP_CONCAT etc. @@ -215,13 +295,32 @@ TODO: to catch queries where the limit is exceeded to make the code clean here. -*/ - -class st_select_lex; +*/ class Item_sum :public Item_result_field { public: + /** + Aggregator class instance. Not set initially. Allocated only after + it is determined if the incoming data are already distinct. + */ + Aggregator *aggr; + + /** + Used in making ROLLUP. Set for the ROLLUP copies of the original + Item_sum and passed to create_tmp_field() to cause it to work + over the temp table buffer that is referenced by + Item_result_field::result_field. + */ + bool force_copy_fields; + + /** + Indicates how the aggregate function was specified by the parser : + 1 if it was written as AGGREGATE(DISTINCT), + 0 if it was AGGREGATE() + */ + bool with_distinct; + enum Sumfunctype { COUNT_FUNC, COUNT_DISTINCT_FUNC, SUM_FUNC, SUM_DISTINCT_FUNC, AVG_FUNC, AVG_DISTINCT_FUNC, MIN_FUNC, MAX_FUNC, STD_FUNC, @@ -263,47 +362,28 @@ public: Item_sum() :quick_group(1), arg_count(0), forced_const(FALSE) { mark_as_sum_func(); + init_aggregator(); } Item_sum(Item *a) :quick_group(1), arg_count(1), args(tmp_args), orig_args(tmp_orig_args), forced_const(FALSE) { args[0]=a; mark_as_sum_func(); + init_aggregator(); } Item_sum( Item *a, Item *b ) :quick_group(1), arg_count(2), args(tmp_args), orig_args(tmp_orig_args), forced_const(FALSE) { args[0]=a; args[1]=b; mark_as_sum_func(); + init_aggregator(); } Item_sum(List &list); //Copy constructor, need to perform subselects with temporary tables Item_sum(THD *thd, Item_sum *item); enum Type type() const { return SUM_FUNC_ITEM; } virtual enum Sumfunctype sum_func () const=0; - - /* - This method is similar to add(), but it is called when the current - aggregation group changes. Thus it performs a combination of - clear() and add(). - */ - inline bool reset() { clear(); return add(); }; - - /* - Prepare this item for evaluation of an aggregate value. This is - called by reset() when a group changes, or, for correlated - subqueries, between subquery executions. E.g. for COUNT(), this - method should set count= 0; - */ - virtual void clear()= 0; - - /* - This method is called for the next row in the same group. Its - purpose is to aggregate the new value to the previous values in - the group (i.e. since clear() was called last time). For example, - for COUNT(), do count++. - */ - virtual bool add()=0; + inline bool reset() { aggregator_clear(); return aggregator_add(); }; /* Called when new group is started and results are being saved in @@ -343,11 +423,6 @@ public: { return new Item_field(field); } table_map used_tables() const { return used_tables_cache; } void update_used_tables (); - void cleanup() - { - Item::cleanup(); - forced_const= FALSE; - } bool is_null() { return null_value; } void make_const () { @@ -359,7 +434,9 @@ public: virtual void print(String *str, enum_query_type query_type); void fix_num_length_and_dec(); - /* + /** + Mark an aggregate as having no rows. + This function is called by the execution engine to assign 'NO ROWS FOUND' value to an aggregate item, when the underlying result set has no rows. Such value, in a general case, may be different from @@ -367,10 +444,15 @@ public: may be initialized to 0 by clear() and to NULL by no_rows_in_result(). */ - void no_rows_in_result() { clear(); } - - virtual bool setup(THD *thd) {return 0;} - virtual void make_unique() {} + void no_rows_in_result() + { + if (!aggr) + set_aggregator(with_distinct ? + Aggregator::DISTINCT_AGGREGATOR : + Aggregator::SIMPLE_AGGREGATOR); + reset(); + } + virtual void make_unique() { force_copy_fields= TRUE; } Item *get_tmp_table_item(THD *thd); virtual Field *create_tmp_field(bool group, TABLE *table, uint convert_blob_length); @@ -381,14 +463,178 @@ public: st_select_lex *depended_from() { return (nest_level == aggr_level ? 0 : aggr_sel); } - Item *get_arg(int i) { return args[i]; } - Item *set_arg(int i, THD *thd, Item *new_val); + Item *get_arg(uint i) { return args[i]; } + Item *set_arg(uint i, THD *thd, Item *new_val); uint get_arg_count() { return arg_count; } + + /* Initialization of distinct related members */ + void init_aggregator() + { + aggr= NULL; + with_distinct= FALSE; + force_copy_fields= FALSE; + } + + /** + Called to initialize the aggregator. + */ + + inline bool aggregator_setup(THD *thd) { return aggr->setup(thd); }; + + /** + Called to cleanup the aggregator. + */ + + inline void aggregator_clear() { aggr->clear(); } + + /** + Called to add value to the aggregator. + */ + + inline bool aggregator_add() { return aggr->add(); }; + + /* stores the declared DISTINCT flag (from the parser) */ + void set_distinct(bool distinct) + { + with_distinct= distinct; + quick_group= with_distinct ? 0 : 1; + } + + /** + Set the type of aggregation : DISTINCT or not. + + Called when the final determination is done about the aggregation + type and the object is about to be used. + */ + + int set_aggregator(Aggregator::Aggregator_type aggregator); + virtual void clear()= 0; + virtual bool add()= 0; + virtual bool setup(THD *thd) {return 0;} + + void cleanup (); +}; + + +class Unique; + + +/** + The distinct aggregator. + Implements AGGFN (DISTINCT ..) + Collects all the data into an Unique (similarly to what Item_sum_distinct + does currently) and then (if applicable) iterates over the list of + unique values and pumps them back into its object +*/ + +class Aggregator_distinct : public Aggregator +{ + friend class Item_sum_sum; + friend class Item_sum_count; + friend class Item_sum_avg; +protected: + + /* + flag to prevent consecutive runs of endup(). Normally in endup there are + expensive calculations (like walking the distinct tree for example) + which we must do only once if there are no data changes. + We can re-use the data for the second and subsequent val_xxx() calls. + endup_done set to TRUE also means that the calculated values for + the aggregate functions are correct and don't need recalculation. + */ + bool endup_done; + + /* + Used depending on the type of the aggregate function and the presence of + blob columns in it: + - For COUNT(DISTINCT) and no blob fields this points to a real temporary + table. It's used as a hash table. + - For AVG/SUM(DISTINCT) or COUNT(DISTINCT) with blob fields only the + in-memory data structure of a temporary table is constructed. + It's used by the Field classes to transform data into row format. + */ + TABLE *table; + + /* + An array of field lengths on row allocated and used only for + COUNT(DISTINCT) with multiple columns and no blobs. Used in + Aggregator_distinct::composite_key_cmp (called from Unique to compare + nodes + */ + uint32 *field_lengths; + + /* + used in conjunction with 'table' to support the access to Field classes + for COUNT(DISTINCT). Needed by copy_fields()/copy_funcs(). + */ + TMP_TABLE_PARAM *tmp_table_param; + + /* + If there are no blobs in the COUNT(DISTINCT) arguments, we can use a tree, + which is faster than heap table. In that case, we still use the table + to help get things set up, but we insert nothing in it. + For AVG/SUM(DISTINCT) we always use this tree (as it takes a single + argument) to get the distinct rows. + */ + Unique *tree; + + /* + The length of the temp table row. Must be a member of the class as it + gets passed down to simple_raw_key_cmp () as a compare function argument + to Unique. simple_raw_key_cmp () is used as a fast comparison function + when the entire row can be binary compared. + */ + uint tree_key_length; + + /* + Set to true if the result is known to be always NULL. + If set deactivates creation and usage of the temporary table (in the + 'table' member) and the Unique instance (in the 'tree' member) as well as + the calculation of the final value on the first call to + Item_[sum|avg|count]::val_xxx(). + */ + bool always_null; + +public: + Aggregator_distinct (Item_sum *sum) : + Aggregator(sum), table(NULL), tmp_table_param(NULL), tree(NULL), + always_null(FALSE) {} + virtual ~Aggregator_distinct (); + Aggregator_type Aggrtype() { return DISTINCT_AGGREGATOR; } + + bool setup(THD *); + void clear(); + bool add(); + void endup(); + + bool unique_walk_function(void *element); + static int composite_key_cmp(void* arg, uchar* key1, uchar* key2); +}; + + +/** + The pass-through aggregator. + Implements AGGFN (DISTINCT ..) by knowing it gets distinct data on input. + So it just pumps them back to the Item_sum descendant class. +*/ +class Aggregator_simple : public Aggregator +{ +public: + + Aggregator_simple (Item_sum *sum) : + Aggregator(sum) {} + Aggregator_type Aggrtype() { return Aggregator::SIMPLE_AGGREGATOR; } + + bool setup(THD * thd) { return item_sum->setup(thd); } + void clear() { item_sum->clear(); } + bool add() { return item_sum->add(); } + void endup() {}; }; class Item_sum_num :public Item_sum { + friend class Aggregator_distinct; protected: /* val_xxx() functions may be called several times during the execution of a @@ -443,9 +689,15 @@ protected: void fix_length_and_dec(); public: - Item_sum_sum(Item *item_par) :Item_sum_num(item_par) {} + Item_sum_sum(Item *item_par, bool distinct= FALSE) :Item_sum_num(item_par) + { + set_distinct(distinct); + } Item_sum_sum(THD *thd, Item_sum_sum *item); - enum Sumfunctype sum_func () const {return SUM_FUNC;} + enum Sumfunctype sum_func () const + { + return with_distinct ? SUM_DISTINCT_FUNC : SUM_FUNC; + } void clear(); bool add(); double val_real(); @@ -456,109 +708,50 @@ public: void reset_field(); void update_field(); void no_rows_in_result() {} - const char *func_name() const { return "sum("; } + const char *func_name() const + { + return with_distinct ? "sum(distinct " : "sum("; + } Item *copy_or_same(THD* thd); }; - -/* Common class for SUM(DISTINCT), AVG(DISTINCT) */ - -class Unique; - -class Item_sum_distinct :public Item_sum_num -{ -protected: - /* storage for the summation result */ - ulonglong count; - Hybrid_type val; - /* storage for unique elements */ - Unique *tree; - TABLE *table; - enum enum_field_types table_field_type; - uint tree_key_length; -protected: - Item_sum_distinct(THD *thd, Item_sum_distinct *item); -public: - Item_sum_distinct(Item *item_par); - ~Item_sum_distinct(); - - bool setup(THD *thd); - void clear(); - void cleanup(); - bool add(); - double val_real(); - my_decimal *val_decimal(my_decimal *); - longlong val_int(); - String *val_str(String *str); - - /* XXX: does it need make_unique? */ - - enum Sumfunctype sum_func () const { return SUM_DISTINCT_FUNC; } - void reset_field() {} // not used - void update_field() {} // not used - virtual void no_rows_in_result() {} - void fix_length_and_dec(); - enum Item_result result_type () const { return val.traits->type(); } - virtual void calculate_val_and_count(); - virtual bool unique_walk_function(void *elem); -}; - - -/* - Item_sum_sum_distinct - implementation of SUM(DISTINCT expr). - See also: MySQL manual, chapter 'Adding New Functions To MySQL' - and comments in item_sum.cc. -*/ - -class Item_sum_sum_distinct :public Item_sum_distinct -{ -private: - Item_sum_sum_distinct(THD *thd, Item_sum_sum_distinct *item) - :Item_sum_distinct(thd, item) {} -public: - Item_sum_sum_distinct(Item *item_arg) :Item_sum_distinct(item_arg) {} - - enum Sumfunctype sum_func () const { return SUM_DISTINCT_FUNC; } - const char *func_name() const { return "sum(distinct "; } - Item *copy_or_same(THD* thd) { return new Item_sum_sum_distinct(thd, this); } -}; - - -/* Item_sum_avg_distinct - SELECT AVG(DISTINCT expr) FROM ... */ - -class Item_sum_avg_distinct: public Item_sum_distinct -{ -private: - Item_sum_avg_distinct(THD *thd, Item_sum_avg_distinct *original) - :Item_sum_distinct(thd, original) {} -public: - uint prec_increment; - Item_sum_avg_distinct(Item *item_arg) : Item_sum_distinct(item_arg) {} - - void fix_length_and_dec(); - virtual void calculate_val_and_count(); - enum Sumfunctype sum_func () const { return AVG_DISTINCT_FUNC; } - const char *func_name() const { return "avg(distinct "; } - Item *copy_or_same(THD* thd) { return new Item_sum_avg_distinct(thd, this); } -}; - - class Item_sum_count :public Item_sum_int { longlong count; + friend class Aggregator_distinct; + + void clear(); + bool add(); + void cleanup(); + public: Item_sum_count(Item *item_par) :Item_sum_int(item_par),count(0) {} + + /** + Constructs an instance for COUNT(DISTINCT) + + @param list a list of the arguments to the aggregate function + + This constructor is called by the parser only for COUNT (DISTINCT). + */ + + Item_sum_count(List &list) + :Item_sum_int(list),count(0) + { + set_distinct(TRUE); + } Item_sum_count(THD *thd, Item_sum_count *item) :Item_sum_int(thd, item), count(item->count) {} - enum Sumfunctype sum_func () const { return COUNT_FUNC; } - void clear(); + enum Sumfunctype sum_func () const + { + return with_distinct ? COUNT_DISTINCT_FUNC : COUNT_FUNC; + } void no_rows_in_result() { count=0; } - bool add(); void make_const(longlong count_arg) { count=count_arg; @@ -566,79 +759,15 @@ class Item_sum_count :public Item_sum_int } longlong val_int(); void reset_field(); - void cleanup(); void update_field(); - const char *func_name() const { return "count("; } + const char *func_name() const + { + return with_distinct ? "count(distinct " : "count("; + } Item *copy_or_same(THD* thd); }; -class TMP_TABLE_PARAM; - -class Item_sum_count_distinct :public Item_sum_int -{ - TABLE *table; - uint32 *field_lengths; - TMP_TABLE_PARAM *tmp_table_param; - bool force_copy_fields; - /* - If there are no blobs, we can use a tree, which - is faster than heap table. In that case, we still use the table - to help get things set up, but we insert nothing in it - */ - Unique *tree; - /* - Storage for the value of count between calls to val_int() so val_int() - will not recalculate on each call. Validitiy of the value is stored in - is_evaluated. - */ - longlong count; - /* - Following is 0 normal object and pointer to original one for copy - (to correctly free resources) - */ - Item_sum_count_distinct *original; - uint tree_key_length; - - - bool always_null; // Set to 1 if the result is always NULL - - - friend int composite_key_cmp(void* arg, uchar* key1, uchar* key2); - friend int simple_str_key_cmp(void* arg, uchar* key1, uchar* key2); - -public: - Item_sum_count_distinct(List &list) - :Item_sum_int(list), table(0), field_lengths(0), tmp_table_param(0), - force_copy_fields(0), tree(0), count(0), - original(0), always_null(FALSE) - { quick_group= 0; } - Item_sum_count_distinct(THD *thd, Item_sum_count_distinct *item) - :Item_sum_int(thd, item), table(item->table), - field_lengths(item->field_lengths), - tmp_table_param(item->tmp_table_param), - force_copy_fields(0), tree(item->tree), count(item->count), - original(item), tree_key_length(item->tree_key_length), - always_null(item->always_null) - {} - ~Item_sum_count_distinct(); - - void cleanup(); - - enum Sumfunctype sum_func () const { return COUNT_DISTINCT_FUNC; } - void clear(); - bool add(); - longlong val_int(); - void reset_field() { return ;} // Never called - void update_field() { return ; } // Never called - const char *func_name() const { return "count(distinct "; } - bool setup(THD *thd); - void make_unique(); - Item *copy_or_same(THD* thd); - void no_rows_in_result() {} -}; - - /* Item to get the value of a stored sum function */ class Item_sum_avg; @@ -674,13 +803,18 @@ public: uint prec_increment; uint f_precision, f_scale, dec_bin_size; - Item_sum_avg(Item *item_par) :Item_sum_sum(item_par), count(0) {} + Item_sum_avg(Item *item_par, bool distinct= FALSE) + :Item_sum_sum(item_par, distinct), count(0) + {} Item_sum_avg(THD *thd, Item_sum_avg *item) :Item_sum_sum(thd, item), count(item->count), prec_increment(item->prec_increment) {} void fix_length_and_dec(); - enum Sumfunctype sum_func () const {return AVG_FUNC;} + enum Sumfunctype sum_func () const + { + return with_distinct ? AVG_DISTINCT_FUNC : AVG_FUNC; + } void clear(); bool add(); double val_real(); @@ -693,7 +827,10 @@ public: Item *result_item(Field *field) { return new Item_avg_field(hybrid_type, this); } void no_rows_in_result() {} - const char *func_name() const { return "avg("; } + const char *func_name() const + { + return with_distinct ? "avg(distinct " : "avg("; + } Item *copy_or_same(THD* thd); Field *create_tmp_field(bool group, TABLE *table, uint convert_blob_length); void cleanup() diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 05575e2744b..52e8d5d61c2 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -708,7 +708,8 @@ static TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge, double read_time); static -TRP_GROUP_MIN_MAX *get_best_group_min_max(PARAM *param, SEL_TREE *tree); +TRP_GROUP_MIN_MAX *get_best_group_min_max(PARAM *param, SEL_TREE *tree, + double read_time); static double get_index_only_read_time(const PARAM* param, ha_rows records, int keynr); @@ -2049,7 +2050,7 @@ public: class TRP_GROUP_MIN_MAX : public TABLE_READ_PLAN { private: - bool have_min, have_max; + bool have_min, have_max, have_agg_distinct; KEY_PART_INFO *min_max_arg_part; uint group_prefix_len; uint used_key_parts; @@ -2061,11 +2062,13 @@ private: SEL_TREE *range_tree; /* Represents all range predicates in the query. */ SEL_ARG *index_tree; /* The SEL_ARG sub-tree corresponding to index_info. */ uint param_idx; /* Index of used key in param->key. */ - /* Number of records selected by the ranges in index_tree. */ + bool is_index_scan; /* Use index_next() instead of random read */ public: + /* Number of records selected by the ranges in index_tree. */ ha_rows quick_prefix_records; public: - TRP_GROUP_MIN_MAX(bool have_min_arg, bool have_max_arg, + TRP_GROUP_MIN_MAX(bool have_min_arg, bool have_max_arg, + bool have_agg_distinct_arg, KEY_PART_INFO *min_max_arg_part_arg, uint group_prefix_len_arg, uint used_key_parts_arg, uint group_key_parts_arg, KEY *index_info_arg, @@ -2074,11 +2077,12 @@ public: SEL_TREE *tree_arg, SEL_ARG *index_tree_arg, uint param_idx_arg, ha_rows quick_prefix_records_arg) : have_min(have_min_arg), have_max(have_max_arg), + have_agg_distinct(have_agg_distinct_arg), min_max_arg_part(min_max_arg_part_arg), group_prefix_len(group_prefix_len_arg), used_key_parts(used_key_parts_arg), group_key_parts(group_key_parts_arg), index_info(index_info_arg), index(index_arg), key_infix_len(key_infix_len_arg), range_tree(tree_arg), - index_tree(index_tree_arg), param_idx(param_idx_arg), + index_tree(index_tree_arg), param_idx(param_idx_arg), is_index_scan(FALSE), quick_prefix_records(quick_prefix_records_arg) { if (key_infix_len) @@ -2088,6 +2092,7 @@ public: QUICK_SELECT_I *make_quick(PARAM *param, bool retrieve_full_rows, MEM_ROOT *parent_alloc); + void use_index_scan() { is_index_scan= TRUE; } }; @@ -2349,7 +2354,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use, Try to construct a QUICK_GROUP_MIN_MAX_SELECT. Notice that it can be constructed no matter if there is a range tree. */ - group_trp= get_best_group_min_max(¶m, tree); + group_trp= get_best_group_min_max(¶m, tree, best_read_time); if (group_trp) { param.table->quick_condition_rows= min(group_trp->records, @@ -9048,15 +9053,10 @@ cost_group_min_max(TABLE* table, KEY *index_info, uint used_key_parts, double *read_cost, ha_rows *records); -/* +/** Test if this access method is applicable to a GROUP query with MIN/MAX functions, and if so, construct a new TRP object. - SYNOPSIS - get_best_group_min_max() - param Parameter from test_quick_select - sel_tree Range tree generated by get_mm_tree - DESCRIPTION Test whether a query can be computed via a QUICK_GROUP_MIN_MAX_SELECT. Queries computable via a QUICK_GROUP_MIN_MAX_SELECT must satisfy the @@ -9167,17 +9167,16 @@ cost_group_min_max(TABLE* table, KEY *index_info, uint used_key_parts, - Lift the limitation in condition (B3), that is, make this access method applicable to ROLLUP queries. - RETURN - If mem_root != NULL - - valid TRP_GROUP_MIN_MAX object if this QUICK class can be used for - the query - - NULL o/w. - If mem_root == NULL - - NULL + @param param Parameter from test_quick_select + @param sel_tree Range tree generated by get_mm_tree + @param read_time Best read time so far (=table/index scan time) + @return table read plan + @retval NULL Loose index scan not applicable or mem_root == NULL + @retval !NULL Loose index scan table read plan */ static TRP_GROUP_MIN_MAX * -get_best_group_min_max(PARAM *param, SEL_TREE *tree) +get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time) { THD *thd= param->thd; JOIN *join= thd->lex->current_select->join; @@ -9198,25 +9197,33 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree) ORDER *tmp_group; Item *item; Item_field *item_field; + bool is_agg_distinct; + List agg_distinct_flds; + DBUG_ENTER("get_best_group_min_max"); /* Perform few 'cheap' tests whether this access method is applicable. */ if (!join) DBUG_RETURN(NULL); /* This is not a select statement. */ if ((join->tables != 1) || /* The query must reference one table. */ - ((!join->group_list) && /* Neither GROUP BY nor a DISTINCT query. */ - (!join->select_distinct)) || (join->select_lex->olap == ROLLUP_TYPE)) /* Check (B3) for ROLLUP */ DBUG_RETURN(NULL); if (table->s->keys == 0) /* There are no indexes to use. */ DBUG_RETURN(NULL); - /* Analyze the query in more detail. */ - 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)) DBUG_RETURN(NULL); + + List_iterator select_items_it(join->fields_list); + is_agg_distinct = is_indexed_agg_distinct(join, &agg_distinct_flds); + + if ((!join->group_list) && /* Neither GROUP BY nor a DISTINCT query. */ + (!join->select_distinct) && + !is_agg_distinct) + DBUG_RETURN(NULL); + /* Analyze the query in more detail. */ + if (join->sum_funcs[0]) { Item_sum *min_max_item; @@ -9227,6 +9234,10 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree) have_min= TRUE; else if (min_max_item->sum_func() == Item_sum::MAX_FUNC) have_max= TRUE; + else if (min_max_item->sum_func() == Item_sum::COUNT_DISTINCT_FUNC || + min_max_item->sum_func() == Item_sum::SUM_DISTINCT_FUNC || + min_max_item->sum_func() == Item_sum::AVG_DISTINCT_FUNC) + continue; else DBUG_RETURN(NULL); @@ -9243,13 +9254,12 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree) DBUG_RETURN(NULL); } } - /* Check (SA5). */ if (join->select_distinct) { while ((item= select_items_it++)) { - if (item->type() != Item::FIELD_ITEM) + if (item->real_item()->type() != Item::FIELD_ITEM) DBUG_RETURN(NULL); } } @@ -9257,7 +9267,7 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree) /* Check (GA4) - that there are no expressions among the group attributes. */ for (tmp_group= join->group_list; tmp_group; tmp_group= tmp_group->next) { - if ((*tmp_group->item)->type() != Item::FIELD_ITEM) + if ((*tmp_group->item)->real_item()->type() != Item::FIELD_ITEM) DBUG_RETURN(NULL); } @@ -9276,6 +9286,7 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree) uint best_param_idx= 0; const uint pk= param->table->s->primary_key; + uint max_key_part; SEL_ARG *cur_index_tree= NULL; ha_rows cur_quick_prefix_records= 0; uint cur_param_idx=MAX_KEY; @@ -9329,6 +9340,8 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree) } } + max_key_part= 0; + used_key_parts_map.clear_all(); /* Check (GA1) for GROUP BY queries. */ @@ -9352,6 +9365,8 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree) { cur_group_prefix_len+= cur_part->store_length; ++cur_group_key_parts; + max_key_part= cur_part - cur_index_info->key_part + 1; + used_key_parts_map.set_bit(max_key_part); } else goto next_index; @@ -9365,14 +9380,26 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree) Later group_fields_array of ORDER objects is used to convert the query to a GROUP query. */ - else if (join->select_distinct) + if ((!join->group_list && join->select_distinct) || + is_agg_distinct) { - select_items_it.rewind(); - used_key_parts_map.clear_all(); - uint max_key_part= 0; - while ((item= select_items_it++)) + if (!is_agg_distinct) { - item_field= (Item_field*) item; /* (SA5) already checked above. */ + select_items_it.rewind(); + } + + List_iterator agg_distinct_flds_it (agg_distinct_flds); + while (NULL != (item = (is_agg_distinct ? + (Item *) agg_distinct_flds_it++ : select_items_it++))) + { + /* (SA5) already checked above. */ + item_field= (Item_field*) item->real_item(); + DBUG_ASSERT(item->real_item()->type() == Item::FIELD_ITEM); + + /* not doing loose index scan for derived tables */ + if (!item_field->field) + goto next_index; + /* Find the order of the key part in the index. */ key_part_nr= get_field_keypart(cur_index_info, item_field->field); /* @@ -9381,7 +9408,8 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree) */ if (used_key_parts_map.is_set(key_part_nr)) continue; - if (key_part_nr < 1 || key_part_nr > join->fields_list.elements) + if (key_part_nr < 1 || + (!is_agg_distinct && key_part_nr > join->fields_list.elements)) goto next_index; cur_part= cur_index_info->key_part + key_part_nr - 1; cur_group_prefix_len+= cur_part->store_length; @@ -9401,10 +9429,6 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree) if (all_parts != cur_parts) goto next_index; } - else - { - DBUG_ASSERT(FALSE); - } /* Check (SA2). */ if (min_max_arg_item) @@ -9558,7 +9582,8 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree) /* The query passes all tests, so construct a new TRP object. */ read_plan= new (param->mem_root) - TRP_GROUP_MIN_MAX(have_min, have_max, min_max_arg_part, + TRP_GROUP_MIN_MAX(have_min, have_max, is_agg_distinct, + min_max_arg_part, group_prefix_len, used_key_parts, group_key_parts, index_info, index, key_infix_len, @@ -9572,6 +9597,11 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree) read_plan->read_cost= best_read_cost; read_plan->records= best_records; + if (read_time < best_read_cost && is_agg_distinct) + { + read_plan->read_cost= 0; + read_plan->use_index_scan(); + } DBUG_PRINT("info", ("Returning group min/max plan: cost: %g, records: %lu", @@ -10077,11 +10107,12 @@ TRP_GROUP_MIN_MAX::make_quick(PARAM *param, bool retrieve_full_rows, quick= new QUICK_GROUP_MIN_MAX_SELECT(param->table, param->thd->lex->current_select->join, - have_min, have_max, min_max_arg_part, + have_min, have_max, + have_agg_distinct, min_max_arg_part, group_prefix_len, group_key_parts, used_key_parts, index_info, index, read_cost, records, key_infix_len, - key_infix, parent_alloc); + key_infix, parent_alloc, is_index_scan); if (!quick) DBUG_RETURN(NULL); @@ -10161,6 +10192,9 @@ TRP_GROUP_MIN_MAX::make_quick(PARAM *param, bool retrieve_full_rows, key_infix_len Length of the key infix appended to the group prefix key_infix Infix of constants from equality predicates parent_alloc Memory pool for this and quick_prefix_select data + is_index_scan get the next different key not by jumping on it via + index read, but by scanning until the end of the + rows with equal key value. RETURN None @@ -10168,20 +10202,22 @@ TRP_GROUP_MIN_MAX::make_quick(PARAM *param, bool retrieve_full_rows, QUICK_GROUP_MIN_MAX_SELECT:: QUICK_GROUP_MIN_MAX_SELECT(TABLE *table, JOIN *join_arg, bool have_min_arg, - bool have_max_arg, + bool have_max_arg, bool have_agg_distinct_arg, KEY_PART_INFO *min_max_arg_part_arg, uint group_prefix_len_arg, uint group_key_parts_arg, uint used_key_parts_arg, KEY *index_info_arg, uint use_index, double read_cost_arg, ha_rows records_arg, uint key_infix_len_arg, - uchar *key_infix_arg, MEM_ROOT *parent_alloc) + uchar *key_infix_arg, MEM_ROOT *parent_alloc, + bool is_index_scan_arg) :join(join_arg), index_info(index_info_arg), group_prefix_len(group_prefix_len_arg), group_key_parts(group_key_parts_arg), have_min(have_min_arg), - have_max(have_max_arg), seen_first_key(FALSE), - min_max_arg_part(min_max_arg_part_arg), key_infix(key_infix_arg), - key_infix_len(key_infix_len_arg), min_functions_it(NULL), - max_functions_it(NULL) + have_max(have_max_arg), have_agg_distinct(have_agg_distinct_arg), + seen_first_key(FALSE), min_max_arg_part(min_max_arg_part_arg), + key_infix(key_infix_arg), key_infix_len(key_infix_len_arg), + min_functions_it(NULL), max_functions_it(NULL), + is_index_scan(is_index_scan_arg) { head= table; file= head->file; @@ -10744,6 +10780,56 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_max() } +/** + Find the next different key value by skiping all the rows with the same key + value. + + Implements a specialized loose index access method for queries + containing aggregate functions with distinct of the form: + SELECT [SUM|COUNT|AVG](DISTINCT a,...) FROM t + This method comes to replace the index scan + Unique class + (distinct selection) for loose index scan that visits all the rows of a + covering index instead of jumping in the begining of each group. + TODO: Placeholder function. To be replaced by a handler API call + + @param is_index_scan hint to use index scan instead of random index read + to find the next different value. + @param file table handler + @param key_part group key to compare + @param record row data + @param group_prefix current key prefix data + @param group_prefix_len length of the current key prefix data + @param group_key_parts number of the current key prefix columns + @return status + @retval 0 success + @retval !0 failure +*/ + +static int index_next_different (bool is_index_scan, handler *file, + KEY_PART_INFO *key_part, uchar * record, + const uchar * group_prefix, + uint group_prefix_len, + uint group_key_parts) +{ + if (is_index_scan) + { + int result= 0; + + while (!key_cmp (key_part, group_prefix, group_prefix_len)) + { + result= file->index_next(record); + if (result) + return(result); + } + return result; + } + else + return file->index_read_map(record, group_prefix, + make_prev_keypart_map(group_key_parts), + HA_READ_AFTER_KEY); +} + + /* Determine the prefix of the next group. @@ -10790,9 +10876,9 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_prefix() else { /* Load the first key in this group into record. */ - result= file->index_read_map(record, group_prefix, - make_prev_keypart_map(group_key_parts), - HA_READ_AFTER_KEY); + result= index_next_different (is_index_scan, file, index_info->key_part, + record, group_prefix, group_prefix_len, + group_key_parts); if (result) DBUG_RETURN(result); } diff --git a/sql/opt_range.h b/sql/opt_range.h index 8d2ba1bb0a6..393ffcb2115 100644 --- a/sql/opt_range.h +++ b/sql/opt_range.h @@ -616,6 +616,7 @@ private: uchar *last_prefix; /* Prefix of the last group for detecting EOF. */ bool have_min; /* Specify whether we are computing */ bool have_max; /* a MIN, a MAX, or both. */ + bool have_agg_distinct;/* aggregate_function(DISTINCT ...). */ bool seen_first_key; /* Denotes whether the first key was retrieved.*/ KEY_PART_INFO *min_max_arg_part; /* The keypart of the only argument field */ /* of all MIN/MAX functions. */ @@ -629,6 +630,11 @@ private: List *max_functions; List_iterator *min_functions_it; List_iterator *max_functions_it; + /* + Use index scan to get the next different key instead of jumping into it + through index read + */ + bool is_index_scan; public: /* The following two members are public to allow easy access from @@ -646,12 +652,13 @@ private: void update_max_result(); public: QUICK_GROUP_MIN_MAX_SELECT(TABLE *table, JOIN *join, bool have_min, - bool have_max, KEY_PART_INFO *min_max_arg_part, + bool have_max, bool have_agg_distinct, + KEY_PART_INFO *min_max_arg_part, uint group_prefix_len, uint group_key_parts, uint used_key_parts, KEY *index_info, uint use_index, double read_cost, ha_rows records, uint key_infix_len, uchar *key_infix, MEM_ROOT - *parent_alloc); + *parent_alloc, bool is_index_scan); ~QUICK_GROUP_MIN_MAX_SELECT(); bool add_range(SEL_ARG *sel_range); void update_key_stat(); @@ -667,6 +674,12 @@ public: #ifndef DBUG_OFF void dbug_dump(int indent, bool verbose); #endif + bool is_agg_distinct() { return have_agg_distinct; } + virtual void append_loose_scan_type(String *str) + { + if (is_index_scan) + str->append(STRING_WITH_LEN(" (scanning)")); + } }; diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index 8e7265ba1ad..d995faf1ab6 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -355,10 +355,13 @@ int opt_sum_query(TABLE_LIST *tables, List &all_fields,COND *conds) const_result= 0; break; } + item_sum->set_aggregator (item_sum->with_distinct ? + Aggregator::DISTINCT_AGGREGATOR : + Aggregator::SIMPLE_AGGREGATOR); if (!count) { /* If count == 0, then we know that is_exact_count == TRUE. */ - ((Item_sum_min*) item_sum)->clear(); /* Set to NULL. */ + ((Item_sum_min*) item_sum)->aggregator_clear(); /* Set to NULL. */ } else ((Item_sum_min*) item_sum)->reset(); /* Set to the constant value. */ @@ -443,10 +446,13 @@ int opt_sum_query(TABLE_LIST *tables, List &all_fields,COND *conds) const_result= 0; break; } + item_sum->set_aggregator (item_sum->with_distinct ? + Aggregator::DISTINCT_AGGREGATOR : + Aggregator::SIMPLE_AGGREGATOR); if (!count) { /* If count != 1, then we know that is_exact_count == TRUE. */ - ((Item_sum_max*) item_sum)->clear(); /* Set to NULL. */ + ((Item_sum_max*) item_sum)->aggregator_clear(); /* Set to NULL. */ } else ((Item_sum_max*) item_sum)->reset(); /* Set to the constant value. */ diff --git a/sql/sql_class.h b/sql/sql_class.h index e845b5a727c..4874801e380 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -54,6 +54,7 @@ class Reprepare_observer { public: + Reprepare_observer() {} /** Check if a change of metadata is OK. In future the signature of this method may be extended to accept the old diff --git a/sql/sql_select.cc b/sql/sql_select.cc index db3a73aec74..3e2d85e4951 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -222,6 +222,7 @@ static void update_tmptable_sum_func(Item_sum **func,TABLE *tmp_table); static void copy_sum_funcs(Item_sum **func_ptr, Item_sum **end); static bool add_ref_to_table_cond(THD *thd, JOIN_TAB *join_tab); static bool setup_sum_funcs(THD *thd, Item_sum **func_ptr); +static bool prepare_sum_aggregators(Item_sum **func_ptr, bool need_distinct); static bool init_sum_functions(Item_sum **func, Item_sum **end); static bool update_sum_func(Item_sum **func); static void select_describe(JOIN *join, bool need_tmp_table,bool need_order, @@ -1232,7 +1233,11 @@ JOIN::optimize() if (test_if_subpart(group_list, order) || (!group_list && tmp_table_param.sum_func_count)) + { order=0; + if (is_indexed_agg_distinct(this, NULL)) + sort_and_group= 0; + } // Can't use sort on head table if using row cache if (full_join) @@ -1410,8 +1415,16 @@ JOIN::optimize() single table queries, thus it is sufficient to test only the first join_tab element of the plan for its access method. */ + bool need_distinct= TRUE; if (join_tab->is_using_loose_index_scan()) + { tmp_table_param.precomputed_group_by= TRUE; + if (join_tab->is_using_agg_loose_index_scan()) + { + need_distinct= FALSE; + tmp_table_param.precomputed_group_by= FALSE; + } + } /* Create a tmp table if distinct or if the sort is too complicated */ if (need_tmp) @@ -1472,6 +1485,7 @@ JOIN::optimize() HA_POS_ERROR, HA_POS_ERROR, FALSE) || alloc_group_fields(this, group_list) || make_sum_func_list(all_fields, fields_list, 1) || + prepare_sum_aggregators(sum_funcs, need_distinct) || setup_sum_funcs(thd, sum_funcs)) { DBUG_RETURN(1); @@ -1481,6 +1495,7 @@ JOIN::optimize() else { if (make_sum_func_list(all_fields, fields_list, 0) || + prepare_sum_aggregators(sum_funcs, need_distinct) || setup_sum_funcs(thd, sum_funcs)) { DBUG_RETURN(1); @@ -1953,7 +1968,9 @@ JOIN::exec() } } if (curr_join->make_sum_func_list(*curr_all_fields, *curr_fields_list, - 1, TRUE)) + 1, TRUE) || + prepare_sum_aggregators(curr_join->sum_funcs, + !curr_join->join_tab->is_using_agg_loose_index_scan())) DBUG_VOID_RETURN; curr_join->group_list= 0; if (!curr_join->sort_and_group && @@ -2056,6 +2073,8 @@ JOIN::exec() if (curr_join->make_sum_func_list(*curr_all_fields, *curr_fields_list, 1, TRUE) || + prepare_sum_aggregators (curr_join->sum_funcs, !curr_join->join_tab || + !curr_join->join_tab->is_using_agg_loose_index_scan()) || setup_sum_funcs(curr_join->thd, curr_join->sum_funcs) || thd->is_fatal_error) DBUG_VOID_RETURN; @@ -3937,6 +3956,82 @@ static void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array) } +/** + Check for the presence of AGGFN(DISTINCT a) queries that may be subject + to loose index scan. + + + Check if the query is a subject to AGGFN(DISTINCT) using loose index scan + (QUICK_GROUP_MIN_MAX_SELECT). + Optionally (if out_args is supplied) will push the arguments of + AGGFN(DISTINCT) to the list + + @param join the join to check + @param[out] out_args list of aggregate function arguments + @return does the query qualify for indexed AGGFN(DISTINCT) + @retval true it does + @retval false AGGFN(DISTINCT) must apply distinct in it. +*/ + +bool +is_indexed_agg_distinct(JOIN *join, List *out_args) +{ + Item_sum **sum_item_ptr; + bool result= false; + + if (join->tables != 1 || /* reference more than 1 table */ + join->select_distinct || /* or a DISTINCT */ + join->select_lex->olap == ROLLUP_TYPE) /* Check (B3) for ROLLUP */ + return false; + + if (join->make_sum_func_list(join->all_fields, join->fields_list, 1)) + return false; + + for (sum_item_ptr= join->sum_funcs; *sum_item_ptr; sum_item_ptr++) + { + Item_sum *sum_item= *sum_item_ptr; + Item *expr; + /* aggregate is not AGGFN(DISTINCT) or more than 1 argument to it */ + switch (sum_item->sum_func()) + { + case Item_sum::MIN_FUNC: + case Item_sum::MAX_FUNC: + continue; + case Item_sum::COUNT_DISTINCT_FUNC: + break; + case Item_sum::AVG_DISTINCT_FUNC: + case Item_sum::SUM_DISTINCT_FUNC: + if (sum_item->get_arg_count() == 1) + break; + /* fall through */ + default: return false; + } + /* + We arrive here for every COUNT(DISTINCT),AVG(DISTINCT) or SUM(DISTINCT). + Collect the arguments of the aggregate functions to a list. + We don't worry about duplicates as these will be sorted out later in + get_best_group_min_max + */ + for (uint i= 0; i < sum_item->get_arg_count(); i++) + { + expr= sum_item->get_arg(i); + /* The AGGFN(DISTINCT) arg is not an attribute? */ + if (expr->real_item()->type() != Item::FIELD_ITEM) + return false; + + /* + If we came to this point the AGGFN(DISTINCT) loose index scan + optimization is applicable + */ + if (out_args) + out_args->push_back((Item_field *) expr); + result= true; + } + } + return result; +} + + /** Discover the indexes that can be used for GROUP BY or DISTINCT queries. @@ -3979,6 +4074,10 @@ add_group_and_distinct_keys(JOIN *join, JOIN_TAB *join_tab) item->walk(&Item::collect_item_field_processor, 0, (uchar*) &indexed_fields); } + else if (is_indexed_agg_distinct(join, &indexed_fields)) + { + join->sort_and_group= 1; + } else return; @@ -10377,6 +10476,7 @@ TABLE *create_virtual_tmp_table(THD *thd, List &field_list) bzero(share, sizeof(*share)); table->field= field; table->s= share; + table->temp_pool_slot= MY_BIT_NONE; share->blob_field= blob_field; share->fields= field_count; share->blob_ptr_size= portable_sizeof_char_ptr; @@ -14532,7 +14632,7 @@ setup_new_fields(THD *thd, List &fields, optimize away 'order by'. */ -static ORDER * +ORDER * create_distinct_group(THD *thd, Item **ref_pointer_array, ORDER *order_list, List &fields, List &all_fields, @@ -15334,7 +15434,22 @@ static bool setup_sum_funcs(THD *thd, Item_sum **func_ptr) DBUG_ENTER("setup_sum_funcs"); while ((func= *(func_ptr++))) { - if (func->setup(thd)) + if (func->aggregator_setup(thd)) + DBUG_RETURN(TRUE); + } + DBUG_RETURN(FALSE); +} + + +static bool prepare_sum_aggregators(Item_sum **func_ptr, bool need_distinct) +{ + Item_sum *func; + DBUG_ENTER("setup_sum_funcs"); + while ((func= *(func_ptr++))) + { + if (func->set_aggregator(need_distinct && func->with_distinct ? + Aggregator::DISTINCT_AGGREGATOR : + Aggregator::SIMPLE_AGGREGATOR)) DBUG_RETURN(TRUE); } DBUG_RETURN(FALSE); @@ -15384,7 +15499,7 @@ init_sum_functions(Item_sum **func_ptr, Item_sum **end_ptr) /* If rollup, calculate the upper sum levels */ for ( ; *func_ptr ; func_ptr++) { - if ((*func_ptr)->add()) + if ((*func_ptr)->aggregator_add()) return 1; } return 0; @@ -15396,7 +15511,7 @@ update_sum_func(Item_sum **func_ptr) { Item_sum *func; for (; (func= (Item_sum*) *func_ptr) ; func_ptr++) - if (func->add()) + if (func->aggregator_add()) return 1; return 0; } @@ -16313,7 +16428,12 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, if (key_read) { if (quick_type == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX) + { + QUICK_GROUP_MIN_MAX_SELECT *qgs= + (QUICK_GROUP_MIN_MAX_SELECT *) tab->select->quick; extra.append(STRING_WITH_LEN("; Using index for group-by")); + qgs->append_loose_scan_type(&extra); + } else extra.append(STRING_WITH_LEN("; Using index")); } diff --git a/sql/sql_select.h b/sql/sql_select.h index a0366d47149..bb751c600ed 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -218,6 +218,11 @@ typedef struct st_join_table { (select->quick->get_type() == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX)); } + bool is_using_agg_loose_index_scan () + { + return (is_using_loose_index_scan() && + ((QUICK_GROUP_MIN_MAX_SELECT *)select->quick)->is_agg_distinct()); + } } JOIN_TAB; enum_nested_loop_state sub_select_cache(JOIN *join, JOIN_TAB *join_tab, bool @@ -564,6 +569,8 @@ Field* create_tmp_field_from_field(THD *thd, Field* org_field, const char *name, TABLE *table, Item_field *item, uint convert_blob_length); +bool is_indexed_agg_distinct(JOIN *join, List *out_args); + /* functions from opt_sum.cc */ bool simple_pred(Item_func *func_item, Item **args, bool *inv_order); int opt_sum_query(TABLE_LIST *tables, List &all_fields,COND *conds); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 329158b7750..e7193d76d4e 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -8128,7 +8128,7 @@ sum_expr: } | AVG_SYM '(' DISTINCT in_sum_expr ')' { - $$= new (YYTHD->mem_root) Item_sum_avg_distinct($4); + $$= new (YYTHD->mem_root) Item_sum_avg($4, TRUE); if ($$ == NULL) MYSQL_YYABORT; } @@ -8171,7 +8171,7 @@ sum_expr: { Select->in_sum_expr--; } ')' { - $$= new (YYTHD->mem_root) Item_sum_count_distinct(* $5); + $$= new (YYTHD->mem_root) Item_sum_count(* $5); if ($$ == NULL) MYSQL_YYABORT; } @@ -8236,7 +8236,7 @@ sum_expr: } | SUM_SYM '(' DISTINCT in_sum_expr ')' { - $$= new (YYTHD->mem_root) Item_sum_sum_distinct($4); + $$= new (YYTHD->mem_root) Item_sum_sum($4, TRUE); if ($$ == NULL) MYSQL_YYABORT; } From 1ab2577461b48be8df9f36275c116bcb4fa9d245 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Wed, 30 Sep 2009 03:21:00 +0200 Subject: [PATCH 4/9] fix tree name --- .bzr-mysql/default.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.bzr-mysql/default.conf b/.bzr-mysql/default.conf index 771201a109b..d771989c69a 100644 --- a/.bzr-mysql/default.conf +++ b/.bzr-mysql/default.conf @@ -1,4 +1,4 @@ [MYSQL] post_commit_to = "commits@lists.mysql.com" post_push_to = "commits@lists.mysql.com" -tree_name = "mysql-5.4.5-next-mr" +tree_name = "mysql-next-mr-wtf" From e6090f10bb581e8f16f8ecb676a27e184548ef24 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Wed, 30 Sep 2009 03:22:57 +0200 Subject: [PATCH 5/9] Windows improvements : manual backport of htttp://lists.mysql.com/commits/50957?f=plain Always use TLS functions instead of __declspec(thread) to access thread local storage variables. The change removes the necessity to recomplile the same source files twice - with USE_TLS for DLLs and without USE_TLS for EXEs. Real benefit of this change is better readability and maintainability of TLS functions within MySQL. There is a performance loss using TlsXXX functions compared to __declspec but the difference is negligible in practice. In a sysbench-like benchmark I ran with with TlsGetValue, pthread_[get|set]_specific was called 600000000 times and took 0.17sec of total 35min CPU time, or 0.008%. --- client/CMakeLists.txt | 18 +++++++++--------- include/my_pthread.h | 24 +++--------------------- libmysql/CMakeLists.txt | 18 +----------------- libmysqld/CMakeLists.txt | 6 ------ libmysqld/examples/CMakeLists.txt | 3 --- mysys/my_thr_init.c | 21 --------------------- mysys/my_winthread.c | 7 ------- tests/CMakeLists.txt | 5 ++--- 8 files changed, 15 insertions(+), 87 deletions(-) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index e96437d40d0..4fadc4e7ae5 100755 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -14,7 +14,7 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA INCLUDE("${PROJECT_SOURCE_DIR}/win/mysql_manifest.cmake") -# We use the "mysqlclient_notls" library here just as safety, in case +# We use the "mysqlclient" library here just as safety, in case # any of the clients here would go beyond the client API and access the # Thread Local Storage directly. @@ -30,27 +30,27 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/strings) ADD_EXECUTABLE(mysql completion_hash.cc mysql.cc readline.cc sql_string.cc ../mysys/my_conio.c) -TARGET_LINK_LIBRARIES(mysql mysqlclient_notls wsock32) +TARGET_LINK_LIBRARIES(mysql mysqlclient wsock32) ADD_EXECUTABLE(mysqltest mysqltest.cc) SET_SOURCE_FILES_PROPERTIES(mysqltest.cc PROPERTIES COMPILE_FLAGS "-DTHREADS") TARGET_LINK_LIBRARIES(mysqltest mysqlclient mysys regex wsock32 dbug) ADD_EXECUTABLE(mysqlcheck mysqlcheck.c) -TARGET_LINK_LIBRARIES(mysqlcheck mysqlclient_notls wsock32) +TARGET_LINK_LIBRARIES(mysqlcheck mysqlclient wsock32) ADD_EXECUTABLE(mysqldump mysqldump.c ../sql-common/my_user.c ../mysys/mf_getdate.c) -TARGET_LINK_LIBRARIES(mysqldump mysqlclient_notls wsock32) +TARGET_LINK_LIBRARIES(mysqldump mysqlclient wsock32) ADD_EXECUTABLE(mysqlimport mysqlimport.c) -TARGET_LINK_LIBRARIES(mysqlimport mysqlclient_notls wsock32) +TARGET_LINK_LIBRARIES(mysqlimport mysqlclient wsock32) ADD_EXECUTABLE(mysql_upgrade mysql_upgrade.c ../mysys/my_getpagesize.c) -TARGET_LINK_LIBRARIES(mysql_upgrade mysqlclient_notls wsock32) +TARGET_LINK_LIBRARIES(mysql_upgrade mysqlclient wsock32) ADD_DEPENDENCIES(mysql_upgrade GenFixPrivs) ADD_EXECUTABLE(mysqlshow mysqlshow.c) -TARGET_LINK_LIBRARIES(mysqlshow mysqlclient_notls wsock32) +TARGET_LINK_LIBRARIES(mysqlshow mysqlclient wsock32) ADD_EXECUTABLE(mysqlbinlog mysqlbinlog.cc ../mysys/mf_tempdir.c @@ -59,10 +59,10 @@ ADD_EXECUTABLE(mysqlbinlog mysqlbinlog.cc ../mysys/my_bitmap.c ../mysys/my_vle.c ../mysys/base64.c) -TARGET_LINK_LIBRARIES(mysqlbinlog mysqlclient_notls wsock32) +TARGET_LINK_LIBRARIES(mysqlbinlog mysqlclient wsock32) ADD_EXECUTABLE(mysqladmin mysqladmin.cc) -TARGET_LINK_LIBRARIES(mysqladmin mysqlclient_notls wsock32) +TARGET_LINK_LIBRARIES(mysqladmin mysqlclient wsock32) ADD_EXECUTABLE(mysqlslap mysqlslap.c) SET_SOURCE_FILES_PROPERTIES(mysqlslap.c PROPERTIES COMPILE_FLAGS "-DTHREADS") diff --git a/include/my_pthread.h b/include/my_pthread.h index 9e2c2111b8e..2928cb60c2d 100644 --- a/include/my_pthread.h +++ b/include/my_pthread.h @@ -100,7 +100,6 @@ struct timespec { } void win_pthread_init(void); -int win_pthread_setspecific(void *A,void *B,uint length); int win_pthread_mutex_trylock(pthread_mutex_t *mutex); int pthread_create(pthread_t *,pthread_attr_t *,pthread_handler,void *); int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr); @@ -126,33 +125,16 @@ void pthread_exit(void *a); /* was #define pthread_exit(A) ExitThread(A)*/ #define _REENTRANT 1 #define HAVE_PTHREAD_ATTR_SETSTACKSIZE 1 -/* - Windows has two ways to use thread local storage. The most efficient - is using __declspec(thread), but that does not work properly when - used in a .dll that is loaded at runtime, after program load. So for - libmysql.dll and libmysqld.dll we define USE_TLS in order to use the - TlsXxx() API instead, which works in all cases. -*/ -#ifdef USE_TLS /* For LIBMYSQL.DLL */ + #undef SAFE_MUTEX /* This will cause conflicts */ #define pthread_key(T,V) DWORD V #define pthread_key_create(A,B) ((*A=TlsAlloc())==0xFFFFFFFF) #define pthread_key_delete(A) TlsFree(A) +#define my_pthread_setspecific_ptr(T,V) (!TlsSetValue((T),(V))) +#define pthread_setspecific(A,B) (!TlsSetValue((A),(B))) #define pthread_getspecific(A) (TlsGetValue(A)) #define my_pthread_getspecific(T,A) ((T) TlsGetValue(A)) #define my_pthread_getspecific_ptr(T,V) ((T) TlsGetValue(V)) -#define my_pthread_setspecific_ptr(T,V) (!TlsSetValue((T),(V))) -#define pthread_setspecific(A,B) (!TlsSetValue((A),(B))) -#else -#define pthread_key(T,V) __declspec(thread) T V -#define pthread_key_create(A,B) pthread_dummy(0) -#define pthread_key_delete(A) pthread_dummy(0) -#define pthread_getspecific(A) (&(A)) -#define my_pthread_getspecific(T,A) (&(A)) -#define my_pthread_getspecific_ptr(T,V) (V) -#define my_pthread_setspecific_ptr(T,V) ((T)=(V),0) -#define pthread_setspecific(A,B) win_pthread_setspecific(&(A),(B),sizeof(A)) -#endif /* USE_TLS */ #define pthread_equal(A,B) ((A) == (B)) #define pthread_mutex_init(A,B) (InitializeCriticalSection(A),0) diff --git a/libmysql/CMakeLists.txt b/libmysql/CMakeLists.txt index b252d94b50e..06c17c80bfe 100755 --- a/libmysql/CMakeLists.txt +++ b/libmysql/CMakeLists.txt @@ -100,29 +100,13 @@ SET(CLIENT_SOURCES ../mysys/array.c ../strings/bchange.c ../strings/bmove.c ../vio/viossl.c ../vio/viosslfactories.c ../strings/xml.c ../mysys/mf_qsort.c ../mysys/my_getsystime.c ../mysys/my_sync.c ../mysys/my_winerr.c ../mysys/my_winfile.c ${LIB_SOURCES}) -# Need to set USE_TLS for building the DLL, since __declspec(thread) -# approach to thread local storage does not work properly in DLLs. -# -# The static library might be used to form another DLL, as is the case -# with the ODBC driver, so it has to be compiled with USE_TLS as well. -# -# We create a third library without USE_TLS for internal use. We can't -# be sure that some client application part of this build doesn't go -# beond the documented API, and try access the Thread Local Storage. -# The "_notls" means no Tls*() functions used, i.e. "static" TLS. + ADD_LIBRARY(mysqlclient STATIC ${CLIENT_SOURCES}) ADD_DEPENDENCIES(mysqlclient GenError) TARGET_LINK_LIBRARIES(mysqlclient) -ADD_LIBRARY(mysqlclient_notls STATIC ${CLIENT_SOURCES}) -ADD_DEPENDENCIES(mysqlclient_notls GenError) -TARGET_LINK_LIBRARIES(mysqlclient_notls) - ADD_LIBRARY(libmysql SHARED ${CLIENT_SOURCES} dll.c libmysql.def) -IF(WIN32) - SET_TARGET_PROPERTIES(libmysql mysqlclient PROPERTIES COMPILE_FLAGS "-DUSE_TLS") -ENDIF(WIN32) ADD_DEPENDENCIES(libmysql GenError) TARGET_LINK_LIBRARIES(libmysql wsock32) diff --git a/libmysqld/CMakeLists.txt b/libmysqld/CMakeLists.txt index b31082b438a..f180c6e1921 100644 --- a/libmysqld/CMakeLists.txt +++ b/libmysqld/CMakeLists.txt @@ -16,12 +16,6 @@ SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") -# Need to set USE_TLS, since __declspec(thread) approach to thread local -# storage does not work properly in DLLs. -IF(WIN32) - ADD_DEFINITIONS(-DUSE_TLS) -ENDIF(WIN32) - ADD_DEFINITIONS(-DMYSQL_SERVER -DEMBEDDED_LIBRARY -DHAVE_DLOPEN) INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include diff --git a/libmysqld/examples/CMakeLists.txt b/libmysqld/examples/CMakeLists.txt index 5194836a728..e4b6533f8a2 100644 --- a/libmysqld/examples/CMakeLists.txt +++ b/libmysqld/examples/CMakeLists.txt @@ -20,9 +20,6 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/extra/yassl/include) # Currently does not work with DBUG, there are missing symbols reported. -IF(WIN32) - ADD_DEFINITIONS(-DUSE_TLS) -ENDIF(WIN32) ADD_DEFINITIONS(-DEMBEDDED_LIBRARY) diff --git a/mysys/my_thr_init.c b/mysys/my_thr_init.c index 2278c467f32..2c346145cf5 100644 --- a/mysys/my_thr_init.c +++ b/mysys/my_thr_init.c @@ -23,11 +23,7 @@ #include #ifdef THREAD -#ifdef USE_TLS pthread_key(struct st_my_thread_var*, THR_KEY_mysys); -#else -pthread_key(struct st_my_thread_var, THR_KEY_mysys); -#endif /* USE_TLS */ pthread_mutex_t THR_LOCK_malloc,THR_LOCK_open, THR_LOCK_lock,THR_LOCK_isam,THR_LOCK_myisam,THR_LOCK_heap, THR_LOCK_net, THR_LOCK_charset, THR_LOCK_threads, THR_LOCK_time; @@ -258,7 +254,6 @@ my_bool my_thread_init(void) (ulong) pthread_self()); #endif -#if !defined(__WIN__) || defined(USE_TLS) if (my_pthread_getspecific(struct st_my_thread_var *,THR_KEY_mysys)) { #ifdef EXTRA_DEBUG_THREADS @@ -273,15 +268,6 @@ my_bool my_thread_init(void) goto end; } pthread_setspecific(THR_KEY_mysys,tmp); - -#else /* defined(__WIN__) && !(defined(USE_TLS) */ - /* - Skip initialization if the thread specific variable is already initialized - */ - if (THR_KEY_mysys.id) - goto end; - tmp= &THR_KEY_mysys; -#endif #if defined(__WIN__) && defined(EMBEDDED_LIBRARY) tmp->pthread_self= (pthread_t) getpid(); #else @@ -342,11 +328,7 @@ void my_thread_end(void) pthread_cond_destroy(&tmp->suspend); #endif pthread_mutex_destroy(&tmp->mutex); -#if !defined(__WIN__) || defined(USE_TLS) free(tmp); -#else - tmp->init= 0; -#endif /* Decrement counter for number of running threads. We are using this @@ -360,10 +342,7 @@ void my_thread_end(void) pthread_cond_signal(&THR_COND_threads); pthread_mutex_unlock(&THR_LOCK_threads); } - /* The following free has to be done, even if my_thread_var() is 0 */ -#if !defined(__WIN__) || defined(USE_TLS) pthread_setspecific(THR_KEY_mysys,0); -#endif } struct st_my_thread_var *_my_thread_var(void) diff --git a/mysys/my_winthread.c b/mysys/my_winthread.c index e94369bec32..543e1787fb6 100644 --- a/mysys/my_winthread.c +++ b/mysys/my_winthread.c @@ -129,12 +129,5 @@ void pthread_exit(void *a) _endthread(); } -/* This is neaded to get the macro pthread_setspecific to work */ - -int win_pthread_setspecific(void *a,void *b,uint length) -{ - memcpy(a,b,length); - return 0; -} #endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 2093fc0da36..9ddfadcda4e 100755 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -13,7 +13,6 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -# About "mysqlclient_notls", see note in "client/CMakeLists.txt" SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") ADD_DEFINITIONS("-DMYSQL_CLIENT") @@ -21,7 +20,7 @@ ADD_DEFINITIONS("-DMYSQL_CLIENT") INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include) ADD_EXECUTABLE(mysql_client_test mysql_client_test.c ../mysys/my_memmem.c) -TARGET_LINK_LIBRARIES(mysql_client_test mysqlclient_notls wsock32) +TARGET_LINK_LIBRARIES(mysql_client_test mysqlclient wsock32) ADD_EXECUTABLE(bug25714 bug25714.c) -TARGET_LINK_LIBRARIES(bug25714 mysqlclient_notls wsock32) +TARGET_LINK_LIBRARIES(bug25714 mysqlclient wsock32) From 46d04ebdefe05377b86e851d73df22c069d63246 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Wed, 30 Sep 2009 03:39:37 +0200 Subject: [PATCH 6/9] Backport of the patch http://lists.mysql.com/commits/57725 Vladislav Vaintroub 2008-11-03 Cleanup CMakeLists.txt(s) - remove winsock2 (ws2_32) from TARGET_LINK_LIBRARIES. Every exe or dll linked with mysys needs ws2_32, because mysys uses winsock function WSAStartup in my_init(). However, there is no need to explicitely add ws2_32 to the list of TARGET_LINK_LIBRARIES multiple times. Visual Studio comes with a handy pragma that tells linker to add library. So patch replaces bunch of ws2_32 in CMakeLists with single pragma comment(lib,"ws2_32") in my_init.c Additionally, reference to non-existing "debug" library has been removed from TARGET_LINK_LIBRARIES. The correct name of the library is "dbug". --- client/CMakeLists.txt | 20 ++++++++++---------- extra/CMakeLists.txt | 10 +++++----- libmysql/CMakeLists.txt | 2 +- mysys/my_init.c | 2 ++ scripts/CMakeLists.txt | 2 +- sql/CMakeLists.txt | 5 ++--- sql/udf_example.c | 5 +++++ storage/myisam/CMakeLists.txt | 8 ++++---- tests/CMakeLists.txt | 4 ++-- 9 files changed, 32 insertions(+), 26 deletions(-) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 4fadc4e7ae5..176613e2dc8 100755 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -30,27 +30,27 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/strings) ADD_EXECUTABLE(mysql completion_hash.cc mysql.cc readline.cc sql_string.cc ../mysys/my_conio.c) -TARGET_LINK_LIBRARIES(mysql mysqlclient wsock32) +TARGET_LINK_LIBRARIES(mysql mysqlclient) ADD_EXECUTABLE(mysqltest mysqltest.cc) SET_SOURCE_FILES_PROPERTIES(mysqltest.cc PROPERTIES COMPILE_FLAGS "-DTHREADS") -TARGET_LINK_LIBRARIES(mysqltest mysqlclient mysys regex wsock32 dbug) +TARGET_LINK_LIBRARIES(mysqltest mysqlclient mysys regex dbug) ADD_EXECUTABLE(mysqlcheck mysqlcheck.c) -TARGET_LINK_LIBRARIES(mysqlcheck mysqlclient wsock32) +TARGET_LINK_LIBRARIES(mysqlcheck mysqlclient) ADD_EXECUTABLE(mysqldump mysqldump.c ../sql-common/my_user.c ../mysys/mf_getdate.c) -TARGET_LINK_LIBRARIES(mysqldump mysqlclient wsock32) +TARGET_LINK_LIBRARIES(mysqldump mysqlclient) ADD_EXECUTABLE(mysqlimport mysqlimport.c) -TARGET_LINK_LIBRARIES(mysqlimport mysqlclient wsock32) +TARGET_LINK_LIBRARIES(mysqlimport mysqlclient) ADD_EXECUTABLE(mysql_upgrade mysql_upgrade.c ../mysys/my_getpagesize.c) -TARGET_LINK_LIBRARIES(mysql_upgrade mysqlclient wsock32) +TARGET_LINK_LIBRARIES(mysql_upgrade mysqlclient) ADD_DEPENDENCIES(mysql_upgrade GenFixPrivs) ADD_EXECUTABLE(mysqlshow mysqlshow.c) -TARGET_LINK_LIBRARIES(mysqlshow mysqlclient wsock32) +TARGET_LINK_LIBRARIES(mysqlshow mysqlclient) ADD_EXECUTABLE(mysqlbinlog mysqlbinlog.cc ../mysys/mf_tempdir.c @@ -59,14 +59,14 @@ ADD_EXECUTABLE(mysqlbinlog mysqlbinlog.cc ../mysys/my_bitmap.c ../mysys/my_vle.c ../mysys/base64.c) -TARGET_LINK_LIBRARIES(mysqlbinlog mysqlclient wsock32) +TARGET_LINK_LIBRARIES(mysqlbinlog mysqlclient) ADD_EXECUTABLE(mysqladmin mysqladmin.cc) -TARGET_LINK_LIBRARIES(mysqladmin mysqlclient wsock32) +TARGET_LINK_LIBRARIES(mysqladmin mysqlclient) ADD_EXECUTABLE(mysqlslap mysqlslap.c) SET_SOURCE_FILES_PROPERTIES(mysqlslap.c PROPERTIES COMPILE_FLAGS "-DTHREADS") -TARGET_LINK_LIBRARIES(mysqlslap mysqlclient mysys zlib wsock32 dbug) +TARGET_LINK_LIBRARIES(mysqlslap mysqlclient mysys zlib dbug) ADD_EXECUTABLE(echo echo.c) diff --git a/extra/CMakeLists.txt b/extra/CMakeLists.txt index cec0db6a4ae..0d09e676af1 100755 --- a/extra/CMakeLists.txt +++ b/extra/CMakeLists.txt @@ -20,7 +20,7 @@ SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include) ADD_EXECUTABLE(comp_err comp_err.c) -TARGET_LINK_LIBRARIES(comp_err debug dbug mysys strings zlib wsock32) +TARGET_LINK_LIBRARIES(comp_err dbug mysys strings zlib) GET_TARGET_PROPERTY(COMP_ERR_EXE comp_err LOCATION) @@ -39,16 +39,16 @@ ADD_CUSTOM_TARGET(GenError DEPENDS ${PROJECT_SOURCE_DIR}/include/mysqld_error.h) ADD_EXECUTABLE(my_print_defaults my_print_defaults.c) -TARGET_LINK_LIBRARIES(my_print_defaults strings mysys debug dbug taocrypt wsock32) +TARGET_LINK_LIBRARIES(my_print_defaults strings mysys dbug taocrypt) ADD_EXECUTABLE(perror perror.c) -TARGET_LINK_LIBRARIES(perror strings mysys debug dbug wsock32) +TARGET_LINK_LIBRARIES(perror strings mysys dbug) ADD_EXECUTABLE(resolveip resolveip.c) -TARGET_LINK_LIBRARIES(resolveip strings mysys debug dbug wsock32) +TARGET_LINK_LIBRARIES(resolveip strings mysys dbug) ADD_EXECUTABLE(replace replace.c) -TARGET_LINK_LIBRARIES(replace strings mysys debug dbug wsock32) +TARGET_LINK_LIBRARIES(replace strings mysys dbug) IF(EMBED_MANIFESTS) MYSQL_EMBED_MANIFEST("myTest" "asInvoker") diff --git a/libmysql/CMakeLists.txt b/libmysql/CMakeLists.txt index 06c17c80bfe..29fe4ca33c3 100755 --- a/libmysql/CMakeLists.txt +++ b/libmysql/CMakeLists.txt @@ -108,7 +108,7 @@ TARGET_LINK_LIBRARIES(mysqlclient) ADD_LIBRARY(libmysql SHARED ${CLIENT_SOURCES} dll.c libmysql.def) ADD_DEPENDENCIES(libmysql GenError) -TARGET_LINK_LIBRARIES(libmysql wsock32) +TARGET_LINK_LIBRARIES(libmysql) IF(EMBED_MANIFESTS) MYSQL_EMBED_MANIFEST("myTest" "asInvoker") diff --git a/mysys/my_init.c b/mysys/my_init.c index a60927be693..c4fda599481 100644 --- a/mysys/my_init.c +++ b/mysys/my_init.c @@ -27,6 +27,8 @@ #ifdef _MSC_VER #include #include +/* WSAStartup needs winsock library*/ +#pragma comment(lib, "ws2_32") #endif my_bool have_tcpip=0; static void my_win_init(void); diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt index f0db25be79a..25cdae2b522 100755 --- a/scripts/CMakeLists.txt +++ b/scripts/CMakeLists.txt @@ -24,7 +24,7 @@ ADD_CUSTOM_COMMAND(OUTPUT ${PROJECT_SOURCE_DIR}/scripts/mysql_fix_privilege_tabl # Build comp_sql - used for embedding SQL in C or C++ programs ADD_EXECUTABLE(comp_sql comp_sql.c) -TARGET_LINK_LIBRARIES(comp_sql debug dbug mysys strings) +TARGET_LINK_LIBRARIES(comp_sql dbug mysys strings) # Use comp_sql to build mysql_fix_privilege_tables_sql.c GET_TARGET_PROPERTY(COMP_SQL_EXE comp_sql LOCATION) diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 592092ba7aa..1b7175a7b82 100755 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -96,7 +96,6 @@ SET_TARGET_PROPERTIES(mysqld PROPERTIES ENABLE_EXPORTS TRUE) SET (MYSQLD_CORE_LIBS mysys zlib dbug strings yassl taocrypt vio regex sql) TARGET_LINK_LIBRARIES(mysqld ${MYSQLD_CORE_LIBS} ${MYSQLD_STATIC_ENGINE_LIBS}) -TARGET_LINK_LIBRARIES(mysqld ws2_32.lib) IF(MSVC AND NOT WITHOUT_DYNAMIC_PLUGINS) @@ -129,7 +128,7 @@ ADD_CUSTOM_COMMAND( # Gen_lex_hash ADD_EXECUTABLE(gen_lex_hash gen_lex_hash.cc) -TARGET_LINK_LIBRARIES(gen_lex_hash debug dbug mysqlclient wsock32) +TARGET_LINK_LIBRARIES(gen_lex_hash dbug mysqlclient) GET_TARGET_PROPERTY(GEN_LEX_HASH_EXE gen_lex_hash LOCATION) ADD_CUSTOM_COMMAND( OUTPUT ${PROJECT_SOURCE_DIR}/sql/lex_hash.h @@ -152,4 +151,4 @@ SET_DIRECTORY_PROPERTIES(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES ADD_LIBRARY(udf_example MODULE udf_example.c udf_example.def) ADD_DEPENDENCIES(udf_example strings GenError) -TARGET_LINK_LIBRARIES(udf_example strings wsock32) +TARGET_LINK_LIBRARIES(udf_example strings) diff --git a/sql/udf_example.c b/sql/udf_example.c index 30d85d95034..1d6a9828594 100644 --- a/sql/udf_example.c +++ b/sql/udf_example.c @@ -138,6 +138,11 @@ typedef long long longlong; #endif #include #include + +#ifdef _WIN32 +/* inet_aton needs winsock library */ +#pragma comment(lib, "ws2_32") +#endif static pthread_mutex_t LOCK_hostname; diff --git a/storage/myisam/CMakeLists.txt b/storage/myisam/CMakeLists.txt index c05e0046e64..0f070510e5c 100755 --- a/storage/myisam/CMakeLists.txt +++ b/storage/myisam/CMakeLists.txt @@ -34,16 +34,16 @@ MYSQL_STORAGE_ENGINE(MYISAM) IF(NOT SOURCE_SUBLIBS) ADD_EXECUTABLE(myisam_ftdump myisam_ftdump.c) - TARGET_LINK_LIBRARIES(myisam_ftdump myisam mysys debug dbug strings zlib wsock32) + TARGET_LINK_LIBRARIES(myisam_ftdump myisam mysys dbug strings zlib) ADD_EXECUTABLE(myisamchk myisamchk.c) - TARGET_LINK_LIBRARIES(myisamchk myisam mysys debug dbug strings zlib wsock32) + TARGET_LINK_LIBRARIES(myisamchk myisam mysys dbug strings zlib) ADD_EXECUTABLE(myisamlog myisamlog.c) - TARGET_LINK_LIBRARIES(myisamlog myisam mysys debug dbug strings zlib wsock32) + TARGET_LINK_LIBRARIES(myisamlog myisam mysys dbug strings zlib) ADD_EXECUTABLE(myisampack myisampack.c) - TARGET_LINK_LIBRARIES(myisampack myisam mysys debug dbug strings zlib wsock32) + TARGET_LINK_LIBRARIES(myisampack myisam mysys dbug strings zlib) SET_TARGET_PROPERTIES(myisamchk myisampack PROPERTIES LINK_FLAGS "setargv.obj") diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9ddfadcda4e..270aaf53c20 100755 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -20,7 +20,7 @@ ADD_DEFINITIONS("-DMYSQL_CLIENT") INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include) ADD_EXECUTABLE(mysql_client_test mysql_client_test.c ../mysys/my_memmem.c) -TARGET_LINK_LIBRARIES(mysql_client_test mysqlclient wsock32) +TARGET_LINK_LIBRARIES(mysql_client_test mysqlclient) ADD_EXECUTABLE(bug25714 bug25714.c) -TARGET_LINK_LIBRARIES(bug25714 mysqlclient wsock32) +TARGET_LINK_LIBRARIES(bug25714 mysqlclient) From 28015993235d3a1a56f08de5b86c4e1ef59d4723 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Wed, 30 Sep 2009 15:35:01 +0200 Subject: [PATCH 7/9] Backport http://lists.mysql.com/commits/57778 2677 Vladislav Vaintroub 2008-11-04 CMakeLists.txt files cleanup - remove SAFEMALLOC and SAFE_MUTEX definitions that were present in *each* CMakeLists.txt. Instead, put them into top level CMakeLists.txt, but disable on Windows, because a) SAFEMALLOC does not add any functionality that is not already present in Debug C runtime ( and 2 safe malloc one on top of the other only unnecessarily slows down the server) b)SAFE_MUTEX does not work on Windows and have been explicitely disabled on Windows with #undef previously. Fortunately, ntdll does pretty good job identifying l problems with CRITICAL_SECTIONs. DebugBreak()s on using uninited critical section, unlocking unowned critical section) -Also, remove occationally used -D_DEBUG (added by compiler anyway) --- CMakeLists.txt | 7 +++++++ client/CMakeLists.txt | 7 ------- dbug/CMakeLists.txt | 1 - extra/CMakeLists.txt | 2 -- libmysql/CMakeLists.txt | 2 -- libmysqld/CMakeLists.txt | 3 --- mysys/CMakeLists.txt | 2 -- regex/CMakeLists.txt | 2 -- sql/CMakeLists.txt | 4 ++-- sql/udf_example.c | 10 +++++----- storage/blackhole/CMakeLists.txt | 3 --- storage/csv/CMakeLists.txt | 2 -- storage/example/CMakeLists.txt | 2 -- storage/heap/CMakeLists.txt | 2 -- storage/innobase/CMakeLists.txt | 6 +----- storage/myisam/CMakeLists.txt | 2 -- storage/myisammrg/CMakeLists.txt | 2 -- strings/CMakeLists.txt | 3 --- tests/CMakeLists.txt | 2 -- vio/CMakeLists.txt | 2 -- 20 files changed, 15 insertions(+), 51 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fba26ec1464..5f2474eaaf8 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,6 +42,13 @@ ADD_DEFINITIONS(-DSHAREDIR="share") # Set debug options SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DFORCE_INIT_OF_VARS") +# Do not use SAFEMALLOC for Windows builds, as Debug CRT has the same functionality +# Neither SAFE_MUTEX works on Windows and it has been explicitely undefined in +# my_pthread.h +IF(NOT WIN32) + SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") + SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") +ENDIF(NOT WIN32) SET(localstatedir "C:\\mysql\\data") CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/support-files/my-huge.cnf.sh diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 176613e2dc8..28e4c354a69 100755 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -14,13 +14,6 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA INCLUDE("${PROJECT_SOURCE_DIR}/win/mysql_manifest.cmake") -# We use the "mysqlclient" library here just as safety, in case -# any of the clients here would go beyond the client API and access the -# Thread Local Storage directly. - -SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") -SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") - INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/zlib ${CMAKE_SOURCE_DIR}/extra/yassl/include diff --git a/dbug/CMakeLists.txt b/dbug/CMakeLists.txt index 8b27f79dcf4..fabb592dccc 100755 --- a/dbug/CMakeLists.txt +++ b/dbug/CMakeLists.txt @@ -18,7 +18,6 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/dbug) SET(DBUG_SOURCES dbug.c factorial.c sanity.c) IF(NOT SOURCE_SUBLIBS) - SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include) ADD_LIBRARY(dbug ${DBUG_SOURCES}) ENDIF(NOT SOURCE_SUBLIBS) diff --git a/extra/CMakeLists.txt b/extra/CMakeLists.txt index 0d09e676af1..44c96ccbb86 100755 --- a/extra/CMakeLists.txt +++ b/extra/CMakeLists.txt @@ -14,8 +14,6 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA INCLUDE("${PROJECT_SOURCE_DIR}/win/mysql_manifest.cmake") -SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") -SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include) diff --git a/libmysql/CMakeLists.txt b/libmysql/CMakeLists.txt index 29fe4ca33c3..805551b7ee3 100755 --- a/libmysql/CMakeLists.txt +++ b/libmysql/CMakeLists.txt @@ -14,8 +14,6 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA INCLUDE("${PROJECT_SOURCE_DIR}/win/mysql_manifest.cmake") -SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") -SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") # Note that we don't link with the libraries "strings" or "mysys" # here, instead we recompile the files needed and include them diff --git a/libmysqld/CMakeLists.txt b/libmysqld/CMakeLists.txt index f180c6e1921..6e1ad17b808 100644 --- a/libmysqld/CMakeLists.txt +++ b/libmysqld/CMakeLists.txt @@ -13,9 +13,6 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") -SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") - ADD_DEFINITIONS(-DMYSQL_SERVER -DEMBEDDED_LIBRARY -DHAVE_DLOPEN) INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include diff --git a/mysys/CMakeLists.txt b/mysys/CMakeLists.txt index f2d540d2295..aa7523aea3f 100755 --- a/mysys/CMakeLists.txt +++ b/mysys/CMakeLists.txt @@ -13,8 +13,6 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") - # Only the server link with this library, the client libraries and the client # executables all link with recompiles of source found in the "mysys" directory. # So we only need to create one version of this library, with the "static" diff --git a/regex/CMakeLists.txt b/regex/CMakeLists.txt index a3088c00357..a2974b45b06 100755 --- a/regex/CMakeLists.txt +++ b/regex/CMakeLists.txt @@ -13,8 +13,6 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_DEBUG -DSAFEMALLOC -DSAFE_MUTEX") -SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -D_DEBUG -DSAFEMALLOC -DSAFE_MUTEX") INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include) diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 1b7175a7b82..49ae232c31c 100755 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -15,9 +15,9 @@ INCLUDE("${PROJECT_SOURCE_DIR}/win/mysql_manifest.cmake") SET(CMAKE_CXX_FLAGS_DEBUG - "${CMAKE_CXX_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX -DUSE_SYMDIR /Zi") + "${CMAKE_CXX_FLAGS_DEBUG} -DUSE_SYMDIR /Zi") SET(CMAKE_C_FLAGS_DEBUG - "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX -DUSE_SYMDIR /Zi") + "${CMAKE_C_FLAGS_DEBUG} -DUSE_SYMDIR /Zi") SET(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} /MAP /MAPINFO:EXPORTS") INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include diff --git a/sql/udf_example.c b/sql/udf_example.c index 1d6a9828594..4b39a83d4ac 100644 --- a/sql/udf_example.c +++ b/sql/udf_example.c @@ -138,11 +138,11 @@ typedef long long longlong; #endif #include #include - -#ifdef _WIN32 -/* inet_aton needs winsock library */ -#pragma comment(lib, "ws2_32") -#endif + +#ifdef _WIN32 +/* inet_aton needs winsock library */ +#pragma comment(lib, "ws2_32") +#endif static pthread_mutex_t LOCK_hostname; diff --git a/storage/blackhole/CMakeLists.txt b/storage/blackhole/CMakeLists.txt index b762228d7fd..bed282ef21d 100644 --- a/storage/blackhole/CMakeLists.txt +++ b/storage/blackhole/CMakeLists.txt @@ -13,9 +13,6 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") -SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") - INCLUDE("${PROJECT_SOURCE_DIR}/storage/mysql_storage_engine.cmake") SET(BLACKHOLE_SOURCES ha_blackhole.cc ha_blackhole.h) diff --git a/storage/csv/CMakeLists.txt b/storage/csv/CMakeLists.txt index eb21a9b048c..37760588897 100644 --- a/storage/csv/CMakeLists.txt +++ b/storage/csv/CMakeLists.txt @@ -13,8 +13,6 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") -SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") INCLUDE("${PROJECT_SOURCE_DIR}/storage/mysql_storage_engine.cmake") SET(CSV_SOURCES ha_tina.cc ha_tina.h transparent_file.cc transparent_file.h) diff --git a/storage/example/CMakeLists.txt b/storage/example/CMakeLists.txt index a328da107bd..f0b1343ab9c 100644 --- a/storage/example/CMakeLists.txt +++ b/storage/example/CMakeLists.txt @@ -13,8 +13,6 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") -SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") INCLUDE("${PROJECT_SOURCE_DIR}/storage/mysql_storage_engine.cmake") SET(EXAMPLE_SOURCES ha_example.cc) MYSQL_STORAGE_ENGINE(EXAMPLE) diff --git a/storage/heap/CMakeLists.txt b/storage/heap/CMakeLists.txt index c2d2cd1290f..4a0fa22c8f1 100755 --- a/storage/heap/CMakeLists.txt +++ b/storage/heap/CMakeLists.txt @@ -13,8 +13,6 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") -SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") INCLUDE("${PROJECT_SOURCE_DIR}/storage/mysql_storage_engine.cmake") SET(HEAP_SOURCES _check.c _rectest.c hp_block.c hp_clear.c hp_close.c hp_create.c diff --git a/storage/innobase/CMakeLists.txt b/storage/innobase/CMakeLists.txt index f4d8e7b8231..31d33f06823 100644 --- a/storage/innobase/CMakeLists.txt +++ b/storage/innobase/CMakeLists.txt @@ -16,11 +16,7 @@ # This is the CMakeLists for InnoDB Plugin -# TODO: remove the two FLAGS_DEBUG settings when merging into -# 6.0-based trees, like is already the case for other engines in -# those trees. -SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") -SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") + INCLUDE("${PROJECT_SOURCE_DIR}/storage/mysql_storage_engine.cmake") # Include directories under innobase diff --git a/storage/myisam/CMakeLists.txt b/storage/myisam/CMakeLists.txt index 0f070510e5c..829d89a798a 100755 --- a/storage/myisam/CMakeLists.txt +++ b/storage/myisam/CMakeLists.txt @@ -15,8 +15,6 @@ INCLUDE("${PROJECT_SOURCE_DIR}/storage/mysql_storage_engine.cmake") INCLUDE("${PROJECT_SOURCE_DIR}/win/mysql_manifest.cmake") -SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") -SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") SET(MYISAM_SOURCES ft_boolean_search.c ft_nlq_search.c ft_parser.c ft_static.c ft_stem.c ha_myisam.cc diff --git a/storage/myisammrg/CMakeLists.txt b/storage/myisammrg/CMakeLists.txt index 60cfffc67ff..c545d04a780 100755 --- a/storage/myisammrg/CMakeLists.txt +++ b/storage/myisammrg/CMakeLists.txt @@ -12,8 +12,6 @@ # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") -SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") INCLUDE("${PROJECT_SOURCE_DIR}/storage/mysql_storage_engine.cmake") diff --git a/strings/CMakeLists.txt b/strings/CMakeLists.txt index 3d9de566670..294a129fc1b 100755 --- a/strings/CMakeLists.txt +++ b/strings/CMakeLists.txt @@ -13,9 +13,6 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_DEBUG -DSAFEMALLOC -DSAFE_MUTEX") -SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -D_DEBUG -DSAFEMALLOC -DSAFE_MUTEX") - INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include) SET(STRINGS_SOURCES bchange.c bcmp.c bfill.c bmove512.c bmove_upp.c ctype-big5.c ctype-bin.c ctype-cp932.c diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 270aaf53c20..0ee8769cd23 100755 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -13,8 +13,6 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") ADD_DEFINITIONS("-DMYSQL_CLIENT") INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include) diff --git a/vio/CMakeLists.txt b/vio/CMakeLists.txt index 164bcde7c4b..e1bd57d150f 100755 --- a/vio/CMakeLists.txt +++ b/vio/CMakeLists.txt @@ -13,8 +13,6 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_DEBUG -DSAFEMALLOC -DSAFE_MUTEX") -SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -D_DEBUG -DSAFEMALLOC -DSAFE_MUTEX") ADD_DEFINITIONS(-DUSE_SYMDIR) INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/extra/yassl/include) From 9cf8d12c855e9713c5d3a170249c7d67935b2209 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Wed, 30 Sep 2009 17:40:12 +0200 Subject: [PATCH 8/9] Backport of this changeset http://lists.mysql.com/commits/59686 Cleanup pthread_self(), pthread_create(), pthread_join() implementation on Windows. Prior implementation is was unnecessarily complicated and even differs in embedded and non-embedded case. Improvements in this patch: * pthread_t is now the unique thread ID, instead of HANDLE returned by beginthread This simplifies pthread_self() to be just straight GetCurrentThreadId(). prior it was much art involved in passing the beginthread() handle from the caller to the TLS structure in the child thread ( did not work for the main thread of course) * remove MySQL specific my_thread_init()/my_thread_end() from pthread_create. No automagic is done on Unix on pthread_create(). Having the same on Windows will improve portability and avoid extra #ifdef's * remove redefinition of getpid() - it was defined as GetCurrentThreadId() --- include/config-win.h | 3 + include/my_pthread.h | 12 ++-- libmysqld/CMakeLists.txt | 2 +- mysys/my_thr_init.c | 43 ++++++++++--- mysys/my_wincond.c | 3 +- mysys/my_winthread.c | 131 +++++++++++++++++++-------------------- sql/mysqld.cc | 62 +++++++----------- sql/sql_connect.cc | 9 --- sql/sql_insert.cc | 5 -- 9 files changed, 131 insertions(+), 139 deletions(-) diff --git a/include/config-win.h b/include/config-win.h index bcad4e04346..514a762d6d8 100644 --- a/include/config-win.h +++ b/include/config-win.h @@ -27,6 +27,9 @@ #include #include #include +#include +#include /* getpid()*/ + #define HAVE_SMEM 1 diff --git a/include/my_pthread.h b/include/my_pthread.h index 2928cb60c2d..b4fe1203d2b 100644 --- a/include/my_pthread.h +++ b/include/my_pthread.h @@ -31,7 +31,7 @@ extern "C" { #if defined(__WIN__) typedef CRITICAL_SECTION pthread_mutex_t; -typedef HANDLE pthread_t; +typedef DWORD pthread_t; typedef struct thread_attr { DWORD dwStackSize ; DWORD dwCreatingFlag ; @@ -64,8 +64,7 @@ typedef struct { typedef int pthread_mutexattr_t; -#define win_pthread_self my_thread_var->pthread_self -#define pthread_self() win_pthread_self +#define pthread_self() GetCurrentThreadId() #define pthread_handler_t EXTERNC void * __cdecl typedef void * (__cdecl *pthread_handler)(void *); @@ -99,7 +98,7 @@ struct timespec { (ABSTIME).max_timeout_msec= (long)((NSEC)/1000000); \ } -void win_pthread_init(void); + int win_pthread_mutex_trylock(pthread_mutex_t *mutex); int pthread_create(pthread_t *,pthread_attr_t *,pthread_handler,void *); int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr); @@ -116,11 +115,11 @@ int pthread_attr_destroy(pthread_attr_t *connect_att); struct tm *localtime_r(const time_t *timep,struct tm *tmp); struct tm *gmtime_r(const time_t *timep,struct tm *tmp); +void pthread_exit(void *a); +int pthread_join(pthread_t thread, void **value_ptr); -void pthread_exit(void *a); /* was #define pthread_exit(A) ExitThread(A)*/ #define ETIMEDOUT 145 /* Win32 doesn't have this */ -#define getpid() GetCurrentThreadId() #define HAVE_LOCALTIME_R 1 #define _REENTRANT 1 #define HAVE_PTHREAD_ATTR_SETSTACKSIZE 1 @@ -145,7 +144,6 @@ void pthread_exit(void *a); /* was #define pthread_exit(A) ExitThread(A)*/ #define my_pthread_setprio(A,B) SetThreadPriority(GetCurrentThread(), (B)) #define pthread_kill(A,B) pthread_dummy((A) ? 0 : ESRCH) -#define pthread_join(A,B) (WaitForSingleObject((A), INFINITE) != WAIT_OBJECT_0) /* Dummy defines for easier code */ #define pthread_attr_setdetachstate(A,B) pthread_dummy(0) diff --git a/libmysqld/CMakeLists.txt b/libmysqld/CMakeLists.txt index 6e1ad17b808..bce14b38338 100644 --- a/libmysqld/CMakeLists.txt +++ b/libmysqld/CMakeLists.txt @@ -130,7 +130,7 @@ SET(LIBMYSQLD_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc ../sql/time.cc ../sql/tztime.cc ../sql/uniques.cc ../sql/unireg.cc ../sql/partition_info.cc ../sql/sql_connect.cc ../sql/scheduler.cc ../sql/event_parse_data.cc - ./sql/sql_signal.cc + ../sql/sql_signal.cc ${GEN_SOURCES} ${LIB_SOURCES}) diff --git a/mysys/my_thr_init.c b/mysys/my_thr_init.c index 2c346145cf5..c59b2d51742 100644 --- a/mysys/my_thr_init.c +++ b/mysys/my_thr_init.c @@ -42,7 +42,9 @@ pthread_mutexattr_t my_fast_mutexattr; #ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP pthread_mutexattr_t my_errorcheck_mutexattr; #endif - +#ifdef _MSC_VER +static void install_sigabrt_handler(); +#endif #ifdef TARGET_OS_LINUX /* @@ -145,15 +147,18 @@ my_bool my_thread_global_init(void) pthread_mutex_init(&THR_LOCK_threads,MY_MUTEX_INIT_FAST); pthread_mutex_init(&THR_LOCK_time,MY_MUTEX_INIT_FAST); pthread_cond_init(&THR_COND_threads, NULL); -#if defined( __WIN__) || defined(OS2) - win_pthread_init(); -#endif + #if !defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R) pthread_mutex_init(&LOCK_localtime_r,MY_MUTEX_INIT_SLOW); #endif #ifndef HAVE_GETHOSTBYNAME_R pthread_mutex_init(&LOCK_gethostbyname_r,MY_MUTEX_INIT_SLOW); #endif + +#ifdef _MSC_VER + install_sigabrt_handler(); +#endif + if (my_thread_init()) { my_thread_global_end(); /* Clean up */ @@ -268,11 +273,7 @@ my_bool my_thread_init(void) goto end; } pthread_setspecific(THR_KEY_mysys,tmp); -#if defined(__WIN__) && defined(EMBEDDED_LIBRARY) - tmp->pthread_self= (pthread_t) getpid(); -#else tmp->pthread_self= pthread_self(); -#endif pthread_mutex_init(&tmp->mutex,MY_MUTEX_INIT_FAST); pthread_cond_init(&tmp->suspend, NULL); tmp->init= 1; @@ -398,4 +399,30 @@ static uint get_thread_lib(void) return THD_LIB_OTHER; } +#ifdef _WIN32 +/* + In Visual Studio 2005 and later, default SIGABRT handler will overwrite + any unhandled exception filter set by the application and will try to + call JIT debugger. This is not what we want, this we calling __debugbreak + to stop in debugger, if process is being debugged or to generate + EXCEPTION_BREAKPOINT and then handle_segfault will do its magic. +*/ + +#if (_MSC_VER >= 1400) +static void my_sigabrt_handler(int sig) +{ + __debugbreak(); +} +#endif /*_MSC_VER >=1400 */ + +static void install_sigabrt_handler(void) +{ +#if (_MSC_VER >=1400) + /*abort() should not override our exception filter*/ + _set_abort_behavior(0,_CALL_REPORTFAULT); + signal(SIGABRT,my_sigabrt_handler); +#endif /* _MSC_VER >=1400 */ +} +#endif + #endif /* THREAD */ diff --git a/mysys/my_wincond.c b/mysys/my_wincond.c index d1b07b61408..956d29a970b 100644 --- a/mysys/my_wincond.c +++ b/mysys/my_wincond.c @@ -16,12 +16,11 @@ /***************************************************************************** ** The following is a simple implementation of posix conditions *****************************************************************************/ +#if defined(_WIN32) #undef SAFE_MUTEX /* Avoid safe_mutex redefinitions */ #include "mysys_priv.h" -#if defined(THREAD) && defined(__WIN__) #include -#undef getpid #include #include diff --git a/mysys/my_winthread.c b/mysys/my_winthread.c index 543e1787fb6..9e8458b0799 100644 --- a/mysys/my_winthread.c +++ b/mysys/my_winthread.c @@ -14,33 +14,23 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /***************************************************************************** -** Simulation of posix threads calls for WIN95 and NT +** Simulation of posix threads calls for Windows *****************************************************************************/ - +#if defined (_WIN32) /* SAFE_MUTEX will not work until the thread structure is up to date */ #undef SAFE_MUTEX - #include "mysys_priv.h" -#if defined(THREAD) && defined(__WIN__) -#include -#undef getpid #include +#include -static pthread_mutex_t THR_LOCK_thread; +static void install_sigabrt_handler(void); -struct pthread_map +struct thread_start_parameter { - HANDLE pthreadself; pthread_handler func; - void *param; + void *arg; }; -void win_pthread_init(void) -{ - pthread_mutex_init(&THR_LOCK_thread,MY_MUTEX_INIT_FAST); -} - - /** Adapter to @c pthread_mutex_trylock() @@ -62,72 +52,81 @@ win_pthread_mutex_trylock(pthread_mutex_t *mutex) return EBUSY; } - -/* -** We have tried to use '_beginthreadex' instead of '_beginthread' here -** but in this case the program leaks about 512 characters for each -** created thread ! -** As we want to save the created thread handler for other threads to -** use and to be returned by pthread_self() (instead of the Win32 pseudo -** handler), we have to go trough pthread_start() to catch the returned handler -** in the new thread. -*/ - -pthread_handler_t pthread_start(void *param) +static unsigned int __stdcall pthread_start(void *p) { - pthread_handler func=((struct pthread_map *) param)->func; - void *func_param=((struct pthread_map *) param)->param; - my_thread_init(); /* Will always succeed in windows */ - pthread_mutex_lock(&THR_LOCK_thread); /* Wait for beginthread to return */ - win_pthread_self=((struct pthread_map *) param)->pthreadself; - pthread_mutex_unlock(&THR_LOCK_thread); - free((char*) param); /* Free param from create */ - pthread_exit((void*) (*func)(func_param)); - return 0; /* Safety */ + struct thread_start_parameter *par= (struct thread_start_parameter *)p; + pthread_handler func= par->func; + void *arg= par->arg; + free(p); + (*func)(arg); + return 0; } int pthread_create(pthread_t *thread_id, pthread_attr_t *attr, - pthread_handler func, void *param) + pthread_handler func, void *param) { - HANDLE hThread; - struct pthread_map *map; + uintptr_t handle; + struct thread_start_parameter *par; + unsigned int stack_size; DBUG_ENTER("pthread_create"); - if (!(map=malloc(sizeof(*map)))) - DBUG_RETURN(-1); - map->func=func; - map->param=param; - pthread_mutex_lock(&THR_LOCK_thread); -#ifdef __BORLANDC__ - hThread=(HANDLE)_beginthread((void(_USERENTRY *)(void *)) pthread_start, - attr->dwStackSize ? attr->dwStackSize : - 65535, (void*) map); -#else - hThread=(HANDLE)_beginthread((void( __cdecl *)(void *)) pthread_start, - attr->dwStackSize ? attr->dwStackSize : - 65535, (void*) map); -#endif - DBUG_PRINT("info", ("hThread=%lu",(long) hThread)); - *thread_id=map->pthreadself=hThread; - pthread_mutex_unlock(&THR_LOCK_thread); + par= (struct thread_start_parameter *)malloc(sizeof(*par)); + if (!par) + goto error_return; - if (hThread == (HANDLE) -1) - { - int error=errno; - DBUG_PRINT("error", - ("Can't create thread to handle request (error %d)",error)); - DBUG_RETURN(error ? error : -1); - } - VOID(SetThreadPriority(hThread, attr->priority)) ; + par->func= func; + par->arg= param; + stack_size= attr?attr->dwStackSize:0; + + handle= _beginthreadex(NULL, stack_size , pthread_start, par, 0, thread_id); + if (!handle) + goto error_return; + DBUG_PRINT("info", ("thread id=%u",*thread_id)); + + /* Do not need thread handle, close it */ + CloseHandle((HANDLE)handle); DBUG_RETURN(0); + +error_return: + DBUG_PRINT("error", + ("Can't create thread to handle request (error %d)",errno)); + DBUG_RETURN(-1); } void pthread_exit(void *a) { - _endthread(); + _endthreadex(0); } +int pthread_join(pthread_t thread, void **value_ptr) +{ + DWORD ret; + HANDLE handle; + + handle= OpenThread(SYNCHRONIZE, FALSE, thread); + if (!handle) + { + errno= EINVAL; + goto error_return; + } + + ret= WaitForSingleObject(handle, INFINITE); + + if(ret != WAIT_OBJECT_0) + { + errno= EINVAL; + goto error_return; + } + + CloseHandle(handle); + return 0; + +error_return: + if(handle) + CloseHandle(handle); + return -1; +} #endif diff --git a/sql/mysqld.cc b/sql/mysqld.cc index bb79f931fb3..3585539318d 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -805,7 +805,10 @@ static void set_server_version(void); static int init_thread_environment(); static char *get_relative_path(const char *path); static int fix_paths(void); -pthread_handler_t handle_connections_sockets(void *arg); +void handle_connections_sockets(); +#ifdef _WIN32 +pthread_handler_t handle_connections_sockets_thread(void *arg); +#endif pthread_handler_t kill_server_thread(void *arg); static void bootstrap(FILE *file); static bool read_init_file(char *file_name); @@ -2034,29 +2037,7 @@ static BOOL WINAPI console_event_handler( DWORD type ) } -/* - In Visual Studio 2005 and later, default SIGABRT handler will overwrite - any unhandled exception filter set by the application and will try to - call JIT debugger. This is not what we want, this we calling __debugbreak - to stop in debugger, if process is being debugged or to generate - EXCEPTION_BREAKPOINT and then handle_segfault will do its magic. -*/ -#if (_MSC_VER >= 1400) -static void my_sigabrt_handler(int sig) -{ - __debugbreak(); -} -#endif /*_MSC_VER >=1400 */ - -void win_install_sigabrt_handler(void) -{ -#if (_MSC_VER >=1400) - /*abort() should not override our exception filter*/ - _set_abort_behavior(0,_CALL_REPORTFAULT); - signal(SIGABRT,my_sigabrt_handler); -#endif /* _MSC_VER >=1400 */ -} #ifdef DEBUG_UNHANDLED_EXCEPTION_FILTER #define DEBUGGER_ATTACH_TIMEOUT 120 @@ -2135,7 +2116,6 @@ LONG WINAPI my_unhandler_exception_filter(EXCEPTION_POINTERS *ex_pointers) static void init_signals(void) { - win_install_sigabrt_handler(); if(opt_console) SetConsoleCtrlHandler(console_event_handler,TRUE); @@ -4132,7 +4112,8 @@ static void create_shutdown_thread() #ifdef __WIN__ hEventShutdown=CreateEvent(0, FALSE, FALSE, shutdown_event_name); pthread_t hThread; - if (pthread_create(&hThread,&connection_attrib,handle_shutdown,0)) + if (pthread_create(&hThread,&connection_attrib, + handle_connections_sockets_thread, 0)) sql_print_warning("Can't create thread to handle shutdown requests"); // On "Stop Service" we have to do regular shutdown @@ -4177,7 +4158,7 @@ static void handle_connections_methods() { handler_count++; if (pthread_create(&hThread,&connection_attrib, - handle_connections_sockets, 0)) + handle_connections_sockets_thread, 0)) { sql_print_warning("Can't create thread to handle TCP/IP"); handler_count--; @@ -4506,18 +4487,11 @@ we force server id to 2, but this MySQL server will not act as a slave."); pthread_cond_signal(&COND_server_started); pthread_mutex_unlock(&LOCK_server_started); -#if defined(__NT__) || defined(HAVE_SMEM) +#if defined(_WIN32) || defined(HAVE_SMEM) handle_connections_methods(); #else -#ifdef __WIN__ - if (!have_tcpip || opt_disable_networking) - { - sql_print_error("TCP/IP unavailable or disabled with --skip-networking; no available interfaces"); - unireg_abort(1); - } -#endif - handle_connections_sockets(0); -#endif /* __NT__ */ + handle_connections_sockets(); +#endif /* _WIN32 || HAVE_SMEM */ /* (void) pthread_attr_destroy(&connection_attrib); */ @@ -4992,7 +4966,7 @@ inline void kill_broken_server() /* Handle new connections and spawn new process to handle them */ #ifndef EMBEDDED_LIBRARY -pthread_handler_t handle_connections_sockets(void *arg __attribute__((unused))) +void handle_connections_sockets() { my_socket sock,new_sock; uint error_count=0; @@ -5195,13 +5169,19 @@ pthread_handler_t handle_connections_sockets(void *arg __attribute__((unused))) create_new_thread(thd); } - - decrement_handler_count(); - DBUG_RETURN(0); + DBUG_VOID_RETURN; } -#ifdef __NT__ +#ifdef _WIN32 +pthread_handler_t handle_connections_sockets_thread(void *arg) +{ + my_thread_init(); + handle_connections_sockets(); + decrement_handler_count(); + return 0; +} + pthread_handler_t handle_connections_namedpipes(void *arg) { HANDLE hConnectedPipe; diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc index 404d734559f..a87d201d9ed 100644 --- a/sql/sql_connect.cc +++ b/sql/sql_connect.cc @@ -39,10 +39,6 @@ #define MIN_HANDSHAKE_SIZE 6 #endif /* HAVE_OPENSSL */ -#ifdef __WIN__ -extern void win_install_sigabrt_handler(); -#endif - /* Get structure for logging connection data for the current user */ @@ -612,13 +608,8 @@ void thd_init_client_charset(THD *thd, uint cs_number) bool init_new_connection_handler_thread() { pthread_detach_this_thread(); -#if defined(__WIN__) - win_install_sigabrt_handler(); -#else - /* Win32 calls this in pthread_create */ if (my_thread_init()) return 1; -#endif /* __WIN__ */ return 0; } diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 7d2513bd419..1ef4d5827ec 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -2498,7 +2498,6 @@ pthread_handler_t handle_delayed_insert(void *arg) since it does not find one in the list. */ pthread_mutex_lock(&di->mutex); -#if !defined( __WIN__) /* Win32 calls this in pthread_create */ if (my_thread_init()) { /* Can't use my_error since store_globals has not yet been called */ @@ -2506,13 +2505,9 @@ pthread_handler_t handle_delayed_insert(void *arg) ER(ER_OUT_OF_RESOURCES), NULL); goto end; } -#endif - handle_delayed_insert_impl(thd, di); -#ifndef __WIN__ end: -#endif /* di should be unlinked from the thread handler list and have no active clients From 6e6b84fd02abc1ba79877722d106838118b269d0 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Wed, 30 Sep 2009 22:10:22 +0200 Subject: [PATCH 9/9] backport of Revision: 2597.72.1 revid:sp1r-Reggie@core.-20080403153947-15243 removed instances of __NT__ from code. We now only build "NT" binaries --- CMakeLists.txt | 3 --- include/config-win.h | 9 ++------- include/my_sys.h | 2 +- mysys/my_delete.c | 2 +- mysys/my_thr_init.c | 5 +++++ mysys/my_winfile.c | 15 ++++++++------- sql/log.cc | 14 +++++++------- sql/mysqld.cc | 20 ++++++++------------ sql/set_var.cc | 2 +- sql/sql_connect.cc | 2 +- vio/viosocket.c | 4 ++-- 11 files changed, 36 insertions(+), 42 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5f2474eaaf8..301855cf326 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,9 +62,6 @@ CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/support-files/my-medium.cnf.sh CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/support-files/my-small.cnf.sh ${CMAKE_SOURCE_DIR}/support-files/my-small.ini @ONLY) - -ADD_DEFINITIONS(-D__NT__) - IF(CYBOZU) ADD_DEFINITIONS(-DCYBOZU) ENDIF(CYBOZU) diff --git a/include/config-win.h b/include/config-win.h index 514a762d6d8..725b4fdf07b 100644 --- a/include/config-win.h +++ b/include/config-win.h @@ -177,7 +177,7 @@ typedef uint rf_SetTimer; #define SIZEOF_CHARP 4 #endif #define HAVE_BROKEN_NETINET_INCLUDES -#ifdef __NT__ +#ifdef _WIN32 #define HAVE_NAMED_PIPE /* We can only create pipes on NT */ #endif @@ -290,11 +290,6 @@ inline ulonglong double2ulonglong(double d) #define strcasecmp stricmp #define strncasecmp strnicmp -#ifndef __NT__ -#undef FILE_SHARE_DELETE -#define FILE_SHARE_DELETE 0 /* Not implemented on Win 98/ME */ -#endif - #ifdef NOT_USED #define HAVE_SNPRINTF /* Gave link error */ #define _snprintf snprintf @@ -344,7 +339,7 @@ inline ulonglong double2ulonglong(double d) #define thread_safe_increment(V,L) InterlockedIncrement((long*) &(V)) #define thread_safe_decrement(V,L) InterlockedDecrement((long*) &(V)) /* The following is only used for statistics, so it should be good enough */ -#ifdef __NT__ /* This should also work on Win98 but .. */ +#ifdef _WIN32 #define thread_safe_add(V,C,L) InterlockedExchangeAdd((long*) &(V),(C)) #define thread_safe_sub(V,C,L) InterlockedExchangeAdd((long*) &(V),-(long) (C)) #endif diff --git a/include/my_sys.h b/include/my_sys.h index 451c8418ebd..4b93dc0e364 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -640,7 +640,7 @@ extern int my_access(const char *path, int amode); extern int check_if_legal_filename(const char *path); extern int check_if_legal_tablename(const char *path); -#if defined(__WIN__) && defined(__NT__) +#ifdef _WIN32 extern int nt_share_delete(const char *name,myf MyFlags); #define my_delete_allow_opened(fname,flags) nt_share_delete((fname),(flags)) #else diff --git a/mysys/my_delete.c b/mysys/my_delete.c index 22425ed95fd..3ab6ba399f9 100644 --- a/mysys/my_delete.c +++ b/mysys/my_delete.c @@ -36,7 +36,7 @@ int my_delete(const char *name, myf MyFlags) DBUG_RETURN(err); } /* my_delete */ -#if defined(__WIN__) && defined(__NT__) +#if defined(__WIN__) /* Delete file which is possibly not closed. diff --git a/mysys/my_thr_init.c b/mysys/my_thr_init.c index c59b2d51742..c6057f19a82 100644 --- a/mysys/my_thr_init.c +++ b/mysys/my_thr_init.c @@ -267,6 +267,11 @@ my_bool my_thread_init(void) #endif goto end; } + +#ifdef _MSC_VER + install_sigabrt_handler(); +#endif + if (!(tmp= (struct st_my_thread_var *) calloc(1, sizeof(*tmp)))) { error= 1; diff --git a/mysys/my_winfile.c b/mysys/my_winfile.c index de1d747c967..6a2cda5ba29 100644 --- a/mysys/my_winfile.c +++ b/mysys/my_winfile.c @@ -314,10 +314,10 @@ size_t my_win_pread(File Filedes, uchar *Buffer, size_t Count, my_off_t offset) if(!ReadFile(hFile, Buffer, (DWORD)Count, &nBytesRead, &ov)) { DWORD lastError= GetLastError(); - /* - ERROR_BROKEN_PIPE is returned when no more data coming - through e.g. a command pipe in windows : see MSDN on ReadFile. - */ + /* + ERROR_BROKEN_PIPE is returned when no more data coming + through e.g. a command pipe in windows : see MSDN on ReadFile. + */ if(lastError == ERROR_HANDLE_EOF || lastError == ERROR_BROKEN_PIPE) DBUG_RETURN(0); /*return 0 at EOF*/ my_osmaperr(lastError); @@ -367,8 +367,8 @@ size_t my_win_pwrite(File Filedes, const uchar *Buffer, size_t Count, LARGE_INTEGER li; DBUG_ENTER("my_win_pwrite"); - DBUG_PRINT("my",("Filedes: %d, Buffer: %p, Count: %zd, offset: %llu", - Filedes, Buffer, Count, (ulonglong)offset)); + DBUG_PRINT("my",("Filedes: %d, Buffer: %p, Count: %llu, offset: %llu", + Filedes, Buffer, (ulonglong)Count, (ulonglong)offset)); if(!Count) DBUG_RETURN(0); @@ -425,7 +425,8 @@ size_t my_win_write(File fd, const uchar *Buffer, size_t Count) HANDLE hFile; DBUG_ENTER("my_win_write"); - DBUG_PRINT("my",("Filedes: %d, Buffer: %p, Count %zd", fd, Buffer, Count)); + DBUG_PRINT("my",("Filedes: %d, Buffer: %p, Count %llu", fd, Buffer, + (ulonglong)Count)); if(my_get_open_flags(fd) & _O_APPEND) { /* diff --git a/sql/log.cc b/sql/log.cc index 3d4aca572f0..ed208a84ffc 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -33,7 +33,7 @@ #include #include // For test_if_number -#ifdef __NT__ +#ifdef _WIN32 #include "message.h" #endif @@ -1794,7 +1794,7 @@ err: DBUG_RETURN(-1); } -#ifdef __NT__ +#ifdef _WIN32 static int eventSource = 0; static void setup_windows_event_source() @@ -1829,7 +1829,7 @@ static void setup_windows_event_source() RegCloseKey(hRegKey); } -#endif /* __NT__ */ +#endif /* _WIN32 */ /** @@ -1960,7 +1960,7 @@ bool MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg, #ifdef EMBEDDED_LIBRARY "embedded library\n", my_progname, server_version, MYSQL_COMPILATION_COMMENT -#elif __NT__ +#elif _WIN32 "started with:\nTCP Port: %d, Named Pipe: %s\n", my_progname, server_version, MYSQL_COMPILATION_COMMENT, mysqld_port, mysqld_unix_port @@ -4851,7 +4851,7 @@ void MYSQL_BIN_LOG::signal_update() DBUG_VOID_RETURN; } -#ifdef __NT__ +#ifdef _WIN32 static void print_buffer_to_nt_eventlog(enum loglevel level, char *buff, size_t length, size_t buffLen) { @@ -4884,7 +4884,7 @@ static void print_buffer_to_nt_eventlog(enum loglevel level, char *buff, DBUG_VOID_RETURN; } -#endif /* __NT__ */ +#endif /* _WIN32 */ /** @@ -4946,7 +4946,7 @@ int vprint_msg_to_log(enum loglevel level, const char *format, va_list args) length= my_vsnprintf(buff, sizeof(buff), format, args); print_buffer_to_file(level, buff); -#ifdef __NT__ +#ifdef _WIN32 print_buffer_to_nt_eventlog(level, buff, length, sizeof(buff)); #endif diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 3585539318d..e2d4de4dc56 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -732,7 +732,7 @@ static NTService Service; ///< Service object for WinNT #endif /* EMBEDDED_LIBRARY */ #endif /* __WIN__ */ -#ifdef __NT__ +#ifdef _WIN32 static char pipe_name[512]; static SECURITY_ATTRIBUTES saPipeSecurity; static SECURITY_DESCRIPTOR sdPipeDescriptor; @@ -812,7 +812,7 @@ pthread_handler_t handle_connections_sockets_thread(void *arg); pthread_handler_t kill_server_thread(void *arg); static void bootstrap(FILE *file); static bool read_init_file(char *file_name); -#ifdef __NT__ +#ifdef _WIN32 pthread_handler_t handle_connections_namedpipes(void *arg); #endif #ifdef HAVE_SMEM @@ -898,7 +898,7 @@ static void close_connections(void) ip_sock= INVALID_SOCKET; } } -#ifdef __NT__ +#ifdef _WIN32 if (hPipe != INVALID_HANDLE_VALUE && opt_enable_named_pipe) { HANDLE temp; @@ -1690,7 +1690,7 @@ static void network_init(void) } } -#ifdef __NT__ +#ifdef _WIN32 /* create named pipe */ if (Service.IsNT() && mysqld_unix_port[0] && !opt_bootstrap && opt_enable_named_pipe) @@ -4124,12 +4124,11 @@ static void create_shutdown_thread() #endif /* EMBEDDED_LIBRARY */ -#if (defined(__NT__) || defined(HAVE_SMEM)) && !defined(EMBEDDED_LIBRARY) +#if (defined(_WIN32) || defined(HAVE_SMEM)) && !defined(EMBEDDED_LIBRARY) static void handle_connections_methods() { pthread_t hThread; DBUG_ENTER("handle_connections_methods"); -#ifdef __NT__ if (hPipe == INVALID_HANDLE_VALUE && (!have_tcpip || opt_disable_networking) && !opt_enable_shared_memory) @@ -4137,12 +4136,10 @@ static void handle_connections_methods() sql_print_error("TCP/IP, --shared-memory, or --named-pipe should be configured on NT OS"); unireg_abort(1); // Will not return } -#endif pthread_mutex_lock(&LOCK_thread_count); (void) pthread_cond_init(&COND_handler_count,NULL); handler_count=0; -#ifdef __NT__ if (hPipe != INVALID_HANDLE_VALUE) { handler_count++; @@ -4153,7 +4150,6 @@ static void handle_connections_methods() handler_count--; } } -#endif /* __NT__ */ if (have_tcpip && !opt_disable_networking) { handler_count++; @@ -4193,7 +4189,7 @@ void decrement_handler_count() } #else #define decrement_handler_count() -#endif /* defined(__NT__) || defined(HAVE_SMEM) */ +#endif /* defined(_WIN32) || defined(HAVE_SMEM) */ #ifndef EMBEDDED_LIBRARY @@ -5261,7 +5257,7 @@ pthread_handler_t handle_connections_namedpipes(void *arg) decrement_handler_count(); DBUG_RETURN(0); } -#endif /* __NT__ */ +#endif /* _WIN32 */ #ifdef HAVE_SMEM @@ -5837,7 +5833,7 @@ struct my_option my_long_options[] = "Deprecated option, use --external-locking instead.", (uchar**) &opt_external_locking, (uchar**) &opt_external_locking, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, -#ifdef __NT__ +#ifdef _WIN32 {"enable-named-pipe", OPT_HAVE_NAMED_PIPE, "Enable the named pipe (NT).", (uchar**) &opt_enable_named_pipe, (uchar**) &opt_enable_named_pipe, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, diff --git a/sql/set_var.cc b/sql/set_var.cc index dc966c306a5..2e2bb369af1 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -434,7 +434,7 @@ static sys_var_thd_enum sys_myisam_stats_method(&vars, "myisam_stats_met &myisam_stats_method_typelib, NULL); -#ifdef __NT__ +#ifdef _WIN32 /* purecov: begin inspected */ static sys_var_const sys_named_pipe(&vars, "named_pipe", OPT_GLOBAL, SHOW_MY_BOOL, diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc index a87d201d9ed..4ae267a880c 100644 --- a/sql/sql_connect.cc +++ b/sql/sql_connect.cc @@ -949,7 +949,7 @@ static bool login_connection(THD *thd) if (error) { // Wrong permissions -#ifdef __NT__ +#ifdef _WIN32 if (vio_type(net->vio) == VIO_TYPE_NAMEDPIPE) my_sleep(1000); /* must wait after eof() */ #endif diff --git a/vio/viosocket.c b/vio/viosocket.c index 2d7fd0e08cd..2a22f8c7c15 100644 --- a/vio/viosocket.c +++ b/vio/viosocket.c @@ -264,7 +264,7 @@ int vio_close(Vio * vio) #ifdef __WIN__ if (vio->type == VIO_TYPE_NAMEDPIPE) { -#if defined(__NT__) && defined(MYSQL_SERVER) +#if defined(MYSQL_SERVER) CancelIo(vio->hPipe); DisconnectNamedPipe(vio->hPipe); #endif @@ -450,7 +450,7 @@ int vio_close_pipe(Vio * vio) { int r; DBUG_ENTER("vio_close_pipe"); -#if defined(__NT__) && defined(MYSQL_SERVER) +#if defined(MYSQL_SERVER) CancelIo(vio->hPipe); DisconnectNamedPipe(vio->hPipe); #endif