summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorunknown <monty@narttu.mysql.fi>2003-03-19 00:45:44 +0200
committerunknown <monty@narttu.mysql.fi>2003-03-19 00:45:44 +0200
commitf45236de394523115dfd160a00a42072b6536fc0 (patch)
treecb3c86ac4e063681514b1a8ffa9db31b639b9c46
parent21b0873a3ce5ee079b120199eb0423bdcff78c22 (diff)
downloadmariadb-git-f45236de394523115dfd160a00a42072b6536fc0.tar.gz
Allow optimzation of multi-table-update also for InnoDB tables
MEMORY is alias for HEAP for CREATE TABLE ... TYPE=HEAP Fixed bug in multi-table-update where a row could be updated several times mysql-test/r/heap.result: Test of CREATE TABLE ... type=MEMORY mysql-test/r/innodb.result: Added multi-update-test mysql-test/r/multi_update.result: Added multi-update-test mysql-test/t/heap.test: Test of CREATE TABLE ... type=MEMORY mysql-test/t/innodb.test: Added multi-update-test mysql-test/t/multi_update.test: Added multi-update-test sql/ha_innodb.h: Allow optimzation of multi-table-update also for InnoDB tables sql/handler.h: Allow optimzation of multi-update also for InnoDB tables sql/key.cc: After merge fix sql/lex.h: MEMORY is alias for HEAP sql/sql_test.cc: Fixed wrong printf sql/sql_update.cc: Fixed bug in multi-table-update where a row could be updated several times sql/sql_yacc.yy: MEMORY is alias for HEAP
-rw-r--r--mysql-test/r/heap.result2
-rw-r--r--mysql-test/r/innodb.result79
-rw-r--r--mysql-test/r/multi_update.result64
-rw-r--r--mysql-test/t/heap.test2
-rw-r--r--mysql-test/t/innodb.test28
-rw-r--r--mysql-test/t/multi_update.test28
-rw-r--r--sql/ha_innodb.h3
-rw-r--r--sql/handler.h1
-rw-r--r--sql/key.cc2
-rw-r--r--sql/lex.h1
-rw-r--r--sql/sql_test.cc2
-rw-r--r--sql/sql_update.cc146
-rw-r--r--sql/sql_yacc.yy3
13 files changed, 313 insertions, 48 deletions
diff --git a/mysql-test/r/heap.result b/mysql-test/r/heap.result
index 73642d7f751..d8905085e34 100644
--- a/mysql-test/r/heap.result
+++ b/mysql-test/r/heap.result
@@ -23,7 +23,7 @@ a b
4 6
alter table t1 add c int not null, add key (c,a);
drop table t1;
-create table t1 (a int not null,b int not null, primary key (a)) type=heap comment="testing heaps";
+create table t1 (a int not null,b int not null, primary key (a)) type=memory comment="testing heaps";
insert into t1 values(1,1),(2,2),(3,3),(4,4);
delete from t1 where a > 0;
select * from t1;
diff --git a/mysql-test/r/innodb.result b/mysql-test/r/innodb.result
index bbabe0a04d6..48db5412da7 100644
--- a/mysql-test/r/innodb.result
+++ b/mysql-test/r/innodb.result
@@ -1122,3 +1122,82 @@ my-test-1 my-test-2
COMMIT;
set autocommit=1;
DROP TABLE t1,t2,t3;
+CREATE TABLE t1 (a int not null primary key, b int not null, unique (b)) type=innodb;
+INSERT INTO t1 values (1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9);
+UPDATE t1 set a=a+100 where b between 2 and 3 and a < 1000;
+SELECT * from t1;
+a b
+1 1
+102 2
+103 3
+4 4
+5 5
+6 6
+7 7
+8 8
+9 9
+drop table t1;
+CREATE TABLE t1 (a int not null primary key, b int not null, key (b)) type=innodb;
+CREATE TABLE t2 (a int not null primary key, b int not null, key (b)) type=innodb;
+INSERT INTO t1 values (1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9);
+INSERT INTO t2 values (1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9);
+update t1,t2 set t1.a=t1.a+100;
+select * from t1;
+a b
+101 1
+102 2
+103 3
+104 4
+105 5
+106 6
+107 7
+108 8
+109 9
+update t1,t2 set t1.a=t1.a+100 where t1.a=101;
+select * from t1;
+a b
+201 1
+102 2
+103 3
+104 4
+105 5
+106 6
+107 7
+108 8
+109 9
+update t1,t2 set t1.b=t1.b+10 where t1.b=2;
+select * from t1;
+a b
+201 1
+103 3
+104 4
+105 5
+106 6
+107 7
+108 8
+109 9
+102 12
+update t1,t2 set t1.b=t1.b+2,t2.b=t1.b where t1.b between 3 and 5;
+select * from t1;
+a b
+201 1
+103 5
+104 6
+106 6
+105 7
+107 7
+108 8
+109 9
+102 12
+select * from t2;
+a b
+1 5
+2 5
+3 5
+4 5
+5 5
+6 5
+7 5
+8 5
+9 5
+drop table t1,t2;
diff --git a/mysql-test/r/multi_update.result b/mysql-test/r/multi_update.result
index fe028a4cb95..0f3644f15bc 100644
--- a/mysql-test/r/multi_update.result
+++ b/mysql-test/r/multi_update.result
@@ -246,3 +246,67 @@ INSERT INTO t3 VALUES (1,'jedan'),(2,'dva');
update t1,t2 set t1.naziv="aaaa" where t1.broj=t2.broj;
update t1,t2,t3 set t1.naziv="bbbb", t2.naziv="aaaa" where t1.broj=t2.broj and t2.broj=t3.broj;
drop table if exists t1,t2,t3;
+CREATE TABLE t1 (a int not null primary key, b int not null, key (b));
+CREATE TABLE t2 (a int not null primary key, b int not null, key (b));
+INSERT INTO t1 values (1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9);
+INSERT INTO t2 values (1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9);
+update t1,t2 set t1.a=t1.a+100;
+select * from t1;
+a b
+101 1
+102 2
+103 3
+104 4
+105 5
+106 6
+107 7
+108 8
+109 9
+update t1,t2 set t1.a=t1.a+100 where t1.a=101;
+select * from t1;
+a b
+201 1
+102 2
+103 3
+104 4
+105 5
+106 6
+107 7
+108 8
+109 9
+update t1,t2 set t1.b=t1.b+10 where t1.b=2;
+select * from t1;
+a b
+201 1
+102 12
+103 3
+104 4
+105 5
+106 6
+107 7
+108 8
+109 9
+update t1,t2 set t1.b=t1.b+2,t2.b=t1.b where t1.b between 3 and 5;
+select * from t1;
+a b
+201 1
+102 12
+103 5
+104 6
+105 7
+106 6
+107 7
+108 8
+109 9
+select * from t2;
+a b
+1 3
+2 3
+3 3
+4 3
+5 3
+6 3
+7 3
+8 3
+9 3
+drop table t1,t2;
diff --git a/mysql-test/t/heap.test b/mysql-test/t/heap.test
index ef7e3239a96..d1e647ae182 100644
--- a/mysql-test/t/heap.test
+++ b/mysql-test/t/heap.test
@@ -17,7 +17,7 @@ select * from t1;
alter table t1 add c int not null, add key (c,a);
drop table t1;
-create table t1 (a int not null,b int not null, primary key (a)) type=heap comment="testing heaps";
+create table t1 (a int not null,b int not null, primary key (a)) type=memory comment="testing heaps";
insert into t1 values(1,1),(2,2),(3,3),(4,4);
delete from t1 where a > 0;
select * from t1;
diff --git a/mysql-test/t/innodb.test b/mysql-test/t/innodb.test
index 320047b68fa..2f7285e7898 100644
--- a/mysql-test/t/innodb.test
+++ b/mysql-test/t/innodb.test
@@ -769,3 +769,31 @@ INSERT INTO t1 values (1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9);
UPDATE t1 set a=a+100 where b between 2 and 3 and a < 1000;
SELECT * from t1;
drop table t1;
+
+#
+# Test multi update with different join methods
+#
+
+CREATE TABLE t1 (a int not null primary key, b int not null, key (b)) type=innodb;
+CREATE TABLE t2 (a int not null primary key, b int not null, key (b)) type=innodb;
+INSERT INTO t1 values (1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9);
+INSERT INTO t2 values (1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9);
+
+# Full join, without key
+update t1,t2 set t1.a=t1.a+100;
+select * from t1;
+
+# unique key
+update t1,t2 set t1.a=t1.a+100 where t1.a=101;
+select * from t1;
+
+# ref key
+update t1,t2 set t1.b=t1.b+10 where t1.b=2;
+select * from t1;
+
+# Range key (in t1)
+update t1,t2 set t1.b=t1.b+2,t2.b=t1.b where t1.b between 3 and 5;
+select * from t1;
+select * from t2;
+
+drop table t1,t2;
diff --git a/mysql-test/t/multi_update.test b/mysql-test/t/multi_update.test
index 088b355a17c..d5543f69bd5 100644
--- a/mysql-test/t/multi_update.test
+++ b/mysql-test/t/multi_update.test
@@ -224,3 +224,31 @@ INSERT INTO t3 VALUES (1,'jedan'),(2,'dva');
update t1,t2 set t1.naziv="aaaa" where t1.broj=t2.broj;
update t1,t2,t3 set t1.naziv="bbbb", t2.naziv="aaaa" where t1.broj=t2.broj and t2.broj=t3.broj;
drop table if exists t1,t2,t3;
+
+#
+# Test multi update with different join methods
+#
+
+CREATE TABLE t1 (a int not null primary key, b int not null, key (b));
+CREATE TABLE t2 (a int not null primary key, b int not null, key (b));
+INSERT INTO t1 values (1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9);
+INSERT INTO t2 values (1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9);
+
+# Full join, without key
+update t1,t2 set t1.a=t1.a+100;
+select * from t1;
+
+# unique key
+update t1,t2 set t1.a=t1.a+100 where t1.a=101;
+select * from t1;
+
+# ref key
+update t1,t2 set t1.b=t1.b+10 where t1.b=2;
+select * from t1;
+
+# Range key (in t1)
+update t1,t2 set t1.b=t1.b+2,t2.b=t1.b where t1.b between 3 and 5;
+select * from t1;
+select * from t2;
+
+drop table t1,t2;
diff --git a/sql/ha_innodb.h b/sql/ha_innodb.h
index 94154d9f05f..8031fa0aa29 100644
--- a/sql/ha_innodb.h
+++ b/sql/ha_innodb.h
@@ -82,8 +82,7 @@ class ha_innobase: public handler
HA_PRIMARY_KEY_IN_READ_INDEX |
HA_DROP_BEFORE_CREATE |
HA_NO_PREFIX_CHAR_KEYS |
- HA_TABLE_SCAN_ON_INDEX |
- HA_NOT_MULTI_UPDATE),
+ HA_TABLE_SCAN_ON_INDEX),
last_dup_key((uint) -1),
start_of_scan(0)
{
diff --git a/sql/handler.h b/sql/handler.h
index 81724bd3abe..8f1d00f64b5 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -67,7 +67,6 @@
#define HA_CAN_FULLTEXT (HA_NO_PREFIX_CHAR_KEYS*2)
#define HA_CAN_SQL_HANDLER (HA_CAN_FULLTEXT*2)
#define HA_NO_AUTO_INCREMENT (HA_CAN_SQL_HANDLER*2)
-#define HA_NOT_MULTI_UPDATE (HA_NO_AUTO_INCREMENT*2)
/*
Next record gives next record according last record read (even
diff --git a/sql/key.cc b/sql/key.cc
index 8520dee215e..809c5a164b9 100644
--- a/sql/key.cc
+++ b/sql/key.cc
@@ -275,7 +275,7 @@ bool check_if_key_used(TABLE *table, uint idx, List<Item> &fields)
key is not updated
*/
if (idx != table->primary_key && table->primary_key < MAX_KEY &&
- (table->file->option_flag() & HA_PRIMARY_KEY_IN_READ_INDEX))
+ (table->file->table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX))
return check_if_key_used(table, table->primary_key, fields);
return 0;
}
diff --git a/sql/lex.h b/sql/lex.h
index a9e44e034d0..d9a84dd25b4 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -240,6 +240,7 @@ static SYMBOL symbols[] = {
{ "MEDIUMINT", SYM(MEDIUMINT),0,0},
{ "MERGE", SYM(MERGE_SYM),0,0},
{ "MEDIUM", SYM(MEDIUM_SYM),0,0},
+ { "MEMORY", SYM(MEMORY_SYM),0,0},
{ "MIDDLEINT", SYM(MEDIUMINT),0,0}, /* For powerbuilder */
{ "MIN_ROWS", SYM(MIN_ROWS),0,0},
{ "MINUTE", SYM(MINUTE_SYM),0,0},
diff --git a/sql/sql_test.cc b/sql/sql_test.cc
index b226bc1300a..6816bb62047 100644
--- a/sql/sql_test.cc
+++ b/sql/sql_test.cc
@@ -131,7 +131,7 @@ void TEST_filesort(SORT_FIELD *sortorder,uint s_length, ha_rows special)
DBUG_LOCK_FILE;
VOID(fputs("\nInfo about FILESORT\n",DBUG_FILE));
if (special)
- fprintf(DBUG_FILE,"Records to sort: %ld\n",special);
+ fprintf(DBUG_FILE,"Records to sort: %lu\n",(ulong) special);
fprintf(DBUG_FILE,"Sortorder: %s\n",out.ptr());
DBUG_UNLOCK_FILE;
DBUG_VOID_RETURN;
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index d8842855093..709f88726de 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -23,6 +23,8 @@
#include "sql_acl.h"
#include "sql_select.h"
+static bool safe_update_on_fly(JOIN_TAB *join_tab, List<Item> *fields);
+
/* Return 0 if row hasn't changed */
static bool compare_record(TABLE *table, ulong query_id)
@@ -520,11 +522,12 @@ int multi_update::prepare(List<Item> &not_used_values)
/*
- Store first used table in main_table as this should be updated first
- This is because we know that no row in this table will be read twice.
+ Initialize table for multi table
- Create temporary tables to store changed values for all other tables
- that are updated.
+ IMPLEMENTATION
+ - Update first table in join on the fly, if possible
+ - Create temporary tables to store changed values for all other tables
+ that are updated (and main_table if the above doesn't hold).
*/
bool
@@ -538,52 +541,113 @@ multi_update::initialize_tables(JOIN *join)
main_table=join->join_tab->table;
trans_safe= transactional_tables= main_table->file->has_transactions();
log_delayed= trans_safe || main_table->tmp_table != NO_TMP_TABLE;
- table_to_update= (main_table->file->table_flags() & HA_NOT_MULTI_UPDATE) ?
- (TABLE *) 0 : main_table;
- /* Create a temporary table for all tables after except main table */
+ table_to_update= 0;
+
+ /* Create a temporary table for keys to all tables, except main table */
for (table_ref= update_tables; table_ref; table_ref=table_ref->next)
{
TABLE *table=table_ref->table;
- if (table != table_to_update)
- {
- uint cnt= table_ref->shared;
- ORDER group;
- List<Item> temp_fields= *fields_for_table[cnt];
- TMP_TABLE_PARAM *tmp_param= tmp_table_param+cnt;
-
- /*
- Create a temporary table to store all fields that are changed for this
- table. The first field in the temporary table is a pointer to the
- original row so that we can find and update it
- */
-
- /* ok to be on stack as this is not referenced outside of this func */
- Field_string offset(table->file->ref_length, 0, "offset",
- table, 1);
- if (temp_fields.push_front(new Item_field(((Field *) &offset))))
- DBUG_RETURN(1);
+ uint cnt= table_ref->shared;
+ List<Item> temp_fields= *fields_for_table[cnt];
+ ORDER group;
- /* Make an unique key over the first field to avoid duplicated updates */
- bzero((char*) &group, sizeof(group));
- group.asc= 1;
- group.item= (Item**) temp_fields.head_ref();
-
- tmp_param->quick_group=1;
- tmp_param->field_count=temp_fields.elements;
- tmp_param->group_parts=1;
- tmp_param->group_length= table->file->ref_length;
- if (!(tmp_tables[cnt]=create_tmp_table(thd,
- tmp_param,
- temp_fields,
- (ORDER*) &group, 0, 0, 0,
- TMP_TABLE_ALL_COLUMNS)))
- DBUG_RETURN(1);
- tmp_tables[cnt]->file->extra(HA_EXTRA_WRITE_CACHE);
+ if (table == main_table) // First table in join
+ {
+ if (safe_update_on_fly(join->join_tab, &temp_fields))
+ {
+ table_to_update= main_table; // Update table on the fly
+ continue;
+ }
}
+
+ TMP_TABLE_PARAM *tmp_param= tmp_table_param+cnt;
+
+ /*
+ Create a temporary table to store all fields that are changed for this
+ table. The first field in the temporary table is a pointer to the
+ original row so that we can find and update it
+ */
+
+ /* ok to be on stack as this is not referenced outside of this func */
+ Field_string offset(table->file->ref_length, 0, "offset",
+ table, 1);
+ if (temp_fields.push_front(new Item_field(((Field *) &offset))))
+ DBUG_RETURN(1);
+
+ /* Make an unique key over the first field to avoid duplicated updates */
+ bzero((char*) &group, sizeof(group));
+ group.asc= 1;
+ group.item= (Item**) temp_fields.head_ref();
+
+ tmp_param->quick_group=1;
+ tmp_param->field_count=temp_fields.elements;
+ tmp_param->group_parts=1;
+ tmp_param->group_length= table->file->ref_length;
+ if (!(tmp_tables[cnt]=create_tmp_table(thd,
+ tmp_param,
+ temp_fields,
+ (ORDER*) &group, 0, 0, 0,
+ TMP_TABLE_ALL_COLUMNS)))
+ DBUG_RETURN(1);
+ tmp_tables[cnt]->file->extra(HA_EXTRA_WRITE_CACHE);
}
DBUG_RETURN(0);
}
+/*
+ Check if table is safe to update on fly
+
+ SYNOPSIS
+ safe_update_on_fly
+ join_tab How table is used in join
+ fields Fields that are updated
+
+ NOTES
+ We can update the first table in join on the fly if we know that
+ a row in this tabel will never be read twice. This is true under
+ the folloing conditions:
+
+ - We are doing a table scan and the data is in a separate file (MyISAM) or
+ if we don't update a clustered key.
+
+ - We are doing a range scan and we don't update the scan key or
+ the primary key for a clustered table handler.
+
+ WARNING
+ This code is a bit dependent of how make_join_readinfo() works.
+
+ RETURN
+ 0 Not safe to update
+ 1 Safe to update
+*/
+
+static bool safe_update_on_fly(JOIN_TAB *join_tab, List<Item> *fields)
+{
+ TABLE *table= join_tab->table;
+ switch (join_tab->type) {
+ case JT_SYSTEM:
+ case JT_CONST:
+ case JT_EQ_REF:
+ return 1; // At most one matching row
+ case JT_REF:
+ return !check_if_key_used(table, join_tab->ref.key, *fields);
+ case JT_ALL:
+ /* If range search on index */
+ if (join_tab->quick)
+ return !check_if_key_used(table, join_tab->quick->index,
+ *fields);
+ /* If scanning in clustered key */
+ if ((table->file->table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX) &&
+ table->primary_key < MAX_KEY)
+ return !check_if_key_used(table, table->primary_key, *fields);
+ return 1;
+ default:
+ break; // Avoid compler warning
+ }
+ return 0;
+}
+
+
multi_update::~multi_update()
{
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 2f339f30eb4..b986b72df32 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -256,6 +256,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token MAX_UPDATES_PER_HOUR
%token MEDIUM_SYM
%token MERGE_SYM
+%token MEMORY_SYM
%token MIN_ROWS
%token MYISAM_SYM
%token NATIONAL_SYM
@@ -871,6 +872,7 @@ table_types:
| MYISAM_SYM { $$= DB_TYPE_MYISAM; }
| MERGE_SYM { $$= DB_TYPE_MRG_MYISAM; }
| HEAP_SYM { $$= DB_TYPE_HEAP; }
+ | MEMORY_SYM { $$= DB_TYPE_HEAP; }
| BERKELEY_DB_SYM { $$= DB_TYPE_BERKELEY_DB; }
| INNOBASE_SYM { $$= DB_TYPE_INNODB; };
@@ -3316,6 +3318,7 @@ keyword:
| MAX_UPDATES_PER_HOUR {}
| MEDIUM_SYM {}
| MERGE_SYM {}
+ | MEMORY_SYM {}
| MINUTE_SYM {}
| MIN_ROWS {}
| MODIFY_SYM {}