List:Commits« Previous MessageNext Message »
From:mhansson Date:March 6 2007 1:44pm
Subject:bk commit into 5.1 tree (mhansson:1.2409) BUG#24778
View as plain text  
Below is the list of changes that have just been committed into a local
5.1 repository of martin. When martin does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html

ChangeSet@stripped, 2007-03-06 13:44:28+01:00, mhansson@stripped +4 -0
  Bug #24778: Innodb: No result when using ORDER BY
  
  This bug appears only in the case when the optimizer has chosen an index
  for accessing a particular table but finds a covering index that enables
  it to skip ORDER BY. This happens in test_if_skip_sort_order.
  
  The bug is in the assumption that: If we change the index for accessing
  a table in order to skip ORDER BY for a given plan operator, this plan 
  operator must be for the first non-const table in the plan. This is not 
  true: There can be other tables preceding it and the test case is an
  example of that. What happens is that
  create_ref_for_key is called with the set of const tables, in this case 
  the empty set. So here no key gets chosen, and we get the empty set.
  
  We should have to find all tables preceding the plan operator at hand 
  in this case. On the other hand, since we know that create_ref_for_key
  picked a key once, and we will simply swap it for another key for the same
  plan operator, it is enough to send in the set of just the previous table.
  It follows from the above reasoing that tab->key_dependent can be used,
  which is precomputed.

  mysql-test/r/key.result@stripped, 2007-03-06 13:44:21+01:00, mhansson@stripped +41 -0
    Bug#24791
    
    test case.
    
    The bug causes the result to be the empty set.

  mysql-test/t/key.test@stripped, 2007-03-06 13:44:21+01:00, mhansson@stripped +47 -0
    Bug#24791
    
    The minimal test case that reveals the bug. The reason for such a 
    complicated schema is that we have to convince the optimizer to pick one
    index, then discard it in order to be able to skip ORDER BY.

  sql/sql_select.cc@stripped, 2007-03-06 13:44:21+01:00, mhansson@stripped +36 -10
    Bug#24791
    
    In order of appearance:
    
    - Added documantation about key_dependent to add_key_field.
    
    - Aligned comments to the same column
    
    - Corrected typo
    
    - completed comments to test_if_subkey
    
    - The change that fixes the bug. See cset comment

  sql/sql_select.h@stripped, 2007-03-06 13:44:21+01:00, mhansson@stripped +23 -5
    Bug#24791
    
    In order of appearance:
    
    - I spent a long time to find out the semantics of JOIN_TAB::key_dependent
    so I hope this comment will save time for anyone else who needs to use it.
    
    - Added comment above the field JOIN::tables

# This is a BitKeeper patch.  What follows are the unified diffs for the
# set of deltas contained in the patch.  The rest of the patch, the part
# that BitKeeper cares about, is below these diffs.
# User:	mhansson
# Host:	linux-st28.site
# Root:	/home/martin/mysql/src/5.1o-bug24778

--- 1.485/sql/sql_select.cc	2007-01-25 00:30:31 +01:00
+++ 1.486/sql/sql_select.cc	2007-03-06 13:44:21 +01:00
@@ -2746,6 +2746,9 @@ merge_key_fields(KEY_FIELD *start,KEY_FI
     If we are doing a NOT NULL comparison on a NOT NULL field in a outer join
     table, we store this to be able to do not exists optimization later.
 
+    This function updates key_dependent for the field's JOIN_TAB by looping 
+    through the items in 'value' and collecting all tables.
+
   RETURN
     *key_fields is incremented if we stored a key in the array
 */
@@ -2799,8 +2802,8 @@ add_key_field(KEY_FIELD **key_fields,uin
 	Field op formula
 	Field IS NULL
 	Field IS NOT NULL
-         Field BETWEEN ...
-         Field IN ...
+	Field BETWEEN ...
+	Field IN ...
       */
       stat[0].key_dependent|=used_tables;
 
@@ -8272,7 +8275,7 @@ static void reset_nj_counters(List<TABLE
 
 
 /*
-  Check interleaving with an inner tables of an outer join for extension table 
+  Check interleaving with an inner table of an outer join for extension table 
 
   SYNOPSIS
     check_interleaving_with_nj()
@@ -12066,16 +12069,33 @@ is_subkey(KEY_PART_INFO *key_part, KEY_P
 }
 
 /*
-  Test if we can use one of the 'usable_keys' instead of 'ref' key for sorting
+  Test if we can use one of the 'usable_keys' instead of 'ref' key for sorting.
+  The function searches the shortest key that is sorted according to 'order'
+  and also covers 'ref', i.e. it consists of the same or more keyparts.
+  'ref' is typically used for ref access to the table.
 
   SYNOPSIS
     test_if_subkey()
-    ref			Number of key, used for WHERE clause
-    usable_keys		Keys for testing
+    order               Only keys sorted accordingly are considered
+    table               The table whose usable keys we scan through
+    ref                 Ordinal number of key, used for WHERE clause
+    ref_key_parts       Number of keyparts that make up the key
+    usable_keys         Keys that may be used
 
   RETURN
-    MAX_KEY			If we can't use other key
-    the number of found key	Otherwise
+    MAX_KEY			If we can't use any other key in usable_keys
+    the ordinal of found key	Otherwise
+
+  IMPLEMENTATION
+    Scans through all usable keys for 'table' and selects the one that:
+    
+      - Has the shortest key_length
+      - Has the greatest number of key parts.
+      - Is a superkey of 'ref'
+      - Is ordered in the same way
+
+    If several keys are eligible, the last one in table->key_info will be 
+    chosen.
 */
 
 static uint
@@ -12345,8 +12365,14 @@ test_if_skip_sort_order(JOIN_TAB *tab,OR
           KEYUSE *keyuse= tab->keyuse;
           while (keyuse->key != new_ref_key && keyuse->table ==
tab->table)
             keyuse++;
-          if (create_ref_for_key(tab->join, tab, keyuse, 
-                                 tab->join->const_table_map))
+	  /*
+	    We will now create the ref access method for the new key,
+	    and we know that another key was being used until now, so
+	    we know that key will be in the set tab->key_dependent. Therefore 
+	    we don't need to send the whole set of previous tables in the plan 
+	    to create_ref_for_key, as we would normally have to.
+	  */
+          if (create_ref_for_key(tab->join, tab, keyuse, tab->key_dependent))
             DBUG_RETURN(0);
 	}
 	else

--- 1.117/sql/sql_select.h	2007-01-24 20:24:50 +01:00
+++ 1.118/sql/sql_select.h	2007-03-06 13:44:21 +01:00
@@ -180,7 +180,22 @@ typedef struct st_join_table {
   */
   ha_rows       read_time;
   
-  table_map	dependent,key_dependent;
+  table_map	dependent;
+
+  /*
+    This member has a different purpose for outer and inner joins.
+
+    For outer joins:
+      This member echoes 'JOIN_TAB::dependent', i.e. they are the same. This is
+      initialized in make_join_statistics
+      
+    For inner joins:
+      The set of all tables that access to this table depends upon, such that 
+      there is a key that can be used for accessing this table. In other words, 
+      this is not the set of *all* tables whose access must precede access to 
+      this table.
+   */
+  table_map	key_dependent;
   uint		use_quick,index;
   uint		status;				// Save status for cache
   uint		used_fields,used_fieldlength,used_blobs;
@@ -258,10 +273,13 @@ public:
   JOIN_TAB **map2table;    // mapping between table indexes and JOIN_TABs
   JOIN_TAB *join_tab_save; // saved join_tab for subquery reexecution
   TABLE    **table,**all_tables,*sort_by_table;
-  uint	   tables,const_tables;
-  uint	   send_group_parts;
-  bool	   sort_and_group,first_record,full_join,group, no_field_update;
-  bool	   do_send_rows;
+
+  /* Total number of tables in the query */
+  uint     tables;
+  uint     const_tables;
+  uint     send_group_parts;
+  bool     sort_and_group, first_record, full_join,group, no_field_update;
+  bool     do_send_rows;
   /*
     TRUE when we want to resume nested loop iterations when
     fetching data from a cursor

--- 1.40/mysql-test/r/key.result	2006-08-11 23:06:20 +02:00
+++ 1.41/mysql-test/r/key.result	2007-03-06 13:44:21 +01:00
@@ -482,3 +482,44 @@ alter table t1 drop index i3, drop index
 alter table t1 add index i3 (c3), add index i2 (c2), add unique index i1 (c1);
 ERROR 23000: Duplicate entry '1' for key 'i1'
 drop table t1;
+CREATE TABLE t1 (
+a INTEGER auto_increment PRIMARY KEY,
+b INTEGER NOT NULL,
+c INTEGER NOT NULL,
+d CHAR(64)
+);
+CREATE TABLE t2 (
+a INTEGER auto_increment PRIMARY KEY,
+b INTEGER NOT NULL,
+c SMALLINT NOT NULL,
+d DATETIME NOT NULL,
+e SMALLINT NOT NULL,
+f INTEGER NOT NULL,
+g INTEGER NOT NULL,  
+h SMALLINT NOT NULL,
+i INTEGER NOT NULL,
+j INTEGER NOT NULL,
+UNIQUE INDEX (b),
+INDEX (b, d, e, f, g, h, i, j, c),
+INDEX (c)
+);
+INSERT INTO t2 VALUES 
+(NULL, 1, 254, '1000-01-01 00:00:00', 257, 0, 0, 0, 0, 0),
+(NULL, 2, 1, '2004-11-30 12:00:00', 1, 0, 0, 0, 0, 0),
+(NULL, 3, 1, '2004-11-30 12:00:00', 1, 0, 0, 2, -21600, 0),
+(NULL, 4, 1, '2004-11-30 12:00:00', 1, 0, 0, 2, -10800, 0),
+(NULL, 5, 1, '2004-11-30 12:00:00', 1, 0, 0, 5, -10800, 0),
+(NULL, 6, 1, '2004-11-30 12:00:00', 102, 0, 0, 0, 0, 0),
+(NULL, 7, 1, '2004-11-30 12:00:00', 105, 2, 0, 0, 0, 0),
+(NULL, 8, 1, '2004-11-30 12:00:00', 105, 10, 0, 0, 0, 0);
+INSERT INTO t1 (b, c, d) VALUES
+(3388000, -553000, NULL),
+(3388000, -553000, NULL);
+SELECT *
+FROM t2 c JOIN t1 pa ON c.b = pa.a 
+WHERE c.c = 1
+ORDER BY c.b, c.d
+;
+a	b	c	d	e	f	g	h	i	j	a	b	c	d
+2	2	1	2004-11-30 12:00:00	1	0	0	0	0	0	2	3388000	-553000	NULL
+DROP TABLE t1, t2;

--- 1.31/mysql-test/t/key.test	2006-07-20 20:41:52 +02:00
+++ 1.32/mysql-test/t/key.test	2007-03-06 13:44:21 +01:00
@@ -442,3 +442,50 @@ alter table t1 drop index i3, drop index
 alter table t1 add index i3 (c3), add index i2 (c2), add unique index i1 (c1);
 drop table t1;
 
+#
+# Bug #24778  	Innodb: No result when using ORDER BY
+#
+CREATE TABLE t1 (
+  a INTEGER auto_increment PRIMARY KEY,
+  b INTEGER NOT NULL,
+  c INTEGER NOT NULL,
+  d CHAR(64)
+);
+
+CREATE TABLE t2 (
+  a INTEGER auto_increment PRIMARY KEY,
+  b INTEGER NOT NULL,
+  c SMALLINT NOT NULL,
+  d DATETIME NOT NULL,
+  e SMALLINT NOT NULL,
+  f INTEGER NOT NULL,
+  g INTEGER NOT NULL,  
+  h SMALLINT NOT NULL,
+  i INTEGER NOT NULL,
+  j INTEGER NOT NULL,
+  UNIQUE INDEX (b),
+  INDEX (b, d, e, f, g, h, i, j, c),
+  INDEX (c)
+);
+
+INSERT INTO t2 VALUES 
+  (NULL, 1, 254, '1000-01-01 00:00:00', 257, 0, 0, 0, 0, 0),
+  (NULL, 2, 1, '2004-11-30 12:00:00', 1, 0, 0, 0, 0, 0),
+  (NULL, 3, 1, '2004-11-30 12:00:00', 1, 0, 0, 2, -21600, 0),
+  (NULL, 4, 1, '2004-11-30 12:00:00', 1, 0, 0, 2, -10800, 0),
+  (NULL, 5, 1, '2004-11-30 12:00:00', 1, 0, 0, 5, -10800, 0),
+  (NULL, 6, 1, '2004-11-30 12:00:00', 102, 0, 0, 0, 0, 0),
+  (NULL, 7, 1, '2004-11-30 12:00:00', 105, 2, 0, 0, 0, 0),
+  (NULL, 8, 1, '2004-11-30 12:00:00', 105, 10, 0, 0, 0, 0);
+
+INSERT INTO t1 (b, c, d) VALUES
+  (3388000, -553000, NULL),
+  (3388000, -553000, NULL);
+
+SELECT *
+FROM t2 c JOIN t1 pa ON c.b = pa.a 
+WHERE c.c = 1
+ORDER BY c.b, c.d
+;
+
+DROP TABLE t1, t2;
Thread
bk commit into 5.1 tree (mhansson:1.2409) BUG#24778mhansson6 Mar