diff --git a/include/m_ctype.h b/include/m_ctype.h index d7a4af08b0f..50673b6d494 100644 --- a/include/m_ctype.h +++ b/include/m_ctype.h @@ -85,7 +85,7 @@ enum my_lex_states { MY_LEX_START, MY_LEX_CHAR, MY_LEX_IDENT, MY_LEX_IDENT_SEP, MY_LEX_IDENT_START, - MY_LEX_REAL, MY_LEX_HEX_NUMBER, + MY_LEX_REAL, MY_LEX_HEX_NUMBER, MY_LEX_BIN_NUMBER, MY_LEX_CMP_OP, MY_LEX_LONG_CMP_OP, MY_LEX_STRING, MY_LEX_COMMENT, MY_LEX_END, MY_LEX_OPERATOR_OR_IDENT, MY_LEX_NUMBER_IDENT, MY_LEX_INT_OR_REAL, MY_LEX_REAL_OR_POINT, MY_LEX_BOOL, MY_LEX_EOL, MY_LEX_ESCAPE, diff --git a/include/my_base.h b/include/my_base.h index 88d3ec0b270..a1b14ecbb65 100644 --- a/include/my_base.h +++ b/include/my_base.h @@ -182,7 +182,8 @@ enum ha_base_keytype { HA_KEYTYPE_UINT24=13, HA_KEYTYPE_INT8=14, HA_KEYTYPE_VARTEXT=15, /* Key is sorted as letters */ - HA_KEYTYPE_VARBINARY=16 /* Key is sorted as unsigned chars */ + HA_KEYTYPE_VARBINARY=16, /* Key is sorted as unsigned chars */ + HA_KEYTYPE_BIT=17 }; #define HA_MAX_KEYTYPE 31 /* Must be log2-1 */ diff --git a/include/my_handler.h b/include/my_handler.h index d81c4590f8e..9ddc0c61eee 100644 --- a/include/my_handler.h +++ b/include/my_handler.h @@ -34,6 +34,8 @@ typedef struct st_HA_KEYSEG /* Key-portion */ uint32 start; /* Start of key in record */ uint32 null_pos; /* position to NULL indicator */ CHARSET_INFO *charset; + uint8 bit_length; /* Length of bit part */ + uint16 bit_pos; /* Position to bit part */ } HA_KEYSEG; #define get_key_length(length,key) \ @@ -64,6 +66,21 @@ typedef struct st_HA_KEYSEG /* Key-portion */ { *(key)=255; mi_int2store((key)+1,(length)); (key)+=3; } \ } +#define get_rec_bits(bit_ptr, bit_ofs, bit_len) \ + (((((uint16) (bit_ptr)[1] << 8) | (uint16) (bit_ptr)[0]) >> (bit_ofs)) & \ + ((1 << (bit_len)) - 1)) + +#define set_rec_bits(bits, bit_ptr, bit_ofs, bit_len) \ +{ \ + (bit_ptr)[0]= ((bit_ptr)[0] & ((1 << (bit_ofs)) - 1)) | \ + ((bits) << (bit_ofs)); \ + if ((bit_ofs) + (bit_len) > 8) \ + (bit_ptr)[1]= ((bits) & ((1 << (bit_len)) - 1)) >> (8 - (bit_ofs)); \ +} + +#define clr_rec_bits(bit_ptr, bit_ofs, bit_len) \ + set_rec_bits(0, bit_ptr, bit_ofs, bit_len) + extern int mi_compare_text(CHARSET_INFO *, uchar *, uint, uchar *, uint , my_bool, my_bool); extern int ha_key_cmp(register HA_KEYSEG *keyseg, register uchar *a, diff --git a/include/mysql_com.h b/include/mysql_com.h index 6a03fe90eb5..59b2ee743ec 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -210,6 +210,7 @@ enum enum_field_types { MYSQL_TYPE_DECIMAL, MYSQL_TYPE_TINY, MYSQL_TYPE_DATE, MYSQL_TYPE_TIME, MYSQL_TYPE_DATETIME, MYSQL_TYPE_YEAR, MYSQL_TYPE_NEWDATE, MYSQL_TYPE_VARCHAR, + MYSQL_TYPE_BIT, MYSQL_TYPE_ENUM=247, MYSQL_TYPE_SET=248, MYSQL_TYPE_TINY_BLOB=249, @@ -250,6 +251,7 @@ enum enum_field_types { MYSQL_TYPE_DECIMAL, MYSQL_TYPE_TINY, #define FIELD_TYPE_CHAR MYSQL_TYPE_TINY #define FIELD_TYPE_INTERVAL MYSQL_TYPE_ENUM #define FIELD_TYPE_GEOMETRY MYSQL_TYPE_GEOMETRY +#define FIELD_TYPE_BIT MYSQL_TYPE_BIT /* Shutdown/kill enums and constants */ diff --git a/myisam/mi_key.c b/myisam/mi_key.c index a775e0ba2d0..c1400033254 100644 --- a/myisam/mi_key.c +++ b/myisam/mi_key.c @@ -82,6 +82,19 @@ uint _mi_make_key(register MI_INFO *info, uint keynr, uchar *key, length); pos= (byte*) record+keyseg->start; + if (type == HA_KEYTYPE_BIT) + { + if (keyseg->bit_length) + { + uchar bits= get_rec_bits((uchar*) record + keyseg->bit_pos, + keyseg->bit_start, keyseg->bit_length); + *key++= bits; + length--; + } + memcpy((byte*) key, pos, length); + key+= length; + continue; + } if (keyseg->flag & HA_SPACE_PACK) { end=pos+length; @@ -333,6 +346,26 @@ static int _mi_put_key_in_record(register MI_INFO *info, uint keynr, } record[keyseg->null_pos]&= ~keyseg->null_bit; } + if (keyseg->type == HA_KEYTYPE_BIT) + { + uint length= keyseg->length; + + if (keyseg->bit_length) + { + uchar bits= *key++; + set_rec_bits(bits, record + keyseg->bit_pos, keyseg->bit_start, + keyseg->bit_length); + length--; + } + else + { + clr_rec_bits(record + keyseg->bit_pos, keyseg->bit_start, + keyseg->bit_length); + } + memcpy(record + keyseg->start, (byte*) key, length); + key+= length; + continue; + } if (keyseg->flag & HA_SPACE_PACK) { uint length; diff --git a/myisam/mi_open.c b/myisam/mi_open.c index 562227d2f03..615e6bb5dd1 100644 --- a/myisam/mi_open.c +++ b/myisam/mi_open.c @@ -1049,12 +1049,13 @@ int mi_keyseg_write(File file, const HA_KEYSEG *keyseg) *ptr++ =keyseg->null_bit; *ptr++ =keyseg->bit_start; *ptr++ =keyseg->bit_end; - *ptr++ =0; /* Not used */ + *ptr++= keyseg->bit_length; mi_int2store(ptr,keyseg->flag); ptr+=2; mi_int2store(ptr,keyseg->length); ptr+=2; mi_int4store(ptr,keyseg->start); ptr+=4; - mi_int4store(ptr,keyseg->null_pos); ptr+=4; - + mi_int4store(ptr, keyseg->null_bit ? keyseg->null_pos : keyseg->bit_pos); + ptr+=4; + return my_write(file,(char*) buff, (uint) (ptr-buff), MYF(MY_NABP)); } @@ -1066,12 +1067,19 @@ char *mi_keyseg_read(char *ptr, HA_KEYSEG *keyseg) keyseg->null_bit = *ptr++; keyseg->bit_start = *ptr++; keyseg->bit_end = *ptr++; - ptr++; + keyseg->bit_length = *ptr++; keyseg->flag = mi_uint2korr(ptr); ptr +=2; keyseg->length = mi_uint2korr(ptr); ptr +=2; keyseg->start = mi_uint4korr(ptr); ptr +=4; keyseg->null_pos = mi_uint4korr(ptr); ptr +=4; keyseg->charset=0; /* Will be filled in later */ + if (keyseg->null_bit) + keyseg->bit_pos= keyseg->null_pos + (keyseg->null_bit == 7); + else + { + keyseg->bit_pos= keyseg->null_pos; + keyseg->null_pos= 0; + } return ptr; } diff --git a/mysql-test/r/type_bit.result b/mysql-test/r/type_bit.result new file mode 100644 index 00000000000..41d84584870 --- /dev/null +++ b/mysql-test/r/type_bit.result @@ -0,0 +1,364 @@ +select 0 + b'1'; +0 + b'1' +1 +select 0 + b'0'; +0 + b'0' +0 +select 0 + b'000001'; +0 + b'000001' +1 +select 0 + b'000011'; +0 + b'000011' +3 +select 0 + b'000101'; +0 + b'000101' +5 +select 0 + b'000000'; +0 + b'000000' +0 +select 0 + b'10000000'; +0 + b'10000000' +128 +select 0 + b'11111111'; +0 + b'11111111' +255 +select 0 + b'10000001'; +0 + b'10000001' +129 +select 0 + b'1000000000000000'; +0 + b'1000000000000000' +32768 +select 0 + b'1111111111111111'; +0 + b'1111111111111111' +65535 +select 0 + b'1000000000000001'; +0 + b'1000000000000001' +32769 +drop table if exists t1; +create table t1 (a bit(65)); +ERROR 42000: Column length too big for column 'a' (max = 64); use BLOB instead +create table t1 (a bit(0)); +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` bit(1) default NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +drop table t1; +create table t1 (a bit, key(a)) engine=innodb; +ERROR 42000: The storage engine for the table doesn't support BIT FIELD +create table t1 (a bit(64)); +insert into t1 values +(b'1111111111111111111111111111111111111111111111111111111111111111'), +(b'1000000000000000000000000000000000000000000000000000000000000000'), +(b'0000000000000000000000000000000000000000000000000000000000000001'), +(b'1010101010101010101010101010101010101010101010101010101010101010'), +(b'0101010101010101010101010101010101010101010101010101010101010101'); +select hex(a) from t1; +hex(a) +FFFFFFFFFFFFFFFF +8000000000000000 +1 +AAAAAAAAAAAAAAAA +5555555555555555 +drop table t1; +create table t1 (a bit); +insert into t1 values (b'0'), (b'1'), (b'000'), (b'100'), (b'001'); +Warnings: +Warning 1264 Out of range value adjusted for column 'a' at row 4 +select hex(a) from t1; +hex(a) +0 +1 +0 +1 +1 +alter table t1 add unique (a); +ERROR 23000: Duplicate entry '' for key 1 +drop table t1; +create table t1 (a bit(2)); +insert into t1 values (b'00'), (b'01'), (b'10'), (b'100'); +Warnings: +Warning 1264 Out of range value adjusted for column 'a' at row 4 +select a+0 from t1; +a+0 +0 +1 +2 +3 +alter table t1 add key (a); +explain select a+0 from t1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index NULL a 2 NULL 4 Using index +select a+0 from t1; +a+0 +0 +1 +2 +3 +drop table t1; +create table t1 (a bit(7), b bit(9), key(a, b)); +insert into t1 values +(94, 46), (31, 438), (61, 152), (78, 123), (88, 411), (122, 118), (0, 177), +(75, 42), (108, 67), (79, 349), (59, 188), (68, 206), (49, 345), (118, 380), +(111, 368), (94, 468), (56, 379), (77, 133), (29, 399), (9, 363), (23, 36), +(116, 390), (119, 368), (87, 351), (123, 411), (24, 398), (34, 202), (28, 499), +(30, 83), (5, 178), (60, 343), (4, 245), (104, 280), (106, 446), (127, 403), +(44, 307), (68, 454), (57, 135); +explain select a+0 from t1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index NULL a 5 NULL 38 Using index +select a+0 from t1; +a+0 +0 +4 +5 +9 +23 +24 +28 +29 +30 +31 +34 +44 +49 +56 +57 +59 +60 +61 +68 +68 +75 +77 +78 +79 +87 +88 +94 +94 +104 +106 +108 +111 +116 +118 +119 +122 +123 +127 +explain select b+0 from t1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index NULL a 5 NULL 38 Using index +select b+0 from t1; +b+0 +177 +245 +178 +363 +36 +398 +499 +399 +83 +438 +202 +307 +345 +379 +135 +188 +343 +152 +206 +454 +42 +133 +123 +349 +351 +411 +46 +468 +280 +446 +67 +368 +390 +380 +368 +118 +411 +403 +explain select a+0, b+0 from t1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index NULL a 5 NULL 38 Using index +select a+0, b+0 from t1; +a+0 b+0 +0 177 +4 245 +5 178 +9 363 +23 36 +24 398 +28 499 +29 399 +30 83 +31 438 +34 202 +44 307 +49 345 +56 379 +57 135 +59 188 +60 343 +61 152 +68 206 +68 454 +75 42 +77 133 +78 123 +79 349 +87 351 +88 411 +94 46 +94 468 +104 280 +106 446 +108 67 +111 368 +116 390 +118 380 +119 368 +122 118 +123 411 +127 403 +explain select a+0, b+0 from t1 where a > 40 and b > 200 order by 1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range a a 2 NULL 27 Using where; Using index; Using filesort +select a+0, b+0 from t1 where a > 40 and b > 200 order by 1; +a+0 b+0 +44 307 +49 345 +56 379 +60 343 +68 206 +68 454 +79 349 +87 351 +88 411 +94 468 +104 280 +106 446 +111 368 +116 390 +118 380 +119 368 +123 411 +127 403 +explain select a+0, b+0 from t1 where a > 40 and a < 70 order by 2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range a a 2 NULL 8 Using where; Using index; Using filesort +select a+0, b+0 from t1 where a > 40 and a < 70 order by 2; +a+0 b+0 +57 135 +61 152 +59 188 +68 206 +44 307 +60 343 +49 345 +56 379 +68 454 +set @@max_length_for_sort_data=0; +select a+0, b+0 from t1 where a > 40 and a < 70 order by 2; +a+0 b+0 +57 135 +61 152 +59 188 +68 206 +44 307 +60 343 +49 345 +56 379 +68 454 +select hex(min(a)) from t1; +hex(min(a)) +0 +select hex(min(b)) from t1; +hex(min(b)) +24 +select hex(min(a)), hex(max(a)), hex(min(b)), hex(max(b)) from t1; +hex(min(a)) hex(max(a)) hex(min(b)) hex(max(b)) +0 7F 24 1F3 +drop table t1; +create table t1 (a int not null, b bit, c bit(9), key(a, b, c)); +insert into t1 values +(4, NULL, 1), (4, 0, 3), (2, 1, 4), (1, 1, 100), (4, 0, 23), (4, 0, 54), +(56, 0, 22), (4, 1, 100), (23, 0, 1), (4, 0, 34); +select a+0, b+0, c+0 from t1; +a+0 b+0 c+0 +1 1 100 +2 1 4 +4 NULL 1 +4 0 3 +4 0 23 +4 0 34 +4 0 54 +4 1 100 +23 0 1 +56 0 22 +select hex(min(b)) from t1 where a = 4; +hex(min(b)) +0 +select hex(min(c)) from t1 where a = 4 and b = 0; +hex(min(c)) +3 +select hex(max(b)) from t1; +hex(max(b)) +1 +select a+0, b+0, c+0 from t1 where a = 4 and b = 0 limit 2; +a+0 b+0 c+0 +4 0 3 +4 0 23 +select a+0, b+0, c+0 from t1 order by b desc; +a+0 b+0 c+0 +2 1 4 +1 1 100 +4 1 100 +4 0 3 +4 0 23 +4 0 54 +56 0 22 +23 0 1 +4 0 34 +4 NULL 1 +select a+0, b+0, c+0 from t1 order by c; +a+0 b+0 c+0 +4 NULL 1 +23 0 1 +4 0 3 +2 1 4 +56 0 22 +4 0 23 +4 0 34 +4 0 54 +1 1 100 +4 1 100 +drop table t1; +create table t1(a bit(2), b bit(2)); +insert into t1 (a) values (0x01), (0x03), (0x02); +update t1 set b= concat(a); +select a+0, b+0 from t1; +a+0 b+0 +1 1 +3 3 +2 2 +drop table t1; +create table t1 (a bit(7), key(a)); +insert into t1 values (44), (57); +select a+0 from t1; +a+0 +44 +57 +drop table t1; diff --git a/mysql-test/t/type_bit.test b/mysql-test/t/type_bit.test new file mode 100644 index 00000000000..075fd5bce07 --- /dev/null +++ b/mysql-test/t/type_bit.test @@ -0,0 +1,106 @@ +# +# testing of the BIT column type +# + +select 0 + b'1'; +select 0 + b'0'; +select 0 + b'000001'; +select 0 + b'000011'; +select 0 + b'000101'; +select 0 + b'000000'; +select 0 + b'10000000'; +select 0 + b'11111111'; +select 0 + b'10000001'; +select 0 + b'1000000000000000'; +select 0 + b'1111111111111111'; +select 0 + b'1000000000000001'; + +--disable_warnings +drop table if exists t1; +--enable_warnings + +--error 1074 +create table t1 (a bit(65)); + +create table t1 (a bit(0)); +show create table t1; +drop table t1; + +--error 1178 +create table t1 (a bit, key(a)) engine=innodb; + +create table t1 (a bit(64)); +insert into t1 values +(b'1111111111111111111111111111111111111111111111111111111111111111'), +(b'1000000000000000000000000000000000000000000000000000000000000000'), +(b'0000000000000000000000000000000000000000000000000000000000000001'), +(b'1010101010101010101010101010101010101010101010101010101010101010'), +(b'0101010101010101010101010101010101010101010101010101010101010101'); +select hex(a) from t1; +drop table t1; + +create table t1 (a bit); +insert into t1 values (b'0'), (b'1'), (b'000'), (b'100'), (b'001'); +select hex(a) from t1; +--error 1062 +alter table t1 add unique (a); +drop table t1; + +create table t1 (a bit(2)); +insert into t1 values (b'00'), (b'01'), (b'10'), (b'100'); +select a+0 from t1; +alter table t1 add key (a); +explain select a+0 from t1; +select a+0 from t1; +drop table t1; + +create table t1 (a bit(7), b bit(9), key(a, b)); +insert into t1 values +(94, 46), (31, 438), (61, 152), (78, 123), (88, 411), (122, 118), (0, 177), +(75, 42), (108, 67), (79, 349), (59, 188), (68, 206), (49, 345), (118, 380), +(111, 368), (94, 468), (56, 379), (77, 133), (29, 399), (9, 363), (23, 36), +(116, 390), (119, 368), (87, 351), (123, 411), (24, 398), (34, 202), (28, 499), +(30, 83), (5, 178), (60, 343), (4, 245), (104, 280), (106, 446), (127, 403), +(44, 307), (68, 454), (57, 135); +explain select a+0 from t1; +select a+0 from t1; +explain select b+0 from t1; +select b+0 from t1; +explain select a+0, b+0 from t1; +select a+0, b+0 from t1; +explain select a+0, b+0 from t1 where a > 40 and b > 200 order by 1; +select a+0, b+0 from t1 where a > 40 and b > 200 order by 1; +explain select a+0, b+0 from t1 where a > 40 and a < 70 order by 2; +select a+0, b+0 from t1 where a > 40 and a < 70 order by 2; +set @@max_length_for_sort_data=0; +select a+0, b+0 from t1 where a > 40 and a < 70 order by 2; +select hex(min(a)) from t1; +select hex(min(b)) from t1; +select hex(min(a)), hex(max(a)), hex(min(b)), hex(max(b)) from t1; +drop table t1; + +create table t1 (a int not null, b bit, c bit(9), key(a, b, c)); +insert into t1 values +(4, NULL, 1), (4, 0, 3), (2, 1, 4), (1, 1, 100), (4, 0, 23), (4, 0, 54), +(56, 0, 22), (4, 1, 100), (23, 0, 1), (4, 0, 34); +select a+0, b+0, c+0 from t1; +select hex(min(b)) from t1 where a = 4; +select hex(min(c)) from t1 where a = 4 and b = 0; +select hex(max(b)) from t1; +select a+0, b+0, c+0 from t1 where a = 4 and b = 0 limit 2; +select a+0, b+0, c+0 from t1 order by b desc; +select a+0, b+0, c+0 from t1 order by c; +drop table t1; + +create table t1(a bit(2), b bit(2)); +insert into t1 (a) values (0x01), (0x03), (0x02); +update t1 set b= concat(a); +select a+0, b+0 from t1; +drop table t1; + +# Some magic numbers + +create table t1 (a bit(7), key(a)); +insert into t1 values (44), (57); +select a+0 from t1; +drop table t1; diff --git a/mysys/my_handler.c b/mysys/my_handler.c index cf8bde31e73..e4dc55964da 100644 --- a/mysys/my_handler.c +++ b/mysys/my_handler.c @@ -178,6 +178,7 @@ int ha_key_cmp(register HA_KEYSEG *keyseg, register uchar *a, } break; case HA_KEYTYPE_BINARY: + case HA_KEYTYPE_BIT: if (keyseg->flag & HA_SPACE_PACK) { int a_length,b_length,pack_length; diff --git a/sql/field.cc b/sql/field.cc index 6f38bd3c85a..7b555571850 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -6048,6 +6048,227 @@ bool Field_num::eq_def(Field *field) } +/* + Bit field. + + We store the first 0 - 6 uneven bits among the null bits + at the start of the record. The rest bytes are stored in + the record itself. + + For example: + + CREATE TABLE t1 (a int, b bit(17), c bit(21) not null, d bit(8)); + We would store data as follows in the record: + + Byte Bit + 1 7 - reserve for delete + 6 - null bit for 'a' + 5 - null bit for 'b' + 4 - first (high) bit of 'b' + 3 - first (high) bit of 'c' + 2 - second bit of 'c' + 1 - third bit of 'c' + 0 - forth bit of 'c' + 2 7 - firth bit of 'c' + 6 - null bit for 'd' + 3 - 6 four bytes for 'a' + 7 - 8 two bytes for 'b' + 9 - 10 two bytes for 'c' + 11 one byte for 'd' +*/ + +void Field_bit::make_field(Send_field *field) +{ + /* table_cache_key is not set for temp tables */ + field->db_name= (orig_table->table_cache_key ? orig_table->table_cache_key : + ""); + field->org_table_name= orig_table->real_name; + field->table_name= orig_table->table_name; + field->col_name= field->org_col_name= field_name; + field->charsetnr= charset()->number; + field->length= field_length; + field->type= type(); + field->flags= table->maybe_null ? (flags & ~NOT_NULL_FLAG) : flags; + field->decimals= 0; +} + + +int Field_bit::store(const char *from, uint length, CHARSET_INFO *cs) +{ + int delta; + + for (; !*from && length; from++, length--); // skip left 0's + delta= field_length - length; + + if (delta < -1 || + (delta == -1 && (uchar) *from > ((1 << bit_len) - 1)) || + (!bit_len && delta < 0)) + { + set_rec_bits(0xff, bit_ptr, bit_ofs, bit_len); + memset(ptr, 0xff, field_length); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + return 1; + } + /* delta is >= -1 here */ + if (delta > 0) + { + if (bit_len) + clr_rec_bits(bit_ptr, bit_ofs, bit_len); + bzero(ptr, delta); + memcpy(ptr + delta, from, length); + } + else if (delta == 0) + { + if (bit_len) + clr_rec_bits(bit_ptr, bit_ofs, bit_len); + memcpy(ptr, from, length); + } + else + { + if (bit_len) + { + set_rec_bits((uchar) *from, bit_ptr, bit_ofs, bit_len); + from++; + } + memcpy(ptr, from, field_length); + } + return 0; +} + + +int Field_bit::store(double nr) +{ + return (Field_bit::store((longlong) nr)); +} + + +int Field_bit::store(longlong nr) +{ + char buf[8]; + + mi_int8store(buf, nr); + return store(buf, 8, NULL); +} + + +double Field_bit::val_real(void) +{ + return (double) Field_bit::val_int(); +} + + +longlong Field_bit::val_int(void) +{ + ulonglong bits= 0; + if (bit_len) + bits= get_rec_bits(bit_ptr, bit_ofs, bit_len); + bits<<= (field_length * 8); + + switch (field_length) { + case 0: return bits; + case 1: return bits | (ulonglong) (uchar) ptr[0]; + case 2: return bits | mi_uint2korr(ptr); + case 3: return bits | mi_uint3korr(ptr); + case 4: return bits | mi_uint4korr(ptr); + case 5: return bits | mi_uint5korr(ptr); + case 6: return bits | mi_uint6korr(ptr); + case 7: return bits | mi_uint7korr(ptr); + default: return mi_uint8korr(ptr + field_length - sizeof(longlong)); + } +} + + +String *Field_bit::val_str(String *val_buffer, + String *val_ptr __attribute__((unused))) +{ + uint length= min(pack_length(), sizeof(longlong)); + ulonglong bits= val_int(); + + val_buffer->alloc(length); + memcpy_fixed((char*) val_buffer->ptr(), (char*) &bits, length); + val_buffer->length(length); + val_buffer->set_charset(&my_charset_bin); + return val_buffer; +} + + +int Field_bit::key_cmp(const byte *str, uint length) +{ + if (bit_len) + { + int flag; + uchar bits= get_rec_bits(bit_ptr, bit_ofs, bit_len); + if ((flag= (int) (bits - *str))) + return flag; + str++; + length--; + } + return bcmp(ptr, str, length); +} + + +int Field_bit::cmp_offset(uint row_offset) +{ + if (bit_len) + { + int flag; + uchar bits_a= get_rec_bits(bit_ptr, bit_ofs, bit_len); + uchar bits_b= get_rec_bits(bit_ptr + row_offset, bit_ofs, bit_len); + if ((flag= (int) (bits_a - bits_b))) + return flag; + } + return bcmp(ptr, ptr + row_offset, field_length); +} + + +void Field_bit::get_key_image(char *buff, uint length, imagetype type) +{ + if (bit_len) + { + uchar bits= get_rec_bits(bit_ptr, bit_ofs, bit_len); + *buff++= bits; + length--; + } + memcpy(buff, ptr, min(length, field_length)); +} + + +void Field_bit::sql_type(String &res) const +{ + CHARSET_INFO *cs= res.charset(); + ulong length= cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(), + "bit(%d)", + (int) field_length * 8 + bit_len); + res.length((uint) length); +} + + +char *Field_bit::pack(char *to, const char *from, uint max_length) +{ + uint length= min(field_length + (bit_len > 0), max_length); + if (bit_len) + { + uchar bits= get_rec_bits(bit_ptr, bit_ofs, bit_len); + *to++= bits; + length--; + } + memcpy(to, from, length); + return to + length; +} + + +const char *Field_bit::unpack(char *to, const char *from) +{ + if (bit_len) + { + set_rec_bits(*from, bit_ptr, bit_ofs, bit_len); + from++; + } + memcpy(to, from, field_length); + return from + field_length; +} + + /***************************************************************************** Handling of field and create_field *****************************************************************************/ @@ -6124,6 +6345,7 @@ uint32 calc_pack_length(enum_field_types type,uint32 length) case FIELD_TYPE_GEOMETRY: return 4+portable_sizeof_char_ptr; case FIELD_TYPE_SET: case FIELD_TYPE_ENUM: abort(); return 0; // This shouldn't happen + case FIELD_TYPE_BIT: return length / 8; default: return 0; } return 0; // Keep compiler happy @@ -6154,11 +6376,30 @@ Field *make_field(char *ptr, uint32 field_length, const char *field_name, struct st_table *table) { + uchar *bit_ptr; + uchar bit_offset; + LINT_INIT(bit_ptr); + LINT_INIT(bit_offset); + if (field_type == FIELD_TYPE_BIT) + { + bit_ptr= null_pos; + bit_offset= null_bit; + if (f_maybe_null(pack_flag)) // if null field + { + bit_ptr+= (null_bit == 7); // shift bit_ptr and bit_offset + bit_offset= (bit_offset + 1) & 7; + } + } + if (!f_maybe_null(pack_flag)) { null_pos=0; null_bit=0; } + else + { + null_bit= ((uchar) 1) << null_bit; + } switch (field_type) { @@ -6280,6 +6521,9 @@ Field *make_field(char *ptr, uint32 field_length, unireg_check, field_name, table, field_charset); case FIELD_TYPE_NULL: return new Field_null(ptr,field_length,unireg_check,field_name,table, field_charset); + case FIELD_TYPE_BIT: + return new Field_bit(ptr, field_length, null_pos, null_bit, bit_ptr, + bit_offset, unireg_check, field_name, table); default: // Impossible (Wrong version) break; } @@ -6338,6 +6582,9 @@ create_field::create_field(Field *old_field,Field *orig_field) geom_type= ((Field_geom*)old_field)->geom_type; break; #endif + case FIELD_TYPE_BIT: + length= ((Field_bit *) old_field)->bit_len + length * 8; + break; default: break; } diff --git a/sql/field.h b/sql/field.h index 90827c44e45..de31f60350d 100644 --- a/sql/field.h +++ b/sql/field.h @@ -80,7 +80,7 @@ public: FIELD_CAST_TIMESTAMP, FIELD_CAST_YEAR, FIELD_CAST_DATE, FIELD_CAST_NEWDATE, FIELD_CAST_TIME, FIELD_CAST_DATETIME, FIELD_CAST_STRING, FIELD_CAST_VARSTRING, FIELD_CAST_BLOB, - FIELD_CAST_GEOM, FIELD_CAST_ENUM, FIELD_CAST_SET + FIELD_CAST_GEOM, FIELD_CAST_ENUM, FIELD_CAST_SET, FIELD_CAST_BIT }; utype unireg_check; @@ -1182,6 +1182,53 @@ public: }; +class Field_bit :public Field { +public: + uchar *bit_ptr; // position in record where 'uneven' bits store + uchar bit_ofs; // offset to 'uneven' high bits + uint bit_len; // number of 'uneven' high bits + Field_bit(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, + uchar null_bit_arg, uchar *bit_ptr_arg, uchar bit_ofs_arg, + enum utype unireg_check_arg, const char *field_name_arg, + struct st_table *table_arg) + : Field(ptr_arg, len_arg >> 3, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg, table_arg), + bit_ptr(bit_ptr_arg), bit_ofs(bit_ofs_arg), bit_len(len_arg & 7) + { } + enum_field_types type() const { return FIELD_TYPE_BIT; } + enum ha_base_keytype key_type() const { return HA_KEYTYPE_BIT; } + uint32 key_length() const { return (uint32) field_length + (bit_len > 0); } + uint32 max_length() { return (uint32) field_length + (bit_len > 0); } + uint size_of() const { return sizeof(*this); } + Item_result result_type () const { return INT_RESULT; } + void make_field(Send_field *); + void reset(void) { bzero(ptr, field_length); } + int store(const char *to, uint length, CHARSET_INFO *charset); + int store(double nr); + int store(longlong nr); + double val_real(void); + longlong val_int(void); + String *val_str(String*, String *); + int cmp(const char *a, const char *b) + { return cmp_binary(a, b); } + int key_cmp(const byte *a, const byte *b) + { return cmp_binary(a, b); } + int key_cmp(const byte *str, uint length); + int cmp_offset(uint row_offset); + void get_key_image(char *buff, uint length, imagetype type); + void set_key_image(char *buff, uint length) + { Field_bit::store(buff, length, &my_charset_bin); } + void sort_string(char *buff, uint length) + { get_key_image(buff, length, itRAW); } + uint32 pack_length() const + { return (uint32) field_length + (bit_len > 0); } + void sql_type(String &str) const; + field_cast_enum field_cast_type() { return FIELD_CAST_BIT; } + char *pack(char *to, const char *from, uint max_length=~(uint) 0); + const char *unpack(char* to, const char *from); +}; + + /* Create field class for CREATE TABLE */ diff --git a/sql/field_conv.cc b/sql/field_conv.cc index f6cc851639a..f0887d8343a 100644 --- a/sql/field_conv.cc +++ b/sql/field_conv.cc @@ -485,6 +485,9 @@ void (*Copy_field::get_copy_func(Field *to,Field *from))(Copy_field*) } else { + if (to->real_type() == FIELD_TYPE_BIT || + from->real_type() == FIELD_TYPE_BIT) + return do_field_int; // Check if identical fields if (from->result_type() == STRING_RESULT) { diff --git a/sql/ha_myisam.cc b/sql/ha_myisam.cc index 726647cd131..c1516e61c88 100644 --- a/sql/ha_myisam.cc +++ b/sql/ha_myisam.cc @@ -1428,6 +1428,13 @@ int ha_myisam::create(const char *name, register TABLE *table_arg, keydef[i].seg[j].bit_start= (uint) (field->pack_length() - table_arg->blob_ptr_size); } + else if (field->type() == FIELD_TYPE_BIT) + { + keydef[i].seg[j].bit_length= ((Field_bit *) field)->bit_len; + keydef[i].seg[j].bit_start= ((Field_bit *) field)->bit_ofs; + keydef[i].seg[j].bit_pos= (uint) (((Field_bit *) field)->bit_ptr - + (uchar*) table_arg->record[0]); + } } keyseg+=pos->key_parts; } diff --git a/sql/ha_myisam.h b/sql/ha_myisam.h index 527e6a49aba..d2fe36c8357 100644 --- a/sql/ha_myisam.h +++ b/sql/ha_myisam.h @@ -47,7 +47,7 @@ class ha_myisam: public handler int_table_flags(HA_NULL_IN_KEY | HA_CAN_FULLTEXT | HA_CAN_SQL_HANDLER | HA_DUPP_POS | HA_CAN_INDEX_BLOBS | HA_AUTO_PART_KEY | HA_FILE_BASED | HA_CAN_GEOMETRY | HA_READ_RND_SAME | - HA_CAN_INSERT_DELAYED), + HA_CAN_INSERT_DELAYED | HA_CAN_BIT_FIELD), can_enable_indexes(1) {} ~ha_myisam() {} diff --git a/sql/handler.h b/sql/handler.h index c5c2d6d0508..3ee66e4b3cf 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -75,6 +75,7 @@ /* Table data are stored in separate files (for lower_case_table_names) */ #define HA_FILE_BASED (1 << 26) #define HA_NO_VARCHAR (1 << 27) +#define HA_CAN_BIT_FIELD (1 << 28) /* supports bit fields */ /* bits in index_flags(index_number) for what you can do with index */ diff --git a/sql/item.cc b/sql/item.cc index f00a35fe628..9117105f26e 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -2573,11 +2573,11 @@ void Item_real::print(String *str) } -/**************************************************************************** -** varbinary item -** In string context this is a binary string -** In number context this is a longlong value. -****************************************************************************/ +/* + hex item + In string context this is a binary string. + In number context this is a longlong value. +*/ inline uint char_val(char X) { @@ -2587,7 +2587,7 @@ inline uint char_val(char X) } -Item_varbinary::Item_varbinary(const char *str, uint str_length) +Item_hex_string::Item_hex_string(const char *str, uint str_length) { name=(char*) str-2; // Lex makes this start with 0x max_length=(str_length+1)/2; @@ -2608,7 +2608,7 @@ Item_varbinary::Item_varbinary(const char *str, uint str_length) fixed= 1; } -longlong Item_varbinary::val_int() +longlong Item_hex_string::val_int() { // following assert is redundant, because fixed=1 assigned in constructor DBUG_ASSERT(fixed == 1); @@ -2622,7 +2622,7 @@ longlong Item_varbinary::val_int() } -int Item_varbinary::save_in_field(Field *field, bool no_conversions) +int Item_hex_string::save_in_field(Field *field, bool no_conversions) { int error; field->set_notnull(); @@ -2639,6 +2639,44 @@ int Item_varbinary::save_in_field(Field *field, bool no_conversions) } +/* + bin item. + In string context this is a binary string. + In number context this is a longlong value. +*/ + +Item_bin_string::Item_bin_string(const char *str, uint str_length) +{ + const char *end= str + str_length - 1; + uchar bits= 0; + uint power= 1; + + name= (char*) str - 2; + max_length= (str_length + 7) >> 3; + char *ptr= (char*) sql_alloc(max_length + 1); + if (!ptr) + return; + str_value.set(ptr, max_length, &my_charset_bin); + ptr+= max_length - 1; + ptr[1]= 0; // Set end null for string + for (; end >= str; end--) + { + if (power == 256) + { + power= 1; + *ptr--= bits; + bits= 0; + } + if (*end == '1') + bits|= power; + power<<= 1; + } + *ptr= (char) bits; + collation.set(&my_charset_bin, DERIVATION_COERCIBLE); + fixed= 1; +} + + /* Pack data in buffer for sending */ @@ -2672,6 +2710,7 @@ bool Item::send(Protocol *protocol, String *buffer) case MYSQL_TYPE_STRING: case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_BIT: { String *res; if ((res=val_str(buffer))) diff --git a/sql/item.h b/sql/item.h index cf3dc8896a5..d5361bdcc8a 100644 --- a/sql/item.h +++ b/sql/item.h @@ -959,13 +959,14 @@ public: }; -class Item_varbinary :public Item +class Item_hex_string: public Item { public: - Item_varbinary(const char *str,uint str_length); + Item_hex_string(): Item() {} + Item_hex_string(const char *str,uint str_length); enum Type type() const { return VARBIN_ITEM; } double val_real() - { DBUG_ASSERT(fixed == 1); return (double) Item_varbinary::val_int(); } + { DBUG_ASSERT(fixed == 1); return (double) Item_hex_string::val_int(); } longlong val_int(); bool basic_const_item() const { return 1; } String *val_str(String*) { DBUG_ASSERT(fixed == 1); return &str_value; } @@ -977,6 +978,12 @@ public: }; +class Item_bin_string: public Item_hex_string +{ +public: + Item_bin_string(const char *str,uint str_length); +}; + class Item_result_field :public Item /* Item with result field */ { public: diff --git a/sql/key.cc b/sql/key.cc index dfd924f1dc7..fe35638608d 100644 --- a/sql/key.cc +++ b/sql/key.cc @@ -102,6 +102,19 @@ void key_copy(byte *to_key, byte *from_record, KEY *key_info, uint key_length) key_part->null_bit); key_length--; } + if (key_part->type == HA_KEYTYPE_BIT) + { + Field_bit *field= (Field_bit *) (key_part->field); + if (field->bit_len) + { + uchar bits= get_rec_bits((uchar*) from_record + + key_part->null_offset + + (key_part->null_bit == 128), + field->bit_ofs, field->bit_len); + *to_key++= bits; + key_length--; + } + } if (key_part->key_part_flag & HA_BLOB_PART) { char *pos; @@ -170,6 +183,23 @@ void key_restore(byte *to_record, byte *from_key, KEY *key_info, to_record[key_part->null_offset]&= ~key_part->null_bit; key_length--; } + if (key_part->type == HA_KEYTYPE_BIT) + { + Field_bit *field= (Field_bit *) (key_part->field); + if (field->bit_len) + { + uchar bits= *(from_key + key_part->length - field->field_length -1); + set_rec_bits(bits, to_record + key_part->null_offset + + (key_part->null_bit == 128), + field->bit_ofs, field->bit_len); + } + else + { + clr_rec_bits(to_record + key_part->null_offset + + (key_part->null_bit == 128), + field->bit_ofs, field->bit_len); + } + } if (key_part->key_part_flag & HA_BLOB_PART) { uint blob_length= uint2korr(from_key); diff --git a/sql/protocol.cc b/sql/protocol.cc index d2e63539610..4c916d78378 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -726,6 +726,7 @@ bool Protocol_simple::store(const char *from, uint length, #ifndef DEBUG_OFF DBUG_ASSERT(field_types == 0 || field_types[field_pos] == MYSQL_TYPE_DECIMAL || + field_types[field_pos] == MYSQL_TYPE_BIT || (field_types[field_pos] >= MYSQL_TYPE_ENUM && field_types[field_pos] <= MYSQL_TYPE_GEOMETRY)); field_pos++; @@ -741,6 +742,7 @@ bool Protocol_simple::store(const char *from, uint length, #ifndef DEBUG_OFF DBUG_ASSERT(field_types == 0 || field_types[field_pos] == MYSQL_TYPE_DECIMAL || + field_types[field_pos] == MYSQL_TYPE_BIT || (field_types[field_pos] >= MYSQL_TYPE_ENUM && field_types[field_pos] <= MYSQL_TYPE_GEOMETRY)); field_pos++; diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 12e4d912f15..2205ec504e9 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -567,8 +567,12 @@ int yylex(void *arg, void *yythd) state= MY_LEX_HEX_NUMBER; break; } - /* Fall through */ - case MY_LEX_IDENT_OR_BIN: // TODO: Add binary string handling + case MY_LEX_IDENT_OR_BIN: + if (yyPeek() == '\'') + { // Found b'bin-number' + state= MY_LEX_BIN_NUMBER; + break; + } case MY_LEX_IDENT: uchar *start; #if defined(USE_MB) && defined(USE_MB_IDENT) @@ -689,6 +693,20 @@ int yylex(void *arg, void *yythd) } yyUnget(); } + else if (c == 'b' && (lex->ptr - lex->tok_start) == 2 && + lex->tok_start[0] == '0' ) + { // b'bin-number' + while (my_isxdigit(cs,(c = yyGet()))) ; + if ((lex->ptr - lex->tok_start) >= 4 && !ident_map[c]) + { + yylval->lex_str= get_token(lex, yyLength()); + yylval->lex_str.str+= 2; // Skip 0x + yylval->lex_str.length-= 2; + lex->yytoklen-= 2; + return (BIN_NUM); + } + yyUnget(); + } // fall through case MY_LEX_IDENT_START: // We come here after '.' result_state= IDENT; @@ -801,6 +819,19 @@ int yylex(void *arg, void *yythd) lex->yytoklen-=3; return (HEX_NUM); + case MY_LEX_BIN_NUMBER: // Found b'bin-string' + yyGet(); // Skip ' + while ((c= yyGet()) == '0' || c == '1'); + length= (lex->ptr - lex->tok_start); // Length of bin-num + 3 + if (c != '\'') + return(ABORT_SYM); // Illegal hex constant + yyGet(); // get_token makes an unget + yylval->lex_str= get_token(lex, length); + yylval->lex_str.str+= 2; // Skip b' + yylval->lex_str.length-= 3; // Don't count b' and last ' + lex->yytoklen-= 3; + return (BIN_NUM); + case MY_LEX_CMP_OP: // Incomplete comparison operator if (state_map[yyPeek()] == MY_LEX_CMP_OP || state_map[yyPeek()] == MY_LEX_LONG_CMP_OP) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 3d4252a2b17..5720aac8dbd 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -5063,6 +5063,19 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, case MYSQL_TYPE_VAR_STRING: DBUG_ASSERT(0); // Impossible break; + case MYSQL_TYPE_BIT: + { + if (!length) + new_field->length= 1; + if (new_field->length > MAX_BIT_FIELD_LENGTH) + { + my_error(ER_TOO_BIG_FIELDLENGTH, MYF(0), field_name, + MAX_BIT_FIELD_LENGTH); + DBUG_RETURN(1); + } + new_field->pack_length= (new_field->length + 7) / 8; + break; + } } if (!(new_field->flags & BLOB_FLAG) && diff --git a/sql/sql_select.cc b/sql/sql_select.cc index e9097370664..8fe83e4f1d7 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -7777,6 +7777,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List &fields, KEY_PART_INFO *key_part_info; Item **copy_func; MI_COLUMNDEF *recinfo; + uint total_uneven_bit_length= 0; DBUG_ENTER("create_tmp_table"); DBUG_PRINT("enter",("distinct: %d save_sum_fields: %d rows_limit: %lu group: %d", (int) distinct, (int) save_sum_fields, @@ -7966,6 +7967,8 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List &fields, reclength+=new_field->pack_length(); if (!(new_field->flags & NOT_NULL_FLAG)) null_count++; + if (new_field->type() == FIELD_TYPE_BIT) + total_uneven_bit_length+= new_field->field_length & 7; if (new_field->flags & BLOB_FLAG) { *blob_field++= new_field; @@ -8014,7 +8017,8 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List &fields, null_count++; } hidden_null_pack_length=(hidden_null_count+7)/8; - null_pack_length=hidden_null_count+(null_count+7)/8; + null_pack_length= hidden_null_count + + (null_count + total_uneven_bit_length + 7) / 8; reclength+=null_pack_length; if (!reclength) reclength=1; // Dummy select diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 56605d1c6e0..8662eb63ce6 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -458,6 +458,7 @@ int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, int field_no,dup_no; int select_field_pos,auto_increment=0; List_iterator it(fields),it2(fields); + uint total_uneven_bit_length= 0; DBUG_ENTER("mysql_prepare_table"); select_field_pos=fields.elements - select_field_count; @@ -614,6 +615,9 @@ int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, if (!(sql_field->flags & NOT_NULL_FLAG)) null_fields++; + if (sql_field->sql_type == FIELD_TYPE_BIT) + total_uneven_bit_length+= sql_field->length & 7; + if (check_column_name(sql_field->field_name)) { my_error(ER_WRONG_COLUMN_NAME, MYF(0), sql_field->field_name); @@ -666,7 +670,7 @@ int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, /* If fixed row records, we need one bit to check for deleted rows */ if (!(db_options & HA_OPTION_PACK_RECORD)) null_fields++; - pos=(null_fields+7)/8; + pos= (null_fields + total_uneven_bit_length + 7) / 8; it.rewind(); while ((sql_field=it++)) @@ -762,6 +766,14 @@ int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, case FIELD_TYPE_NULL: sql_field->pack_flag=f_settype((uint) sql_field->sql_type); break; + case FIELD_TYPE_BIT: + if (!(file->table_flags() & HA_CAN_BIT_FIELD)) + { + my_error(ER_CHECK_NOT_IMPLEMENTED, MYF(0), "BIT FIELD"); + DBUG_RETURN(-1); + } + sql_field->pack_flag= FIELDFLAG_NUMBER; + break; case FIELD_TYPE_TIMESTAMP: /* We should replace old TIMESTAMP fields with their newer analogs */ if (sql_field->unireg_check == Field::TIMESTAMP_OLD_FIELD) diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 3511777dd27..55a0f6ff692 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -208,6 +208,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token BACKUP_SYM %token BERKELEY_DB_SYM %token BINARY +%token BIN_NUM %token BIT_SYM %token BOOL_SYM %token BOOLEAN_SYM @@ -664,7 +665,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); LEX_HOSTNAME ULONGLONG_NUM field_ident select_alias ident ident_or_text UNDERSCORE_CHARSET IDENT_sys TEXT_STRING_sys TEXT_STRING_literal NCHAR_STRING opt_component key_cache_name - sp_opt_label + sp_opt_label BIN_NUM %type opt_table_alias @@ -2750,8 +2751,10 @@ type: int_type opt_len field_options { $$=$1; } | real_type opt_precision field_options { $$=$1; } | FLOAT_SYM float_options field_options { $$=FIELD_TYPE_FLOAT; } - | BIT_SYM opt_len { Lex->length=(char*) "1"; - $$=FIELD_TYPE_TINY; } + | BIT_SYM { Lex->length= (char*) "1"; + $$=FIELD_TYPE_BIT; } + | BIT_SYM '(' NUM ')' { Lex->length= $3.str; + $$=FIELD_TYPE_BIT; } | BOOL_SYM { Lex->length=(char*) "1"; $$=FIELD_TYPE_TINY; } | BOOLEAN_SYM { Lex->length=(char*) "1"; @@ -6458,15 +6461,25 @@ text_string: { $$= new (YYTHD->mem_root) String($1.str,$1.length,YYTHD->variables.collation_connection); } | HEX_NUM { - Item *tmp = new Item_varbinary($1.str,$1.length); + Item *tmp= new Item_hex_string($1.str, $1.length); /* - it is OK only emulate fix_fieds, because we need only + it is OK only emulate fix_fields, because we need only value of constant */ $$= tmp ? tmp->quick_fix_field(), tmp->val_str((String*) 0) : (String*) 0; } + | BIN_NUM + { + Item *tmp= new Item_bin_string($1.str, $1.length); + /* + it is OK only emulate fix_fields, because we need only + value of constant + */ + $$= tmp ? tmp->quick_fix_field(), tmp->val_str((String*) 0) : + (String*) 0; + } ; param_marker: @@ -6508,10 +6521,11 @@ literal: | NUM_literal { $$ = $1; } | NULL_SYM { $$ = new Item_null(); Lex->next_state=MY_LEX_OPERATOR_OR_IDENT;} - | HEX_NUM { $$ = new Item_varbinary($1.str,$1.length);} + | HEX_NUM { $$ = new Item_hex_string($1.str, $1.length);} + | BIN_NUM { $$= new Item_bin_string($1.str, $1.length); } | UNDERSCORE_CHARSET HEX_NUM { - Item *tmp= new Item_varbinary($2.str,$2.length); + Item *tmp= new Item_hex_string($2.str, $2.length); /* it is OK only emulate fix_fieds, because we need only value of constant @@ -6523,6 +6537,20 @@ literal: str ? str->length() : 0, Lex->charset); } + | UNDERSCORE_CHARSET BIN_NUM + { + Item *tmp= new Item_bin_string($2.str, $2.length); + /* + it is OK only emulate fix_fieds, because we need only + value of constant + */ + String *str= tmp ? + tmp->quick_fix_field(), tmp->val_str((String*) 0) : + (String*) 0; + $$= new Item_string(str ? str->ptr() : "", + str ? str->length() : 0, + Lex->charset); + } | DATE_SYM text_literal { $$ = $2; } | TIME_SYM text_literal { $$ = $2; } | TIMESTAMP text_literal { $$ = $2; }; diff --git a/sql/table.cc b/sql/table.cc index b54cc351dff..610de9e4e9b 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -81,7 +81,7 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, KEY *keyinfo; KEY_PART_INFO *key_part; uchar *null_pos; - uint null_bit, new_frm_ver, field_pack_length; + uint null_bit_pos, new_frm_ver, field_pack_length; SQL_CRYPT *crypted=0; MEM_ROOT **root_ptr, *old_root; DBUG_ENTER("openfrm"); @@ -409,15 +409,15 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, if (null_field_first) { outparam->null_flags=null_pos=(uchar*) record+1; - null_bit= (db_create_options & HA_OPTION_PACK_RECORD) ? 1 : 2; - outparam->null_bytes=(outparam->null_fields+null_bit+6)/8; + null_bit_pos= (db_create_options & HA_OPTION_PACK_RECORD) ? 0 : 1; + outparam->null_bytes= (outparam->null_fields + null_bit_pos + 7) / 8; } else { outparam->null_bytes=(outparam->null_fields+7)/8; outparam->null_flags=null_pos= (uchar*) (record+1+outparam->reclength-outparam->null_bytes); - null_bit=1; + null_bit_pos= 0; } use_hash= outparam->fields >= MAX_FIELDS_BEFORE_HASH; @@ -512,7 +512,7 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, *field_ptr=reg_field= make_field(record+recpos, (uint32) field_length, - null_pos,null_bit, + null_pos, null_bit_pos, pack_flag, field_type, charset, @@ -529,13 +529,18 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, goto err_not_open; /* purecov: inspected */ } reg_field->comment=comment; + if (field_type == FIELD_TYPE_BIT) + { + if ((null_bit_pos+= field_length & 7) > 7) + { + null_pos++; + null_bit_pos-= 8; + } + } if (!(reg_field->flags & NOT_NULL_FLAG)) { - if ((null_bit<<=1) == 256) - { - null_pos++; - null_bit=1; - } + if (!(null_bit_pos= (null_bit_pos + 1) & 7)) + null_pos++; } if (f_no_default(pack_flag)) reg_field->flags|= NO_DEFAULT_VALUE_FLAG; diff --git a/sql/unireg.cc b/sql/unireg.cc index 636156940a4..dbd3da58a33 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -652,7 +652,7 @@ static bool make_empty_rec(File file,enum db_type table_type, Field *regfield=make_field((char*) buff+field->offset,field->length, field->flags & NOT_NULL_FLAG ? 0: null_pos+null_count/8, - 1 << (null_count & 7), + null_count & 7, field->pack_flag, field->sql_type, field->charset, diff --git a/sql/unireg.h b/sql/unireg.h index 31b28da2423..932bdf4dfc5 100644 --- a/sql/unireg.h +++ b/sql/unireg.h @@ -66,6 +66,8 @@ /* Max column width +1 */ #define MAX_FIELD_WIDTH (MAX_FIELD_CHARLENGTH*MAX_MBWIDTH+1) +#define MAX_BIT_FIELD_LENGTH 64 /* Max length in bits for bit fields */ + #define MAX_DATE_WIDTH 10 /* YYYY-MM-DD */ #define MAX_TIME_WIDTH 23 /* -DDDDDD HH:MM:SS.###### */ #define MAX_DATETIME_FULL_WIDTH 29 /* YYYY-MM-DD HH:MM:SS.###### AM */