mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
Add support for NULL=NULL in keys (Used in GROUP BY optimization)
Add ISAM to Windows version Fix of test results Fixes for NULL keys in HEAP tables. Docs/manual.texi: Changelog heap/hp_open.c: Add support for NULL=NULL in keys (for GROUP BY) heap/hp_rkey.c: Cleanup heap/hp_write.c: Cleanup include/config-win.h: Add ISAM to Windows version include/my_base.h: Add support for NULL=NULL in keys (for GROUP BY) libmysqld/Makefile.am: Rename of innobase to innodb myisam/mi_write.c: Add support for NULL=NULL in keys (for GROUP BY) BitKeeper/etc/ignore: Added libmysqld/ha_innodb.cc to the ignore list mysql-test/r/group_by.result: Test of NULL keys in HEAP tables mysql-test/r/heap.result: Test of NULL keys in HEAP tables mysql-test/r/null.result: Cleanup mysql-test/r/order_by.result: Fix for result of new ORDER BY optimization mysql-test/t/group_by.test: Test of NULL keys in HEAP tables mysql-test/t/heap.test: Test of NULL keys in HEAP tables mysql-test/t/null.test: Cleanup sql/ha_heap.cc: Add support of NULL keys sql/item_strfunc.h: Fix for BINARY and CAST functions sql/item_timefunc.h: Fix for BINARY and CAST functions sql/sql_parse.cc: Cleanup sql/sql_select.cc: Add support for NULL=NULL in keys (for GROUP BY)
This commit is contained in:
@ -451,3 +451,4 @@ vio/test-ssl
|
|||||||
vio/test-sslclient
|
vio/test-sslclient
|
||||||
vio/test-sslserver
|
vio/test-sslserver
|
||||||
vio/viotest-ssl
|
vio/viotest-ssl
|
||||||
|
libmysqld/ha_innodb.cc
|
||||||
|
@ -48121,10 +48121,12 @@ Our TODO section contains what we plan to have in 4.0. @xref{TODO MySQL 4.0}.
|
|||||||
|
|
||||||
@itemize @bullet
|
@itemize @bullet
|
||||||
@item
|
@item
|
||||||
|
Fixed bug in @code{GROUP BY BINARY column}
|
||||||
|
@item
|
||||||
Added support for @code{NULL} keys in HEAP tables.
|
Added support for @code{NULL} keys in HEAP tables.
|
||||||
@item
|
@item
|
||||||
Use index for @code{ORDER BY} in queries of type:
|
Use index for @code{ORDER BY} in queries of type:
|
||||||
@code{SELECT * FROM t1 WHERE key_part1=1 ORDER BY key_part1 DESC,key_part2 DESC}
|
@code{SELECT * FROM t WHERE key_part1=1 ORDER BY key_part1 DESC,key_part2 DESC}
|
||||||
@item
|
@item
|
||||||
Fixed bug in @code{FLUSH QUERY CACHE}.
|
Fixed bug in @code{FLUSH QUERY CACHE}.
|
||||||
@item
|
@item
|
||||||
|
@ -46,7 +46,8 @@ HP_INFO *heap_open(const char *name, int mode, uint keys, HP_KEYDEF *keydef,
|
|||||||
for (j=length=0 ; j < keydef[i].keysegs; j++)
|
for (j=length=0 ; j < keydef[i].keysegs; j++)
|
||||||
{
|
{
|
||||||
length+=keydef[i].seg[j].length;
|
length+=keydef[i].seg[j].length;
|
||||||
if (keydef[i].seg[j].null_bit)
|
if (keydef[i].seg[j].null_bit &&
|
||||||
|
!(keydef[i].flag & HA_NULL_ARE_EQUAL))
|
||||||
keydef[i].flag |= HA_NULL_PART_KEY;
|
keydef[i].flag |= HA_NULL_PART_KEY;
|
||||||
}
|
}
|
||||||
keydef[i].length=length;
|
keydef[i].length=length;
|
||||||
|
@ -20,7 +20,7 @@ int heap_rkey(HP_INFO *info, byte *record, int inx, const byte *key)
|
|||||||
{
|
{
|
||||||
byte *pos;
|
byte *pos;
|
||||||
HP_SHARE *share=info->s;
|
HP_SHARE *share=info->s;
|
||||||
DBUG_ENTER("hp_rkey");
|
DBUG_ENTER("heap_rkey");
|
||||||
DBUG_PRINT("enter",("base: %lx inx: %d",info,inx));
|
DBUG_PRINT("enter",("base: %lx inx: %d",info,inx));
|
||||||
|
|
||||||
if ((uint) inx >= share->keys)
|
if ((uint) inx >= share->keys)
|
||||||
|
@ -238,7 +238,7 @@ int _hp_write_key(register HP_SHARE *info, HP_KEYDEF *keyinfo,
|
|||||||
_hp_movelink(pos,gpos,empty);
|
_hp_movelink(pos,gpos,empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check if dupplicated keys */
|
/* Check if duplicated keys */
|
||||||
if ((keyinfo->flag & HA_NOSAME) && pos == gpos &&
|
if ((keyinfo->flag & HA_NOSAME) && pos == gpos &&
|
||||||
(!(keyinfo->flag & HA_NULL_PART_KEY) ||
|
(!(keyinfo->flag & HA_NULL_PART_KEY) ||
|
||||||
!hp_if_null_in_key(keyinfo, record)))
|
!hp_if_null_in_key(keyinfo, record)))
|
||||||
|
@ -255,6 +255,8 @@ inline double ulonglong2double(ulonglong value)
|
|||||||
#define HAVE_COMPRESS
|
#define HAVE_COMPRESS
|
||||||
#define HAVE_CREATESEMAPHORE
|
#define HAVE_CREATESEMAPHORE
|
||||||
|
|
||||||
|
#define HAVE_ISAM /* We want to have support for ISAM in 4.0 */
|
||||||
|
|
||||||
#ifdef NOT_USED
|
#ifdef NOT_USED
|
||||||
#define HAVE_SNPRINTF /* Gave link error */
|
#define HAVE_SNPRINTF /* Gave link error */
|
||||||
#define _snprintf snprintf
|
#define _snprintf snprintf
|
||||||
|
@ -150,6 +150,7 @@ enum ha_base_keytype {
|
|||||||
#define HA_FULLTEXT 128 /* SerG: for full-text search */
|
#define HA_FULLTEXT 128 /* SerG: for full-text search */
|
||||||
#define HA_UNIQUE_CHECK 256 /* Check the key for uniqueness */
|
#define HA_UNIQUE_CHECK 256 /* Check the key for uniqueness */
|
||||||
#define HA_SPATIAL 1024 /* Alex Barkov: for spatial search */
|
#define HA_SPATIAL 1024 /* Alex Barkov: for spatial search */
|
||||||
|
#define HA_NULL_ARE_EQUAL 2048 /* NULL in key are cmp as equal */
|
||||||
|
|
||||||
|
|
||||||
/* Automatic bits in key-flag */
|
/* Automatic bits in key-flag */
|
||||||
@ -260,6 +261,7 @@ enum ha_base_keytype {
|
|||||||
#define MBR_DISJOINT 4096
|
#define MBR_DISJOINT 4096
|
||||||
#define MBR_EQUAL 8192
|
#define MBR_EQUAL 8192
|
||||||
#define MBR_DATA 16384
|
#define MBR_DATA 16384
|
||||||
|
#define SEARCH_NULL_ARE_EQUAL 32768 /* NULL in keys are equal */
|
||||||
|
|
||||||
/* bits in opt_flag */
|
/* bits in opt_flag */
|
||||||
#define QUICK_USED 1
|
#define QUICK_USED 1
|
||||||
|
@ -38,7 +38,7 @@ libmysqlsources = errmsg.c get_password.c password.c
|
|||||||
noinst_HEADERS = embedded_priv.h
|
noinst_HEADERS = embedded_priv.h
|
||||||
|
|
||||||
sqlsources = convert.cc derror.cc field.cc field_conv.cc filesort.cc \
|
sqlsources = convert.cc derror.cc field.cc field_conv.cc filesort.cc \
|
||||||
ha_innobase.cc ha_berkeley.cc ha_heap.cc ha_isam.cc ha_isammrg.cc \
|
ha_innodb.cc ha_berkeley.cc ha_heap.cc ha_isam.cc ha_isammrg.cc \
|
||||||
ha_myisam.cc ha_myisammrg.cc handler.cc sql_handler.cc \
|
ha_myisam.cc ha_myisammrg.cc handler.cc sql_handler.cc \
|
||||||
hostname.cc init.cc \
|
hostname.cc init.cc \
|
||||||
item.cc item_buff.cc item_cmpfunc.cc item_create.cc \
|
item.cc item_buff.cc item_cmpfunc.cc item_create.cc \
|
||||||
|
@ -25,7 +25,8 @@
|
|||||||
|
|
||||||
/* Functions declared in this file */
|
/* Functions declared in this file */
|
||||||
|
|
||||||
static int w_search(MI_INFO *info,MI_KEYDEF *keyinfo,uchar *key,
|
static int w_search(MI_INFO *info,MI_KEYDEF *keyinfo,
|
||||||
|
uint comp_flag, uchar *key,
|
||||||
uint key_length, my_off_t pos, uchar *father_buff,
|
uint key_length, my_off_t pos, uchar *father_buff,
|
||||||
uchar *father_keypos, my_off_t father_page,
|
uchar *father_keypos, my_off_t father_page,
|
||||||
my_bool insert_last);
|
my_bool insert_last);
|
||||||
@ -245,10 +246,23 @@ int _mi_ck_write_btree(register MI_INFO *info, uint keynr, uchar *key,
|
|||||||
uint key_length)
|
uint key_length)
|
||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
|
uint comp_flag;
|
||||||
|
MI_KEYDEF *keyinfo=info->s->keyinfo+keynr;
|
||||||
DBUG_ENTER("_mi_ck_write_btree");
|
DBUG_ENTER("_mi_ck_write_btree");
|
||||||
|
|
||||||
|
if (keyinfo->flag & HA_SORT_ALLOWS_SAME)
|
||||||
|
comp_flag=SEARCH_BIGGER; /* Put after same key */
|
||||||
|
else if (keyinfo->flag & HA_NOSAME)
|
||||||
|
{
|
||||||
|
comp_flag=SEARCH_FIND | SEARCH_UPDATE; /* No dupplicates */
|
||||||
|
if (keyinfo->flag & HA_NULL_ARE_EQUAL)
|
||||||
|
comp_flag|= SEARCH_NULL_ARE_EQUAL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
comp_flag=SEARCH_SAME; /* Keys in rec-pos order */
|
||||||
|
|
||||||
if (info->s->state.key_root[keynr] == HA_OFFSET_ERROR ||
|
if (info->s->state.key_root[keynr] == HA_OFFSET_ERROR ||
|
||||||
(error=w_search(info,info->s->keyinfo+keynr,key, key_length,
|
(error=w_search(info, keyinfo, comp_flag, key, key_length,
|
||||||
info->s->state.key_root[keynr], (uchar *) 0, (uchar*) 0,
|
info->s->state.key_root[keynr], (uchar *) 0, (uchar*) 0,
|
||||||
(my_off_t) 0, 1)) > 0)
|
(my_off_t) 0, 1)) > 0)
|
||||||
error=_mi_enlarge_root(info,keynr,key);
|
error=_mi_enlarge_root(info,keynr,key);
|
||||||
@ -291,13 +305,12 @@ int _mi_enlarge_root(register MI_INFO *info, uint keynr, uchar *key)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
static int w_search(register MI_INFO *info, register MI_KEYDEF *keyinfo,
|
static int w_search(register MI_INFO *info, register MI_KEYDEF *keyinfo,
|
||||||
uchar *key, uint key_length, my_off_t page,
|
uint comp_flag, uchar *key, uint key_length, my_off_t page,
|
||||||
uchar *father_buff,
|
uchar *father_buff, uchar *father_keypos,
|
||||||
uchar *father_keypos, my_off_t father_page,
|
my_off_t father_page, my_bool insert_last)
|
||||||
my_bool insert_last)
|
|
||||||
{
|
{
|
||||||
int error,flag;
|
int error,flag;
|
||||||
uint comp_flag,nod_flag, search_key_length;
|
uint nod_flag, search_key_length;
|
||||||
uchar *temp_buff,*keypos;
|
uchar *temp_buff,*keypos;
|
||||||
uchar keybuff[MI_MAX_KEY_BUFF];
|
uchar keybuff[MI_MAX_KEY_BUFF];
|
||||||
my_bool was_last_key;
|
my_bool was_last_key;
|
||||||
@ -305,17 +318,7 @@ static int w_search(register MI_INFO *info, register MI_KEYDEF *keyinfo,
|
|||||||
DBUG_ENTER("w_search");
|
DBUG_ENTER("w_search");
|
||||||
DBUG_PRINT("enter",("page: %ld",page));
|
DBUG_PRINT("enter",("page: %ld",page));
|
||||||
|
|
||||||
search_key_length=USE_WHOLE_KEY;
|
search_key_length= (comp_flag & SEARCH_FIND) ? key_length : USE_WHOLE_KEY;
|
||||||
if (keyinfo->flag & HA_SORT_ALLOWS_SAME)
|
|
||||||
comp_flag=SEARCH_BIGGER; /* Put after same key */
|
|
||||||
else if (keyinfo->flag & HA_NOSAME)
|
|
||||||
{
|
|
||||||
comp_flag=SEARCH_FIND | SEARCH_UPDATE; /* No dupplicates */
|
|
||||||
search_key_length= key_length;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
comp_flag=SEARCH_SAME; /* Keys in rec-pos order */
|
|
||||||
|
|
||||||
if (!(temp_buff= (uchar*) my_alloca((uint) keyinfo->block_length+
|
if (!(temp_buff= (uchar*) my_alloca((uint) keyinfo->block_length+
|
||||||
MI_MAX_KEY_BUFF*2)))
|
MI_MAX_KEY_BUFF*2)))
|
||||||
DBUG_RETURN(-1);
|
DBUG_RETURN(-1);
|
||||||
@ -344,7 +347,7 @@ static int w_search(register MI_INFO *info, register MI_KEYDEF *keyinfo,
|
|||||||
insert_last=0;
|
insert_last=0;
|
||||||
next_page=_mi_kpos(nod_flag,keypos);
|
next_page=_mi_kpos(nod_flag,keypos);
|
||||||
if (next_page == HA_OFFSET_ERROR ||
|
if (next_page == HA_OFFSET_ERROR ||
|
||||||
(error=w_search(info,keyinfo,key,key_length,next_page,
|
(error=w_search(info, keyinfo, comp_flag, key, key_length, next_page,
|
||||||
temp_buff, keypos, page, insert_last)) >0)
|
temp_buff, keypos, page, insert_last)) >0)
|
||||||
{
|
{
|
||||||
error=_mi_insert(info,keyinfo,key,temp_buff,keypos,keybuff,father_buff,
|
error=_mi_insert(info,keyinfo,key,temp_buff,keypos,keybuff,father_buff,
|
||||||
@ -759,15 +762,17 @@ static int keys_compare(bulk_insert_param *param, uchar *key1, uchar *key2)
|
|||||||
{
|
{
|
||||||
uint not_used;
|
uint not_used;
|
||||||
return _mi_key_cmp(param->info->s->keyinfo[param->keynr].seg,
|
return _mi_key_cmp(param->info->s->keyinfo[param->keynr].seg,
|
||||||
key1, key2, USE_WHOLE_KEY, SEARCH_SAME, ¬_used);
|
key1, key2, USE_WHOLE_KEY, SEARCH_SAME,
|
||||||
|
¬_used);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int keys_free(uchar *key, TREE_FREE mode, bulk_insert_param *param)
|
static int keys_free(uchar *key, TREE_FREE mode, bulk_insert_param *param)
|
||||||
{
|
{
|
||||||
/* probably I can use info->lastkey here, but I'm not sure,
|
/*
|
||||||
|
Probably I can use info->lastkey here, but I'm not sure,
|
||||||
and to be safe I'd better use local lastkey.
|
and to be safe I'd better use local lastkey.
|
||||||
Monty, feel free to comment on this */
|
*/
|
||||||
uchar lastkey[MI_MAX_KEY_BUFF];
|
uchar lastkey[MI_MAX_KEY_BUFF];
|
||||||
uint keylen;
|
uint keylen;
|
||||||
MI_KEYDEF *keyinfo;
|
MI_KEYDEF *keyinfo;
|
||||||
@ -794,6 +799,7 @@ static int keys_free(uchar *key, TREE_FREE mode, bulk_insert_param *param)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int _mi_init_bulk_insert(MI_INFO *info)
|
int _mi_init_bulk_insert(MI_INFO *info)
|
||||||
{
|
{
|
||||||
MYISAM_SHARE *share=info->s;
|
MYISAM_SHARE *share=info->s;
|
||||||
|
@ -263,3 +263,84 @@ score count(*)
|
|||||||
2 1
|
2 1
|
||||||
1 2
|
1 2
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
CREATE TABLE t1 (a char(1));
|
||||||
|
INSERT INTO t1 VALUES ('A'),('B'),('A'),('B'),('A'),('B'),(NULL),('a'),('b'),(NULL),('A'),('B'),(NULL);
|
||||||
|
SELECT a FROM t1 GROUP BY a;
|
||||||
|
a
|
||||||
|
NULL
|
||||||
|
A
|
||||||
|
B
|
||||||
|
SELECT a,count(*) FROM t1 GROUP BY a;
|
||||||
|
a count(*)
|
||||||
|
NULL 3
|
||||||
|
A 5
|
||||||
|
B 5
|
||||||
|
SELECT a FROM t1 GROUP BY binary a;
|
||||||
|
a
|
||||||
|
NULL
|
||||||
|
A
|
||||||
|
B
|
||||||
|
a
|
||||||
|
b
|
||||||
|
SELECT a,count(*) FROM t1 GROUP BY binary a;
|
||||||
|
a count(*)
|
||||||
|
NULL 3
|
||||||
|
A 4
|
||||||
|
B 4
|
||||||
|
a 1
|
||||||
|
b 1
|
||||||
|
SELECT binary a FROM t1 GROUP BY 1;
|
||||||
|
binary a
|
||||||
|
NULL
|
||||||
|
A
|
||||||
|
B
|
||||||
|
a
|
||||||
|
b
|
||||||
|
SELECT binary a,count(*) FROM t1 GROUP BY 1;
|
||||||
|
binary a count(*)
|
||||||
|
NULL 3
|
||||||
|
A 4
|
||||||
|
B 4
|
||||||
|
a 1
|
||||||
|
b 1
|
||||||
|
SET SQL_BIG_TABLES=1;
|
||||||
|
SELECT a FROM t1 GROUP BY a;
|
||||||
|
a
|
||||||
|
NULL
|
||||||
|
A
|
||||||
|
B
|
||||||
|
SELECT a,count(*) FROM t1 GROUP BY a;
|
||||||
|
a count(*)
|
||||||
|
NULL 3
|
||||||
|
A 5
|
||||||
|
B 5
|
||||||
|
SELECT a FROM t1 GROUP BY binary a;
|
||||||
|
a
|
||||||
|
NULL
|
||||||
|
A
|
||||||
|
B
|
||||||
|
a
|
||||||
|
b
|
||||||
|
SELECT a,count(*) FROM t1 GROUP BY binary a;
|
||||||
|
a count(*)
|
||||||
|
NULL 3
|
||||||
|
A 4
|
||||||
|
B 4
|
||||||
|
a 1
|
||||||
|
b 1
|
||||||
|
SELECT binary a FROM t1 GROUP BY 1;
|
||||||
|
binary a
|
||||||
|
NULL
|
||||||
|
A
|
||||||
|
B
|
||||||
|
a
|
||||||
|
b
|
||||||
|
SELECT binary a,count(*) FROM t1 GROUP BY 1;
|
||||||
|
binary a count(*)
|
||||||
|
NULL 3
|
||||||
|
A 4
|
||||||
|
B 4
|
||||||
|
a 1
|
||||||
|
b 1
|
||||||
|
SET SQL_BIG_TABLES=0;
|
||||||
|
drop table t1;
|
||||||
|
@ -165,3 +165,29 @@ explain select * from t1 where btn="a" and new_col="a";
|
|||||||
table type possible_keys key key_len ref rows Extra
|
table type possible_keys key key_len ref rows Extra
|
||||||
t1 ref btn btn 11 const,const 10 where used
|
t1 ref btn btn 11 const,const 10 where used
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
CREATE TABLE t1 (
|
||||||
|
a int default NULL,
|
||||||
|
b int default NULL,
|
||||||
|
KEY a (a),
|
||||||
|
UNIQUE b (b)
|
||||||
|
) type=heap;
|
||||||
|
INSERT INTO t1 VALUES (NULL,99),(99,NULL),(1,1),(2,2),(1,3);
|
||||||
|
SELECT * FROM t1 WHERE a=NULL;
|
||||||
|
a b
|
||||||
|
explain SELECT * FROM t1 WHERE a IS NULL;
|
||||||
|
table type possible_keys key key_len ref rows Extra
|
||||||
|
t1 ref a a 5 const 10 where used
|
||||||
|
SELECT * FROM t1 WHERE a<=>NULL;
|
||||||
|
a b
|
||||||
|
NULL 99
|
||||||
|
SELECT * FROM t1 WHERE b=NULL;
|
||||||
|
a b
|
||||||
|
explain SELECT * FROM t1 WHERE b IS NULL;
|
||||||
|
table type possible_keys key key_len ref rows Extra
|
||||||
|
t1 ref b b 5 const 1 where used
|
||||||
|
SELECT * FROM t1 WHERE b<=>NULL;
|
||||||
|
a b
|
||||||
|
99 NULL
|
||||||
|
INSERT INTO t1 VALUES (1,3);
|
||||||
|
Duplicate entry '3' for key 1
|
||||||
|
DROP TABLE t1;
|
||||||
|
@ -40,7 +40,6 @@ insert into t1 values (null);
|
|||||||
select * from t1 where x != 0;
|
select * from t1 where x != 0;
|
||||||
x
|
x
|
||||||
drop table t1;
|
drop table t1;
|
||||||
DROP TABLE IF EXISTS t1;
|
|
||||||
CREATE TABLE t1 (
|
CREATE TABLE t1 (
|
||||||
indexed_field int default NULL,
|
indexed_field int default NULL,
|
||||||
KEY indexed_field (indexed_field)
|
KEY indexed_field (indexed_field)
|
||||||
|
@ -286,15 +286,15 @@ a b c
|
|||||||
1 NULL NULL
|
1 NULL NULL
|
||||||
explain select * from t1 where a = 1 order by a desc, b desc;
|
explain select * from t1 where a = 1 order by a desc, b desc;
|
||||||
table type possible_keys key key_len ref rows Extra
|
table type possible_keys key key_len ref rows Extra
|
||||||
t1 ref a a 4 const 5 where used; Using index; Using filesort
|
t1 ref a a 4 const 5 where used; Using index
|
||||||
select * from t1 where a = 1 order by a desc, b desc;
|
select * from t1 where a = 1 order by a desc, b desc;
|
||||||
a b c
|
a b c
|
||||||
1 3 b
|
1 3 b
|
||||||
|
1 1 b
|
||||||
|
1 1 b
|
||||||
1 1 NULL
|
1 1 NULL
|
||||||
1 1 b
|
|
||||||
1 1 b
|
|
||||||
1 NULL NULL
|
|
||||||
1 NULL b
|
1 NULL b
|
||||||
|
1 NULL NULL
|
||||||
explain select * from t1 where a = 1 and b is null order by a desc, b desc;
|
explain select * from t1 where a = 1 and b is null order by a desc, b desc;
|
||||||
table type possible_keys key key_len ref rows Extra
|
table type possible_keys key key_len ref rows Extra
|
||||||
t1 ref a a 9 const,const 2 where used; Using index; Using filesort
|
t1 ref a a 9 const,const 2 where used; Using index; Using filesort
|
||||||
|
@ -243,3 +243,26 @@ select sql_big_result spid,sum(userid) from t1 group by spid desc;
|
|||||||
explain select sql_big_result score,count(*) from t1 group by score desc;
|
explain select sql_big_result score,count(*) from t1 group by score desc;
|
||||||
select sql_big_result score,count(*) from t1 group by score desc;
|
select sql_big_result score,count(*) from t1 group by score desc;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
|
||||||
|
#
|
||||||
|
# Compare with hash keys
|
||||||
|
#
|
||||||
|
|
||||||
|
CREATE TABLE t1 (a char(1));
|
||||||
|
INSERT INTO t1 VALUES ('A'),('B'),('A'),('B'),('A'),('B'),(NULL),('a'),('b'),(NULL),('A'),('B'),(NULL);
|
||||||
|
SELECT a FROM t1 GROUP BY a;
|
||||||
|
SELECT a,count(*) FROM t1 GROUP BY a;
|
||||||
|
SELECT a FROM t1 GROUP BY binary a;
|
||||||
|
SELECT a,count(*) FROM t1 GROUP BY binary a;
|
||||||
|
SELECT binary a FROM t1 GROUP BY 1;
|
||||||
|
SELECT binary a,count(*) FROM t1 GROUP BY 1;
|
||||||
|
# Do the same tests with MyISAM temporary tables
|
||||||
|
SET SQL_BIG_TABLES=1;
|
||||||
|
SELECT a FROM t1 GROUP BY a;
|
||||||
|
SELECT a,count(*) FROM t1 GROUP BY a;
|
||||||
|
SELECT a FROM t1 GROUP BY binary a;
|
||||||
|
SELECT a,count(*) FROM t1 GROUP BY binary a;
|
||||||
|
SELECT binary a FROM t1 GROUP BY 1;
|
||||||
|
SELECT binary a,count(*) FROM t1 GROUP BY 1;
|
||||||
|
SET SQL_BIG_TABLES=0;
|
||||||
|
drop table t1;
|
||||||
|
@ -100,3 +100,25 @@ update t1 set new_col=btn;
|
|||||||
explain select * from t1 where btn="a";
|
explain select * from t1 where btn="a";
|
||||||
explain select * from t1 where btn="a" and new_col="a";
|
explain select * from t1 where btn="a" and new_col="a";
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
|
||||||
|
#
|
||||||
|
# Test of NULL keys
|
||||||
|
#
|
||||||
|
|
||||||
|
CREATE TABLE t1 (
|
||||||
|
a int default NULL,
|
||||||
|
b int default NULL,
|
||||||
|
KEY a (a),
|
||||||
|
UNIQUE b (b)
|
||||||
|
) type=heap;
|
||||||
|
INSERT INTO t1 VALUES (NULL,99),(99,NULL),(1,1),(2,2),(1,3);
|
||||||
|
SELECT * FROM t1 WHERE a=NULL;
|
||||||
|
explain SELECT * FROM t1 WHERE a IS NULL;
|
||||||
|
SELECT * FROM t1 WHERE a<=>NULL;
|
||||||
|
SELECT * FROM t1 WHERE b=NULL;
|
||||||
|
explain SELECT * FROM t1 WHERE b IS NULL;
|
||||||
|
SELECT * FROM t1 WHERE b<=>NULL;
|
||||||
|
|
||||||
|
--error 1062
|
||||||
|
INSERT INTO t1 VALUES (1,3);
|
||||||
|
DROP TABLE t1;
|
||||||
|
@ -25,7 +25,6 @@ drop table t1;
|
|||||||
# Test problem med index on NULL columns and testing with =NULL;
|
# Test problem med index on NULL columns and testing with =NULL;
|
||||||
#
|
#
|
||||||
|
|
||||||
DROP TABLE IF EXISTS t1;
|
|
||||||
CREATE TABLE t1 (
|
CREATE TABLE t1 (
|
||||||
indexed_field int default NULL,
|
indexed_field int default NULL,
|
||||||
KEY indexed_field (indexed_field)
|
KEY indexed_field (indexed_field)
|
||||||
|
@ -33,7 +33,7 @@ const char **ha_heap::bas_ext() const
|
|||||||
|
|
||||||
int ha_heap::open(const char *name, int mode, uint test_if_locked)
|
int ha_heap::open(const char *name, int mode, uint test_if_locked)
|
||||||
{
|
{
|
||||||
uint key,part,parts,mem_per_row=0;
|
uint key,parts,mem_per_row=0;
|
||||||
ulong max_rows;
|
ulong max_rows;
|
||||||
HP_KEYDEF *keydef;
|
HP_KEYDEF *keydef;
|
||||||
HP_KEYSEG *seg;
|
HP_KEYSEG *seg;
|
||||||
@ -48,24 +48,27 @@ int ha_heap::open(const char *name, int mode, uint test_if_locked)
|
|||||||
for (key=0 ; key < table->keys ; key++)
|
for (key=0 ; key < table->keys ; key++)
|
||||||
{
|
{
|
||||||
KEY *pos=table->key_info+key;
|
KEY *pos=table->key_info+key;
|
||||||
|
KEY_PART_INFO *key_part= pos->key_part;
|
||||||
|
KEY_PART_INFO *key_part_end= key_part+pos->key_parts;
|
||||||
|
|
||||||
mem_per_row += (pos->key_length + (sizeof(char*) * 2));
|
mem_per_row += (pos->key_length + (sizeof(char*) * 2));
|
||||||
|
|
||||||
keydef[key].keysegs=(uint) pos->key_parts;
|
keydef[key].keysegs=(uint) pos->key_parts;
|
||||||
keydef[key].flag = (pos->flags & HA_NOSAME);
|
keydef[key].flag = (pos->flags & (HA_NOSAME | HA_NULL_ARE_EQUAL));
|
||||||
keydef[key].seg=seg;
|
keydef[key].seg=seg;
|
||||||
|
|
||||||
for (part=0 ; part < pos->key_parts ; part++)
|
for (; key_part != key_part_end ; key_part++, seg++)
|
||||||
{
|
{
|
||||||
uint flag=pos->key_part[part].key_type;
|
uint flag=key_part->key_type;
|
||||||
Field *field=pos->key_part[part].field;
|
Field *field=key_part->field;
|
||||||
if (!f_is_packed(flag) &&
|
if (!f_is_packed(flag) &&
|
||||||
f_packtype(flag) == (int) FIELD_TYPE_DECIMAL &&
|
f_packtype(flag) == (int) FIELD_TYPE_DECIMAL &&
|
||||||
!(flag & FIELDFLAG_BINARY))
|
!(flag & FIELDFLAG_BINARY))
|
||||||
seg->type= (int) HA_KEYTYPE_TEXT;
|
seg->type= (int) HA_KEYTYPE_TEXT;
|
||||||
else
|
else
|
||||||
seg->type= (int) HA_KEYTYPE_BINARY;
|
seg->type= (int) HA_KEYTYPE_BINARY;
|
||||||
seg->start=(uint) pos->key_part[part].offset;
|
seg->start=(uint) key_part->offset;
|
||||||
seg->length=(uint) pos->key_part[part].length;
|
seg->length=(uint) key_part->length;
|
||||||
if (field->null_ptr)
|
if (field->null_ptr)
|
||||||
{
|
{
|
||||||
seg->null_bit=field->null_bit;
|
seg->null_bit=field->null_bit;
|
||||||
@ -88,6 +91,7 @@ int ha_heap::open(const char *name, int mode, uint test_if_locked)
|
|||||||
table->max_rows : max_rows),
|
table->max_rows : max_rows),
|
||||||
table->min_rows);
|
table->min_rows);
|
||||||
my_free((gptr) keydef,MYF(0));
|
my_free((gptr) keydef,MYF(0));
|
||||||
|
if (file)
|
||||||
info(HA_STATUS_NO_LOCK | HA_STATUS_CONST | HA_STATUS_VARIABLE);
|
info(HA_STATUS_NO_LOCK | HA_STATUS_CONST | HA_STATUS_VARIABLE);
|
||||||
ref_length=sizeof(HEAP_PTR);
|
ref_length=sizeof(HEAP_PTR);
|
||||||
return (!file ? errno : 0);
|
return (!file ? errno : 0);
|
||||||
|
@ -435,7 +435,8 @@ class Item_func_binary :public Item_str_func
|
|||||||
public:
|
public:
|
||||||
Item_func_binary(Item *a) :Item_str_func(a) {}
|
Item_func_binary(Item *a) :Item_str_func(a) {}
|
||||||
const char *func_name() const { return "binary"; }
|
const char *func_name() const { return "binary"; }
|
||||||
String *val_str(String *a) { return (args[0]->val_str(a)); }
|
String *val_str(String *a)
|
||||||
|
{ a=args[0]->val_str(a); null_value=args[0]->null_value; return a; }
|
||||||
void fix_length_and_dec() { binary=1; max_length=args[0]->max_length; }
|
void fix_length_and_dec() { binary=1; max_length=args[0]->max_length; }
|
||||||
void print(String *str) { print_op(str); }
|
void print(String *str) { print_op(str); }
|
||||||
};
|
};
|
||||||
|
@ -418,7 +418,8 @@ class Item_typecast :public Item_str_func
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Item_typecast(Item *a) :Item_str_func(a) {}
|
Item_typecast(Item *a) :Item_str_func(a) {}
|
||||||
String *val_str(String *a) { return (args[0]->val_str(a)); }
|
String *val_str(String *a)
|
||||||
|
{ a=args[0]->val_str(a); null_value=args[0]->null_value; return a; }
|
||||||
void fix_length_and_dec() { max_length=args[0]->max_length; }
|
void fix_length_and_dec() { max_length=args[0]->max_length; }
|
||||||
void print(String *str);
|
void print(String *str);
|
||||||
};
|
};
|
||||||
|
@ -557,9 +557,9 @@ pthread_handler_decl(handle_one_connection,arg)
|
|||||||
|
|
||||||
pthread_detach_this_thread();
|
pthread_detach_this_thread();
|
||||||
|
|
||||||
#if !defined( __WIN__) && !defined(OS2) /* Win32 calls this in pthread_create */
|
#if !defined( __WIN__) && !defined(OS2) // Win32 calls this in pthread_create
|
||||||
if (my_thread_init()) // needed to be called first before we call
|
// The following calls needs to be done before we call DBUG_ macros
|
||||||
// DBUG_ macros
|
if (my_thread_init())
|
||||||
{
|
{
|
||||||
close_connection(&thd->net,ER_OUT_OF_RESOURCES);
|
close_connection(&thd->net,ER_OUT_OF_RESOURCES);
|
||||||
statistic_increment(aborted_connects,&LOCK_thread_count);
|
statistic_increment(aborted_connects,&LOCK_thread_count);
|
||||||
@ -568,13 +568,13 @@ pthread_handler_decl(handle_one_connection,arg)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// handle_one_connection() is the only way a thread would start
|
/*
|
||||||
// and would always be on top of the stack
|
handle_one_connection() is the only way a thread would start
|
||||||
// therefore, the thread stack always starts at the address of the first
|
and would always be on top of the stack, therefore, the thread
|
||||||
// local variable of handle_one_connection, which is thd
|
stack always starts at the address of the first local variable
|
||||||
// we need to know the start of the stack so that we could check for
|
of handle_one_connection, which is thd. We need to know the
|
||||||
// stack overruns
|
start of the stack so that we could check for stack overruns.
|
||||||
|
*/
|
||||||
DBUG_PRINT("info", ("handle_one_connection called by thread %d\n",
|
DBUG_PRINT("info", ("handle_one_connection called by thread %d\n",
|
||||||
thd->thread_id));
|
thd->thread_id));
|
||||||
// now that we've called my_thread_init(), it is safe to call DBUG_*
|
// now that we've called my_thread_init(), it is safe to call DBUG_*
|
||||||
@ -1216,7 +1216,6 @@ mysql_execute_command(void)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
thread_safe_increment(com_stat[lex->sql_command],&LOCK_thread_count);
|
|
||||||
/*
|
/*
|
||||||
Skip if we are in the slave thread, some table rules have been given
|
Skip if we are in the slave thread, some table rules have been given
|
||||||
and the table list says the query should not be replicated
|
and the table list says the query should not be replicated
|
||||||
|
@ -183,7 +183,7 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds,
|
|||||||
ulong select_options,select_result *result)
|
ulong select_options,select_result *result)
|
||||||
{
|
{
|
||||||
TABLE *tmp_table;
|
TABLE *tmp_table;
|
||||||
int error,tmp;
|
int error, tmp_error, tmp;
|
||||||
bool need_tmp,hidden_group_fields;
|
bool need_tmp,hidden_group_fields;
|
||||||
bool simple_order,simple_group,no_order, skip_sort_order;
|
bool simple_order,simple_group,no_order, skip_sort_order;
|
||||||
Item::cond_result cond_value;
|
Item::cond_result cond_value;
|
||||||
@ -678,8 +678,11 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds,
|
|||||||
|
|
||||||
/* Copy data to the temporary table */
|
/* Copy data to the temporary table */
|
||||||
thd->proc_info="Copying to tmp table";
|
thd->proc_info="Copying to tmp table";
|
||||||
if (do_select(&join,(List<Item> *) 0,tmp_table,0))
|
if ((tmp_error=do_select(&join,(List<Item> *) 0,tmp_table,0)))
|
||||||
|
{
|
||||||
|
error=tmp_error;
|
||||||
goto err; /* purecov: inspected */
|
goto err; /* purecov: inspected */
|
||||||
|
}
|
||||||
if (join.having)
|
if (join.having)
|
||||||
join.having=having=0; // Allready done
|
join.having=having=0; // Allready done
|
||||||
|
|
||||||
@ -752,9 +755,11 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds,
|
|||||||
group=0;
|
group=0;
|
||||||
}
|
}
|
||||||
thd->proc_info="Copying to group table";
|
thd->proc_info="Copying to group table";
|
||||||
|
tmp_error= -1;
|
||||||
if (make_sum_func_list(&join,all_fields) ||
|
if (make_sum_func_list(&join,all_fields) ||
|
||||||
do_select(&join,(List<Item> *) 0,tmp_table2,0))
|
(tmp_error=do_select(&join,(List<Item> *) 0,tmp_table2,0)))
|
||||||
{
|
{
|
||||||
|
error=tmp_error;
|
||||||
free_tmp_table(thd,tmp_table2);
|
free_tmp_table(thd,tmp_table2);
|
||||||
goto err; /* purecov: inspected */
|
goto err; /* purecov: inspected */
|
||||||
}
|
}
|
||||||
@ -3736,14 +3741,16 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
|
|||||||
if (maybe_null)
|
if (maybe_null)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
To be able to group on NULL, we move the null bit to be
|
To be able to group on NULL, we reserve place in group_buff
|
||||||
just before the column.
|
for the NULL flag just before the column.
|
||||||
The null byte is updated by 'end_update()'
|
The field data is after this flag.
|
||||||
|
The NULL flag is updated by 'end_update()' and 'end_write()'
|
||||||
*/
|
*/
|
||||||
key_part_info->null_bit=1;
|
keyinfo->flags|= HA_NULL_ARE_EQUAL; // def. that NULL == NULL
|
||||||
key_part_info->null_offset= key_part_info->offset-1;
|
key_part_info->null_bit=field->null_bit;
|
||||||
group->field->move_field((char*) group_buff+1, (uchar*) group_buff,
|
key_part_info->null_offset= (uint) (field->null_ptr -
|
||||||
1);
|
(uchar*) table->record[0]);
|
||||||
|
group->field->move_field((char*) ++group->buff);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
group->field->move_field((char*) group_buff);
|
group->field->move_field((char*) group_buff);
|
||||||
@ -3923,11 +3930,17 @@ static bool create_myisam_tmp_table(TABLE *table,TMP_TABLE_PARAM *param,
|
|||||||
keyinfo->key_part[i].length > 4)
|
keyinfo->key_part[i].length > 4)
|
||||||
seg->flag|=HA_SPACE_PACK;
|
seg->flag|=HA_SPACE_PACK;
|
||||||
}
|
}
|
||||||
if (using_unique_constraint &&
|
if (!(field->flags & NOT_NULL_FLAG))
|
||||||
!(field->flags & NOT_NULL_FLAG))
|
|
||||||
{
|
{
|
||||||
seg->null_bit= field->null_bit;
|
seg->null_bit= field->null_bit;
|
||||||
seg->null_pos= (uint) (field->null_ptr - (uchar*) table->record[0]);
|
seg->null_pos= (uint) (field->null_ptr - (uchar*) table->record[0]);
|
||||||
|
/*
|
||||||
|
We are using a GROUP BY on something that contains NULL
|
||||||
|
In this case we have to tell MyISAM that two NULL should
|
||||||
|
on INSERT be compared as equal
|
||||||
|
*/
|
||||||
|
if (!using_unique_constraint)
|
||||||
|
keydef.flag|= HA_NULL_ARE_EQUAL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4065,9 +4078,12 @@ bool create_myisam_from_heap(TABLE *table, TMP_TABLE_PARAM *param, int error,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************
|
/****************************************************************************
|
||||||
** Make a join of all tables and write it on socket or to table
|
Make a join of all tables and write it on socket or to table
|
||||||
*****************************************************************************/
|
Return: 0 if ok
|
||||||
|
1 if error is sent
|
||||||
|
-1 if error should be sent
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
static int
|
static int
|
||||||
do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
|
do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
|
||||||
@ -4144,15 +4160,21 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
|
|||||||
if (error == -3)
|
if (error == -3)
|
||||||
error=0; /* select_limit used */
|
error=0; /* select_limit used */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Return 1 if error is sent; -1 if error should be sent */
|
||||||
if (error < 0)
|
if (error < 0)
|
||||||
|
{
|
||||||
join->result->send_error(0,NullS); /* purecov: inspected */
|
join->result->send_error(0,NullS); /* purecov: inspected */
|
||||||
|
error=1; // Error sent
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
error=0;
|
||||||
if (!table) // If sending data to client
|
if (!table) // If sending data to client
|
||||||
{
|
{
|
||||||
join_free(join); // Unlock all cursors
|
join_free(join); // Unlock all cursors
|
||||||
if (join->result->send_eof())
|
if (join->result->send_eof())
|
||||||
error= -1;
|
error= 1; // Don't send error
|
||||||
}
|
}
|
||||||
DBUG_PRINT("info",("%ld records output",join->send_records));
|
DBUG_PRINT("info",("%ld records output",join->send_records));
|
||||||
}
|
}
|
||||||
@ -4169,10 +4191,10 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
|
|||||||
my_errno=tmp;
|
my_errno=tmp;
|
||||||
error= -1;
|
error= -1;
|
||||||
}
|
}
|
||||||
if (error != old_error)
|
if (error == -1)
|
||||||
table->file->print_error(my_errno,MYF(0));
|
table->file->print_error(my_errno,MYF(0));
|
||||||
}
|
}
|
||||||
DBUG_RETURN(error < 0);
|
DBUG_RETURN(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -4926,6 +4948,7 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
|
|||||||
copy_fields(&join->tmp_table_param);
|
copy_fields(&join->tmp_table_param);
|
||||||
copy_funcs(join->tmp_table_param.funcs);
|
copy_funcs(join->tmp_table_param.funcs);
|
||||||
|
|
||||||
|
#ifdef TO_BE_DELETED
|
||||||
if (!table->uniques) // If not unique handling
|
if (!table->uniques) // If not unique handling
|
||||||
{
|
{
|
||||||
/* Copy null values from group to row */
|
/* Copy null values from group to row */
|
||||||
@ -4936,10 +4959,11 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
|
|||||||
if (item->maybe_null)
|
if (item->maybe_null)
|
||||||
{
|
{
|
||||||
Field *field=item->tmp_table_field();
|
Field *field=item->tmp_table_field();
|
||||||
field->ptr[-1]= (byte) (field->is_null() ? 0 : 1);
|
field->ptr[-1]= (byte) (field->is_null() ? 1 : 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
if (!join->having || join->having->val_int())
|
if (!join->having || join->having->val_int())
|
||||||
{
|
{
|
||||||
join->found_records++;
|
join->found_records++;
|
||||||
@ -4994,8 +5018,9 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
|
|||||||
{
|
{
|
||||||
Item *item= *group->item;
|
Item *item= *group->item;
|
||||||
item->save_org_in_field(group->field);
|
item->save_org_in_field(group->field);
|
||||||
|
/* Store in the used key if the field was 0 */
|
||||||
if (item->maybe_null)
|
if (item->maybe_null)
|
||||||
group->buff[0]=item->null_value ? 0: 1; // Save reversed value
|
group->buff[-1]=item->null_value ? 1 : 0;
|
||||||
}
|
}
|
||||||
// table->file->index_init(0);
|
// table->file->index_init(0);
|
||||||
if (!table->file->index_read(table->record[1],
|
if (!table->file->index_read(table->record[1],
|
||||||
|
Reference in New Issue
Block a user