Commit f015cbdc authored by unknown's avatar unknown

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)
parent 71a5af5b
...@@ -451,3 +451,4 @@ vio/test-ssl ...@@ -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}. ...@@ -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, ...@@ -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) ...@@ -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, ...@@ -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) ...@@ -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 { ...@@ -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 { ...@@ -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 ...@@ -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 @@ ...@@ -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, ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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,41 +762,44 @@ static int keys_compare(bulk_insert_param *param, uchar *key1, uchar *key2) ...@@ -759,41 +762,44 @@ 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, &not_used); key1, key2, USE_WHOLE_KEY, SEARCH_SAME,
&not_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, /*
and to be safe I'd better use local lastkey. Probably I can use info->lastkey here, but I'm not sure,
Monty, feel free to comment on this */ and to be safe I'd better use local lastkey.
*/
uchar lastkey[MI_MAX_KEY_BUFF]; uchar lastkey[MI_MAX_KEY_BUFF];
uint keylen; uint keylen;
MI_KEYDEF *keyinfo; MI_KEYDEF *keyinfo;
switch (mode) { switch (mode) {
case free_init: case free_init:
if (param->info->s->concurrent_insert) if (param->info->s->concurrent_insert)
{ {
rw_wrlock(&param->info->s->key_root_lock[param->keynr]); rw_wrlock(&param->info->s->key_root_lock[param->keynr]);
param->info->s->keyinfo[param->keynr].version++; param->info->s->keyinfo[param->keynr].version++;
} }
return 0; return 0;
case free_free: case free_free:
keyinfo=param->info->s->keyinfo+param->keynr; keyinfo=param->info->s->keyinfo+param->keynr;
keylen=_mi_keylength(keyinfo, key); keylen=_mi_keylength(keyinfo, key);
memcpy(lastkey, key, keylen); memcpy(lastkey, key, keylen);
return _mi_ck_write_btree(param->info,param->keynr,lastkey, return _mi_ck_write_btree(param->info,param->keynr,lastkey,
keylen - param->info->s->rec_reflength); keylen - param->info->s->rec_reflength);
case free_end: case free_end:
if (param->info->s->concurrent_insert) if (param->info->s->concurrent_insert)
rw_unlock(&param->info->s->key_root_lock[param->keynr]); rw_unlock(&param->info->s->key_root_lock[param->keynr]);
return 0; return 0;
} }
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(*) ...@@ -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"; ...@@ -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); ...@@ -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 ...@@ -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 NULL
1 1 b 1 1 b
1 1 b 1 1 b
1 NULL NULL 1 1 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; ...@@ -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; ...@@ -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; ...@@ -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 ...@@ -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) ...@@ -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,7 +91,8 @@ int ha_heap::open(const char *name, int mode, uint test_if_locked) ...@@ -88,7 +91,8 @@ 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));
info(HA_STATUS_NO_LOCK | HA_STATUS_CONST | HA_STATUS_VARIABLE); if (file)
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 ...@@ -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 ...@@ -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) ...@@ -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) ...@@ -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_*
...@@ -634,12 +634,12 @@ pthread_handler_decl(handle_one_connection,arg) ...@@ -634,12 +634,12 @@ pthread_handler_decl(handle_one_connection,arg)
if (net->error && net->vio != 0) if (net->error && net->vio != 0)
{ {
if (!thd->killed && opt_warnings) if (!thd->killed && opt_warnings)
sql_print_error(ER(ER_NEW_ABORTING_CONNECTION), sql_print_error(ER(ER_NEW_ABORTING_CONNECTION),
thd->thread_id,(thd->db ? thd->db : "unconnected"), thd->thread_id,(thd->db ? thd->db : "unconnected"),
thd->user ? thd->user : "unauthenticated", thd->user ? thd->user : "unauthenticated",
thd->host_or_ip, thd->host_or_ip,
(net->last_errno ? ER(net->last_errno) : (net->last_errno ? ER(net->last_errno) :
ER(ER_UNKNOWN_ERROR))); ER(ER_UNKNOWN_ERROR)));
send_error(net,net->last_errno,NullS); send_error(net,net->last_errno,NullS);
thread_safe_increment(aborted_threads,&LOCK_thread_count); thread_safe_increment(aborted_threads,&LOCK_thread_count);
} }
...@@ -1216,7 +1216,6 @@ mysql_execute_command(void) ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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);
...@@ -3899,10 +3906,10 @@ static bool create_myisam_tmp_table(TABLE *table,TMP_TABLE_PARAM *param, ...@@ -3899,10 +3906,10 @@ static bool create_myisam_tmp_table(TABLE *table,TMP_TABLE_PARAM *param,
for (uint i=0; i < keyinfo->key_parts ; i++,seg++) for (uint i=0; i < keyinfo->key_parts ; i++,seg++)
{ {
Field *field=keyinfo->key_part[i].field; Field *field=keyinfo->key_part[i].field;
seg->flag=0; seg->flag= 0;
seg->language=MY_CHARSET_CURRENT; seg->language= MY_CHARSET_CURRENT;
seg->length=keyinfo->key_part[i].length; seg->length= keyinfo->key_part[i].length;
seg->start=keyinfo->key_part[i].offset; seg->start= keyinfo->key_part[i].offset;
if (field->flags & BLOB_FLAG) if (field->flags & BLOB_FLAG)
{ {
seg->type= seg->type=
...@@ -3923,11 +3930,17 @@ static bool create_myisam_tmp_table(TABLE *table,TMP_TABLE_PARAM *param, ...@@ -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, ...@@ -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) ...@@ -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
{ {
if (!table) // If sending data to client error=0;
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) ...@@ -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)), ...@@ -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)), ...@@ -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)), ...@@ -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],
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment