Commit 08526ba3 authored by unknown's avatar unknown

Changes for new binary .frm format

Fixes after last merge from 4.0.
(Code not yet complete, need anoter merge from 4.0)


heap/hp_write.c:
  cleanup
myisam/ft_boolean_search.c:
  Fixed tree handling to new format
mysql-test/r/alter_table.result:
  SHOW FULL COLUMN FROM TABLE now returns comment
mysql-test/r/func_math.result:
  Updated results
mysql-test/r/heap_btree.result:
  Portability fix
mysql-test/r/isam.result:
  SHOW FULL COLUMN FROM TABLE now returns comment
mysql-test/r/show_check.result:
  SHOW FULL COLUMN FROM TABLE now returns comment
mysql-test/t/heap_btree.test:
  Portability fix
mysql-test/t/show_check.test:
  SHOW FULL COLUMN FROM TABLE now returns comment
sql/field.cc:
  Fix for comment handling
sql/field.h:
  Added CHARSET_INFO to field structure
sql/item_cmpfunc.cc:
  Fixed like to use system charset (need to be updated)
sql/item_func.cc:
  Update to new charset handling
sql/mysql_priv.h:
  cleanup
sql/sql_base.cc:
  Added charset to HA_CREATE_INFO
sql/sql_delete.cc:
  Added charset to HA_CREATE_INFO
sql/sql_parse.cc:
  Added charset to HA_CREATE_INFO
sql/sql_select.cc:
  cleanup
sql/sql_show.cc:
  charset change
sql/sql_string.h:
  cleanup
sql/sql_table.cc:
  cleanup
sql/sql_yacc.yy:
  Go back to old code for ALTER table ... MODIFY
sql/table.cc:
  fixed comment handling
sql/unireg.cc:
  new field format
parent f0409fa9
...@@ -89,7 +89,7 @@ int heap_write(HP_INFO *info, const byte *record) ...@@ -89,7 +89,7 @@ int heap_write(HP_INFO *info, const byte *record)
*/ */
int hp_rb_write_key(HP_INFO *info, HP_KEYDEF *keyinfo, const byte *record, int hp_rb_write_key(HP_INFO *info, HP_KEYDEF *keyinfo, const byte *record,
byte *recpos) byte *recpos)
{ {
heap_rb_param custom_arg; heap_rb_param custom_arg;
......
...@@ -426,10 +426,11 @@ int ft_boolean_read_next(FT_INFO *ftb, char *record) ...@@ -426,10 +426,11 @@ int ft_boolean_read_next(FT_INFO *ftb, char *record)
if (!ftb->queue.elements) if (!ftb->queue.elements)
return my_errno=HA_ERR_END_OF_FILE; return my_errno=HA_ERR_END_OF_FILE;
while(ftb->state == INDEX_SEARCH && while (ftb->state == INDEX_SEARCH &&
(curdoc=((FTB_WORD *)queue_top(& ftb->queue))->docid[0]) != HA_POS_ERROR) (curdoc=((FTB_WORD *)queue_top(& ftb->queue))->docid[0]) !=
HA_POS_ERROR)
{ {
while (curdoc==(ftbw=(FTB_WORD *)queue_top(& ftb->queue))->docid[0]) while (curdoc == (ftbw=(FTB_WORD *)queue_top(& ftb->queue))->docid[0])
{ {
_ftb_climb_the_tree(ftb, ftbw, 0); _ftb_climb_the_tree(ftb, ftbw, 0);
...@@ -467,13 +468,15 @@ int ft_boolean_read_next(FT_INFO *ftb, char *record) ...@@ -467,13 +468,15 @@ int ft_boolean_read_next(FT_INFO *ftb, char *record)
ftbe->yesses>=(ftbe->ythresh-ftbe->yweaks) && !ftbe->nos) ftbe->yesses>=(ftbe->ythresh-ftbe->yweaks) && !ftbe->nos)
{ {
/* curdoc matched ! */ /* curdoc matched ! */
if (is_tree_inited(& ftb->no_dupes) && if (is_tree_inited(&ftb->no_dupes) &&
tree_insert(& ftb->no_dupes, &curdoc, 0)->count >1) tree_insert(&ftb->no_dupes, &curdoc, 0,
ftb->no_dupes.custom_arg)->count >1)
/* but it managed to get past this line once */ /* but it managed to get past this line once */
continue; continue;
info->lastpos=curdoc; info->lastpos=curdoc;
info->update&= (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED); /* why is this ? */ /* Clear all states, except that the table was updated */
info->update&= (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
if (!(*info->read_record)(info,curdoc,record)) if (!(*info->read_record)(info,curdoc,record))
{ {
......
...@@ -50,10 +50,10 @@ PRIMARY KEY (GROUP_ID,LANG_ID), ...@@ -50,10 +50,10 @@ PRIMARY KEY (GROUP_ID,LANG_ID),
KEY NAME (NAME)); KEY NAME (NAME));
ALTER TABLE t1 CHANGE NAME NAME CHAR(80) not null; ALTER TABLE t1 CHANGE NAME NAME CHAR(80) not null;
SHOW FULL COLUMNS FROM t1; SHOW FULL COLUMNS FROM t1;
Field Type Null Key Default Extra Privileges Field Type Null Key Default Extra Privileges Comment
GROUP_ID int(10) unsigned PRI 0 select,insert,update,references GROUP_ID int(10) unsigned PRI 0 select,insert,update,references
LANG_ID smallint(5) unsigned PRI 0 select,insert,update,references LANG_ID smallint(5) unsigned PRI 0 select,insert,update,references
NAME char(80) MUL select,insert,update,references NAME char(80) MUL select,insert,update,references
DROP TABLE t1; DROP TABLE t1;
create table t1 (n int); create table t1 (n int);
insert into t1 values(9),(3),(12),(10); insert into t1 values(9),(3),(12),(10);
......
...@@ -4,8 +4,10 @@ floor(5.5) floor(-5.5) ...@@ -4,8 +4,10 @@ floor(5.5) floor(-5.5)
select ceiling(5.5),ceiling(-5.5); select ceiling(5.5),ceiling(-5.5);
ceiling(5.5) ceiling(-5.5) ceiling(5.5) ceiling(-5.5)
6 -5 6 -5
select truncate(52.64,1),truncate(52.64,2),truncate(52.64,-1),truncate(52.64,-2), truncate(-52.64,1),truncate(-52.64,-1);
truncate(52.64,1) truncate(52.64,2) truncate(52.64,-1) truncate(52.64,-2) truncate(-52.64,1) truncate(-52.64,-1) truncate(52.64,1) truncate(52.64,2) truncate(52.64,-1) truncate(52.64,-2) truncate(-52.64,1) truncate(-52.64,-1)
52.6 52.64 50 0 -52.6 -50 52.6 52.64 50 0 -52.6 -50
select round(5.5),round(-5.5);
round(5.5) round(-5.5) round(5.5) round(-5.5)
6 -6 6 -6
select round(5.64,1),round(5.64,2),round(5.64,-1),round(5.64,-2); select round(5.64,1),round(5.64,2),round(5.64,-1),round(5.64,-2);
......
...@@ -94,7 +94,7 @@ select max(a) from t1; ...@@ -94,7 +94,7 @@ select max(a) from t1;
max(a) max(a)
1 1
drop table t1; drop table t1;
CREATE TABLE t1 ( a int not null default 0, b int not null default 0, key using BTREE (a), key using BTREE (b) ) TYPE=HEAP; CREATE TABLE t1 ( a int not null default 0, b int not null default 0, key using BTREE (a,b), key using BTREE (b) ) TYPE=HEAP;
insert into t1 values(1,1),(1,2),(2,3),(1,3),(1,4),(1,5),(1,6); insert into t1 values(1,1),(1,2),(2,3),(1,3),(1,4),(1,5),(1,6);
select * from t1 where a=1; select * from t1 where a=1;
a b a b
...@@ -108,17 +108,30 @@ insert into t1 values(1,1),(1,2),(2,3),(1,3),(1,4),(1,5),(1,6); ...@@ -108,17 +108,30 @@ insert into t1 values(1,1),(1,2),(2,3),(1,3),(1,4),(1,5),(1,6);
select * from t1 where a=1; select * from t1 where a=1;
a b a b
1 1 1 1
1 2
1 3
1 4
1 5
1 6
1 1 1 1
1 2 1 2
1 2
1 3 1 3
1 3
1 4
1 4 1 4
1 5 1 5
1 5
1 6
1 6 1 6
explain select * from tx where a=x order by a,b;
table type possible_keys key key_len ref rows Extra
tx ref a a x const x where used
explain select * from tx where a=x order by b;
table type possible_keys key key_len ref rows Extra
tx ref a a x const x where used
select * from t1 where b=1;
a b
1 1
1 1
explain select * from tx where b=x;
table type possible_keys key key_len ref rows Extra
tx ref b b x const x where used
drop table t1; drop table t1;
create table t1 (id int unsigned not null, primary key using BTREE (id)) type=HEAP; create table t1 (id int unsigned not null, primary key using BTREE (id)) type=HEAP;
insert into t1 values(1); insert into t1 values(1);
......
...@@ -67,10 +67,10 @@ a int(11) PRI 0 ...@@ -67,10 +67,10 @@ a int(11) PRI 0
b int(11) MUL 0 b int(11) MUL 0
c int(11) 0 c int(11) 0
show full columns from t1; show full columns from t1;
Field Type Null Key Default Extra Privileges Field Type Null Key Default Extra Privileges Comment
a int(11) PRI 0 select,insert,update,references a int(11) PRI 0 select,insert,update,references
b int(11) MUL 0 select,insert,update,references b int(11) MUL 0 select,insert,update,references
c int(11) 0 select,insert,update,references c int(11) 0 select,insert,update,references
show index from t1; show index from t1;
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment
t1 0 PRIMARY 1 a A 4 NULL NULL BTREE t1 0 PRIMARY 1 a A 4 NULL NULL BTREE
......
...@@ -87,14 +87,21 @@ t2 CREATE TEMPORARY TABLE `t2` ( ...@@ -87,14 +87,21 @@ t2 CREATE TEMPORARY TABLE `t2` (
drop table t2; drop table t2;
create table t1 ( create table t1 (
test_set set( 'val1', 'val2', 'val3' ) not null default '', test_set set( 'val1', 'val2', 'val3' ) not null default '',
name char(20) default 'O''Brien' name char(20) default 'O''Brien' comment 'O''Brien as default',
c int not null comment 'int column'
) comment = 'it\'s a table' ; ) comment = 'it\'s a table' ;
show create table t1 ; show create table t1 ;
Table Create Table Table Create Table
t1 CREATE TABLE `t1` ( t1 CREATE TABLE `t1` (
`test_set` set('val1','val2','val3') NOT NULL default '', `test_set` set('val1','val2','val3') NOT NULL default '',
`name` char(20) default 'O''Brien' `name` char(20) default 'O''Brien' COMMENT 'O''Brien as default',
`c` int(11) NOT NULL default '0' COMMENT 'int column'
) TYPE=MyISAM COMMENT='it''s a table' ) TYPE=MyISAM COMMENT='it''s a table'
show full columns from t1;
Field Type Null Key Default Extra Privileges Comment
test_set set('val1','val2','val3') select,insert,update,references
name char(20) YES O'Brien select,insert,update,references O'Brien as default
c int(11) 0 select,insert,update,references int column
drop table t1; drop table t1;
create table t1 (a int not null, unique aa (a)); create table t1 (a int not null, unique aa (a));
show create table t1; show create table t1;
...@@ -155,6 +162,14 @@ e double(9,2) YES NULL ...@@ -155,6 +162,14 @@ e double(9,2) YES NULL
f double(5,0) YES NULL f double(5,0) YES NULL
h float(3,2) YES NULL h float(3,2) YES NULL
i float(3,0) YES NULL i float(3,0) YES NULL
show full columns from t1;
Field Type Null Key Default Extra Privileges Comment
a decimal(9,2) YES NULL select,insert,update,references
b decimal(9,0) YES NULL select,insert,update,references
e double(9,2) YES NULL select,insert,update,references
f double(5,0) YES NULL select,insert,update,references
h float(3,2) YES NULL select,insert,update,references
i float(3,0) YES NULL select,insert,update,references
drop table t1; drop table t1;
create table t1 (c decimal, d double, f float, r real); create table t1 (c decimal, d double, f float, r real);
show columns from t1; show columns from t1;
......
...@@ -55,11 +55,18 @@ insert into t1 values(1); ...@@ -55,11 +55,18 @@ insert into t1 values(1);
select max(a) from t1; select max(a) from t1;
drop table t1; drop table t1;
CREATE TABLE t1 ( a int not null default 0, b int not null default 0, key using BTREE (a), key using BTREE (b) ) TYPE=HEAP; CREATE TABLE t1 ( a int not null default 0, b int not null default 0, key using BTREE (a,b), key using BTREE (b) ) TYPE=HEAP;
insert into t1 values(1,1),(1,2),(2,3),(1,3),(1,4),(1,5),(1,6); insert into t1 values(1,1),(1,2),(2,3),(1,3),(1,4),(1,5),(1,6);
select * from t1 where a=1; select * from t1 where a=1;
insert into t1 values(1,1),(1,2),(2,3),(1,3),(1,4),(1,5),(1,6); insert into t1 values(1,1),(1,2),(2,3),(1,3),(1,4),(1,5),(1,6);
select * from t1 where a=1; select * from t1 where a=1;
--replace_result 0 x 1 x 2 x 3 x 4 x 5 x 6 x 7 x 8 x 9 x
explain select * from t1 where a=1 order by a,b;
--replace_result 0 x 1 x 2 x 3 x 4 x 5 x 6 x 7 x 8 x 9 x
explain select * from t1 where a=1 order by b;
select * from t1 where b=1;
--replace_result 0 x 1 x 2 x 3 x 4 x 5 x 6 x 7 x 8 x 9 x
explain select * from t1 where b=1;
drop table t1; drop table t1;
create table t1 (id int unsigned not null, primary key using BTREE (id)) type=HEAP; create table t1 (id int unsigned not null, primary key using BTREE (id)) type=HEAP;
......
...@@ -49,9 +49,11 @@ drop table t2; ...@@ -49,9 +49,11 @@ drop table t2;
create table t1 ( create table t1 (
test_set set( 'val1', 'val2', 'val3' ) not null default '', test_set set( 'val1', 'val2', 'val3' ) not null default '',
name char(20) default 'O''Brien' name char(20) default 'O''Brien' comment 'O''Brien as default',
c int not null comment 'int column'
) comment = 'it\'s a table' ; ) comment = 'it\'s a table' ;
show create table t1 ; show create table t1 ;
show full columns from t1;
drop table t1; drop table t1;
create table t1 (a int not null, unique aa (a)); create table t1 (a int not null, unique aa (a));
...@@ -78,6 +80,7 @@ drop table t1; ...@@ -78,6 +80,7 @@ drop table t1;
create table t1 (a decimal(9,2), b decimal (9,0), e double(9,2), f double(5,0), h float(3,2), i float(3,0)); create table t1 (a decimal(9,2), b decimal (9,0), e double(9,2), f double(5,0), h float(3,2), i float(3,0));
show columns from t1; show columns from t1;
show full columns from t1;
drop table t1; drop table t1;
# Check auto conversions of types # Check auto conversions of types
......
...@@ -233,6 +233,8 @@ Field::Field(char *ptr_arg,uint32 length_arg,uchar *null_ptr_arg, ...@@ -233,6 +233,8 @@ Field::Field(char *ptr_arg,uint32 length_arg,uchar *null_ptr_arg,
field_length(length_arg),null_bit(null_bit_arg) field_length(length_arg),null_bit(null_bit_arg)
{ {
flags=null_ptr ? 0: NOT_NULL_FLAG; flags=null_ptr ? 0: NOT_NULL_FLAG;
comment.str= (char*) "";
comment.length=0;
} }
uint Field::offset() uint Field::offset()
...@@ -4861,6 +4863,8 @@ create_field::create_field(Field *old_field,Field *orig_field) ...@@ -4861,6 +4863,8 @@ create_field::create_field(Field *old_field,Field *orig_field)
unireg_check=old_field->unireg_check; unireg_check=old_field->unireg_check;
pack_length=old_field->pack_length(); pack_length=old_field->pack_length();
sql_type= old_field->real_type(); sql_type= old_field->real_type();
charset= old_field->charset(); // May be NULL ptr
comment= old_field->comment;
/* Fix if the original table had 4 byte pointer blobs */ /* Fix if the original table had 4 byte pointer blobs */
if (flags & BLOB_FLAG) if (flags & BLOB_FLAG)
...@@ -4869,6 +4873,7 @@ create_field::create_field(Field *old_field,Field *orig_field) ...@@ -4869,6 +4873,7 @@ create_field::create_field(Field *old_field,Field *orig_field)
decimals= old_field->decimals(); decimals= old_field->decimals();
if (sql_type == FIELD_TYPE_STRING) if (sql_type == FIELD_TYPE_STRING)
{ {
/* Change CHAR -> VARCHAR if dynamic record length */
sql_type=old_field->type(); sql_type=old_field->type();
decimals=0; decimals=0;
} }
......
...@@ -190,6 +190,7 @@ class Field { ...@@ -190,6 +190,7 @@ class Field {
uint fill_cache_field(struct st_cache_field *copy); uint fill_cache_field(struct st_cache_field *copy);
virtual bool get_date(TIME *ltime,bool fuzzydate); virtual bool get_date(TIME *ltime,bool fuzzydate);
virtual bool get_time(TIME *ltime); virtual bool get_time(TIME *ltime);
virtual CHARSET_INFO *charset(void) { return 0; }
friend bool reopen_table(THD *,struct st_table *,bool); friend bool reopen_table(THD *,struct st_table *,bool);
friend int cre_myisam(my_string name, register TABLE *form, uint options, friend int cre_myisam(my_string name, register TABLE *form, uint options,
ulonglong auto_increment_value); ulonglong auto_increment_value);
...@@ -249,10 +250,10 @@ class Field_str :public Field { ...@@ -249,10 +250,10 @@ class Field_str :public Field {
{ field_charset=charset; } { field_charset=charset; }
Item_result result_type () const { return STRING_RESULT; } Item_result result_type () const { return STRING_RESULT; }
uint decimals() const { return NOT_FIXED_DEC; } uint decimals() const { return NOT_FIXED_DEC; }
friend class create_field;
void make_field(Send_field *); void make_field(Send_field *);
uint size_of() const { return sizeof(*this); } uint size_of() const { return sizeof(*this); }
inline CHARSET_INFO *charset() const { return field_charset; } CHARSET_INFO *charset(void) { return field_charset; }
inline void set_charset(CHARSET_INFO *charset) { field_charset=charset; } inline void set_charset(CHARSET_INFO *charset) { field_charset=charset; }
inline int cmp_image(char *buff,uint length) inline int cmp_image(char *buff,uint length)
{ {
...@@ -261,7 +262,7 @@ class Field_str :public Field { ...@@ -261,7 +262,7 @@ class Field_str :public Field {
else else
return my_strncasecmp(field_charset,ptr,buff,length); return my_strncasecmp(field_charset,ptr,buff,length);
} }
friend class create_field;
}; };
......
...@@ -1420,9 +1420,9 @@ Item_func_regex::~Item_func_regex() ...@@ -1420,9 +1420,9 @@ Item_func_regex::~Item_func_regex()
#ifdef LIKE_CMP_TOUPPER #ifdef LIKE_CMP_TOUPPER
#define likeconv(A) (uchar) toupper(A) #define likeconv(cs,A) (uchar) (cs)->toupper(A)
#else #else
#define likeconv(A) (uchar) my_sort_order[(uchar) (A)] #define likeconv(cs,A) (uchar) (cs)->sort_order[(uchar) (A)]
#endif #endif
...@@ -1436,7 +1436,8 @@ void Item_func_like::turboBM_compute_suffixes(int* suff) ...@@ -1436,7 +1436,8 @@ void Item_func_like::turboBM_compute_suffixes(int* suff)
const int plm1 = pattern_len - 1; const int plm1 = pattern_len - 1;
int f = 0; int f = 0;
int g = plm1; int g = plm1;
int* const splm1 = suff + plm1; int *const splm1 = suff + plm1;
CHARSET_INFO *cs=system_charset_info; // QQ Needs to be fixed
*splm1 = pattern_len; *splm1 = pattern_len;
...@@ -1472,7 +1473,8 @@ void Item_func_like::turboBM_compute_suffixes(int* suff) ...@@ -1472,7 +1473,8 @@ void Item_func_like::turboBM_compute_suffixes(int* suff)
if (i < g) if (i < g)
g = i; // g = min(i, g) g = i; // g = min(i, g)
f = i; f = i;
while (g >= 0 && likeconv(pattern[g]) == likeconv(pattern[g + plm1 - f])) while (g >= 0 && likeconv(cs, pattern[g]) ==
likeconv(cs, pattern[g + plm1 - f]))
g--; g--;
suff[i] = f - g; suff[i] = f - g;
} }
...@@ -1533,19 +1535,25 @@ void Item_func_like::turboBM_compute_good_suffix_shifts(int* suff) ...@@ -1533,19 +1535,25 @@ void Item_func_like::turboBM_compute_good_suffix_shifts(int* suff)
void Item_func_like::turboBM_compute_bad_character_shifts() void Item_func_like::turboBM_compute_bad_character_shifts()
{ {
int* i; int *i;
int* end = bmBc + alphabet_size; int *end = bmBc + alphabet_size;
int j;
const int plm1 = pattern_len - 1;
CHARSET_INFO *cs=system_charset_info; // QQ Needs to be fixed
for (i = bmBc; i < end; i++) for (i = bmBc; i < end; i++)
*i = pattern_len; *i = pattern_len;
int j;
const int plm1 = pattern_len - 1;
if (binary) if (binary)
{
for (j = 0; j < plm1; j++) for (j = 0; j < plm1; j++)
bmBc[pattern[j]] = plm1 - j; bmBc[pattern[j]] = plm1 - j;
}
else else
{
for (j = 0; j < plm1; j++) for (j = 0; j < plm1; j++)
bmBc[likeconv(pattern[j])] = plm1 - j; bmBc[likeconv(cs,pattern[j])] = plm1 - j;
}
} }
...@@ -1561,6 +1569,7 @@ bool Item_func_like::turboBM_matches(const char* text, int text_len) const ...@@ -1561,6 +1569,7 @@ bool Item_func_like::turboBM_matches(const char* text, int text_len) const
int shift = pattern_len; int shift = pattern_len;
int j = 0; int j = 0;
int u = 0; int u = 0;
CHARSET_INFO *cs=system_charset_info; // QQ Needs to be fixed
const int plm1 = pattern_len - 1; const int plm1 = pattern_len - 1;
const int tlmpl = text_len - pattern_len; const int tlmpl = text_len - pattern_len;
...@@ -1602,7 +1611,7 @@ bool Item_func_like::turboBM_matches(const char* text, int text_len) const ...@@ -1602,7 +1611,7 @@ bool Item_func_like::turboBM_matches(const char* text, int text_len) const
while (j <= tlmpl) while (j <= tlmpl)
{ {
register int i = plm1; register int i = plm1;
while (i >= 0 && likeconv(pattern[i]) == likeconv(text[i + j])) while (i >= 0 && likeconv(cs,pattern[i]) == likeconv(cs,text[i + j]))
{ {
i--; i--;
if (i == plm1 - shift) if (i == plm1 - shift)
...@@ -1613,7 +1622,7 @@ bool Item_func_like::turboBM_matches(const char* text, int text_len) const ...@@ -1613,7 +1622,7 @@ bool Item_func_like::turboBM_matches(const char* text, int text_len) const
register const int v = plm1 - i; register const int v = plm1 - i;
turboShift = u - v; turboShift = u - v;
bcShift = bmBc[likeconv(text[i + j])] - plm1 + i; bcShift = bmBc[likeconv(cs, text[i + j])] - plm1 + i;
shift = max(turboShift, bcShift); shift = max(turboShift, bcShift);
shift = max(shift, bmGs[i]); shift = max(shift, bmGs[i]);
if (shift == bmGs[i]) if (shift == bmGs[i])
......
...@@ -2048,7 +2048,9 @@ void Item_func_match::init_search(bool no_order) ...@@ -2048,7 +2048,9 @@ void Item_func_match::init_search(bool no_order)
return; return;
if (key == NO_SUCH_KEY) if (key == NO_SUCH_KEY)
concat=new Item_func_concat_ws(new Item_string(" ",1), default_charset_info, fields); concat=new Item_func_concat_ws(new Item_string(" ",1,
default_charset_info),
fields);
if (master) if (master)
{ {
...@@ -2256,12 +2258,12 @@ double Item_func_match::val() ...@@ -2256,12 +2258,12 @@ double Item_func_match::val()
Item *get_system_var(LEX_STRING name) Item *get_system_var(LEX_STRING name)
{ {
if (!my_strcasecmp(name.str,"IDENTITY")) if (!my_strcasecmp(system_charset_info, name.str, "IDENTITY"))
return new Item_int((char*) "@@IDENTITY", return new Item_int((char*) "@@IDENTITY",
current_thd->insert_id(),21); current_thd->insert_id(),21);
if (!my_strcasecmp(name.str,"VERSION")) if (!my_strcasecmp(system_charset_info, name.str, "VERSION"))
return new Item_string("@@VERSION",server_version, return new Item_string("@@VERSION",server_version,
(uint) strlen(server_version)); (uint) strlen(server_version), system_charset_info);
net_printf(&current_thd->net, ER_UNKNOWN_SYSTEM_VARIABLE, name.str); net_printf(&current_thd->net, ER_UNKNOWN_SYSTEM_VARIABLE, name.str);
return 0; return 0;
} }
......
...@@ -748,7 +748,7 @@ int wild_case_compare(CHARSET_INFO *cs, const char *str,const char *wildstr); ...@@ -748,7 +748,7 @@ int wild_case_compare(CHARSET_INFO *cs, const char *str,const char *wildstr);
int wild_compare(const char *str,const char *str_end, int wild_compare(const char *str,const char *str_end,
const char *wildstr,const char *wildend,char escape); const char *wildstr,const char *wildend,char escape);
int wild_case_compare(CHARSET_INFO *cs, const char *str,const char *str_end, int wild_case_compare(CHARSET_INFO *cs, const char *str,const char *str_end,
const char *wildstr,const char *wildend,char escape); const char *wildstr,const char *wildend,char escape);
/* from hostname.cc */ /* from hostname.cc */
struct in_addr; struct in_addr;
......
...@@ -2051,10 +2051,10 @@ static void mysql_rm_tmp_tables(void) ...@@ -2051,10 +2051,10 @@ static void mysql_rm_tmp_tables(void)
/* /*
** CREATE INDEX and DROP INDEX are implemented by calling ALTER TABLE with CREATE INDEX and DROP INDEX are implemented by calling ALTER TABLE with
** the proper arguments. This isn't very fast but it should work for most the proper arguments. This isn't very fast but it should work for most
** cases. cases.
** One should normally create all indexes with CREATE TABLE or ALTER TABLE. One should normally create all indexes with CREATE TABLE or ALTER TABLE.
*/ */
int mysql_create_index(THD *thd, TABLE_LIST *table_list, List<Key> &keys) int mysql_create_index(THD *thd, TABLE_LIST *table_list, List<Key> &keys)
...@@ -2066,6 +2066,7 @@ int mysql_create_index(THD *thd, TABLE_LIST *table_list, List<Key> &keys) ...@@ -2066,6 +2066,7 @@ int mysql_create_index(THD *thd, TABLE_LIST *table_list, List<Key> &keys)
DBUG_ENTER("mysql_create_index"); DBUG_ENTER("mysql_create_index");
bzero((char*) &create_info,sizeof(create_info)); bzero((char*) &create_info,sizeof(create_info));
create_info.db_type=DB_TYPE_DEFAULT; create_info.db_type=DB_TYPE_DEFAULT;
create_info.table_charset=default_charset_info;
DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->real_name, DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->real_name,
&create_info, table_list, &create_info, table_list,
fields, keys, drop, alter, (ORDER*)0, FALSE, fields, keys, drop, alter, (ORDER*)0, FALSE,
...@@ -2082,6 +2083,7 @@ int mysql_drop_index(THD *thd, TABLE_LIST *table_list, List<Alter_drop> &drop) ...@@ -2082,6 +2083,7 @@ int mysql_drop_index(THD *thd, TABLE_LIST *table_list, List<Alter_drop> &drop)
DBUG_ENTER("mysql_drop_index"); DBUG_ENTER("mysql_drop_index");
bzero((char*) &create_info,sizeof(create_info)); bzero((char*) &create_info,sizeof(create_info));
create_info.db_type=DB_TYPE_DEFAULT; create_info.db_type=DB_TYPE_DEFAULT;
create_info.table_charset=default_charset_info;
DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->real_name, DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->real_name,
&create_info, table_list, &create_info, table_list,
fields, keys, drop, alter, (ORDER*)0, FALSE, fields, keys, drop, alter, (ORDER*)0, FALSE,
......
...@@ -516,8 +516,9 @@ int mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) ...@@ -516,8 +516,9 @@ int mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok)
table->file->info(HA_STATUS_AUTO | HA_STATUS_NO_LOCK); table->file->info(HA_STATUS_AUTO | HA_STATUS_NO_LOCK);
bzero((char*) &create_info,sizeof(create_info)); bzero((char*) &create_info,sizeof(create_info));
create_info.auto_increment_value= table->file->auto_increment_value; create_info.auto_increment_value= table->file->auto_increment_value;
db_type table_type=table->db_type; create_info.table_charset=default_charset_info;
db_type table_type=table->db_type;
strmov(path,table->path); strmov(path,table->path);
*table_ptr= table->next; // Unlink table from list *table_ptr= table->next; // Unlink table from list
close_temporary(table,0); close_temporary(table,0);
...@@ -527,7 +528,8 @@ int mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) ...@@ -527,7 +528,8 @@ int mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok)
if ((error= (int) !(open_temporary_table(thd, path, table_list->db, if ((error= (int) !(open_temporary_table(thd, path, table_list->db,
table_list->real_name, 1)))) table_list->real_name, 1))))
(void) rm_temporary_table(table_type, path); (void) rm_temporary_table(table_type, path);
/* Sasha: if we return here we will not have binloged the truncation and /*
If we return here we will not have binloged the truncation and
we will not send_ok() to the client. Yes, we do need better coverage we will not send_ok() to the client. Yes, we do need better coverage
testing, this bug has been here for a few months :-). testing, this bug has been here for a few months :-).
*/ */
...@@ -557,6 +559,8 @@ int mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) ...@@ -557,6 +559,8 @@ int mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok)
} }
bzero((char*) &create_info,sizeof(create_info)); bzero((char*) &create_info,sizeof(create_info));
create_info.table_charset=default_charset_info;
*fn_ext(path)=0; // Remove the .frm extension *fn_ext(path)=0; // Remove the .frm extension
error= ha_create_table(path,&create_info,1) ? -1 : 0; error= ha_create_table(path,&create_info,1) ? -1 : 0;
query_cache_invalidate3(thd, table_list, 0); query_cache_invalidate3(thd, table_list, 0);
......
...@@ -1749,6 +1749,7 @@ mysql_execute_command(void) ...@@ -1749,6 +1749,7 @@ mysql_execute_command(void)
bzero((char*) &create_info,sizeof(create_info)); bzero((char*) &create_info,sizeof(create_info));
create_info.db_type=DB_TYPE_DEFAULT; create_info.db_type=DB_TYPE_DEFAULT;
create_info.row_type=ROW_TYPE_DEFAULT; create_info.row_type=ROW_TYPE_DEFAULT;
create_info.table_charset=default_charset_info;
res= mysql_alter_table(thd, NullS, NullS, &create_info, res= mysql_alter_table(thd, NullS, NullS, &create_info,
tables, lex->create_list, tables, lex->create_list,
lex->key_list, lex->drop_list, lex->alter_list, lex->key_list, lex->drop_list, lex->alter_list,
...@@ -2866,6 +2867,8 @@ bool add_field_to_list(char *field_name, enum_field_types type, ...@@ -2866,6 +2867,8 @@ bool add_field_to_list(char *field_name, enum_field_types type,
new_field->change=change; new_field->change=change;
new_field->interval=0; new_field->interval=0;
new_field->pack_length=0; new_field->pack_length=0;
new_field->charset=0; // QQ: To be fixed
if (!comment) if (!comment)
{ {
new_field->comment.str=0; new_field->comment.str=0;
......
...@@ -4019,6 +4019,7 @@ static bool create_myisam_tmp_table(TABLE *table,TMP_TABLE_PARAM *param, ...@@ -4019,6 +4019,7 @@ static bool create_myisam_tmp_table(TABLE *table,TMP_TABLE_PARAM *param,
} }
MI_CREATE_INFO create_info; MI_CREATE_INFO create_info;
bzero((char*) &create_info,sizeof(create_info)); bzero((char*) &create_info,sizeof(create_info));
if ((options & (OPTION_BIG_TABLES | SELECT_SMALL_RESULT)) == if ((options & (OPTION_BIG_TABLES | SELECT_SMALL_RESULT)) ==
OPTION_BIG_TABLES) OPTION_BIG_TABLES)
create_info.data_file_length= ~(ulonglong) 0; create_info.data_file_length= ~(ulonglong) 0;
......
...@@ -1180,7 +1180,8 @@ int mysqld_show(THD *thd, const char *wild, show_var_st *variables) ...@@ -1180,7 +1180,8 @@ int mysqld_show(THD *thd, const char *wild, show_var_st *variables)
pthread_mutex_lock(&LOCK_status); pthread_mutex_lock(&LOCK_status);
for (i=0; variables[i].name; i++) for (i=0; variables[i].name; i++)
{ {
if (!(wild && wild[0] && wild_case_compare(variables[i].name,wild))) if (!(wild && wild[0] && wild_case_compare(system_charset_info,
variables[i].name,wild)))
{ {
packet2.length(0); packet2.length(0);
net_store_data(&packet2,convert,variables[i].name); net_store_data(&packet2,convert,variables[i].name);
......
...@@ -213,16 +213,17 @@ class String ...@@ -213,16 +213,17 @@ class String
uint32 numchars(); uint32 numchars();
int charpos(int i,uint32 offset=0); int charpos(int i,uint32 offset=0);
// added by Holyfoot for "geometry" needs
int reserve(uint32 space_needed) int reserve(uint32 space_needed)
{ {
return realloc(str_length + space_needed); return realloc(str_length + space_needed);
} }
int reserve(uint32 space_needed, uint32 grow_by); int reserve(uint32 space_needed, uint32 grow_by);
// these append operations do NOT check alloced memory /*
// q_*** methods writes values of parameters itself The following append operations do NOT check alloced memory
// qs_*** methods writes string representation of value q_*** methods writes values of parameters itself
qs_*** methods writes string representation of value
*/
void q_append(const char &c) void q_append(const char &c)
{ {
Ptr[str_length++] = c; Ptr[str_length++] = c;
......
...@@ -1577,12 +1577,13 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, ...@@ -1577,12 +1577,13 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name,
{ {
if (cfield->change) if (cfield->change)
{ {
if (!my_strcasecmp(system_charset_info,key_part_name, cfield->change)) if (!my_strcasecmp(system_charset_info, key_part_name,
cfield->change))
break; break;
} }
else if (!my_strcasecmp(system_charset_info, else if (!my_strcasecmp(system_charset_info,
key_part_name, cfield->field_name)) key_part_name, cfield->field_name))
break; break;
} }
if (!cfield) if (!cfield)
continue; // Field is removed continue; // Field is removed
...@@ -1618,6 +1619,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, ...@@ -1618,6 +1619,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name,
key_list.push_back(key); key_list.push_back(key);
} }
} }
if (drop_list.elements) if (drop_list.elements)
{ {
my_error(ER_CANT_DROP_FIELD_OR_KEY,MYF(0),drop_list.head()->name); my_error(ER_CANT_DROP_FIELD_OR_KEY,MYF(0),drop_list.head()->name);
......
...@@ -1147,7 +1147,7 @@ references: ...@@ -1147,7 +1147,7 @@ references:
}; };
opt_ref_list: opt_ref_list:
/* empty */ {} /* empty */ opt_on_delete {}
| '(' ref_list ')' opt_on_delete {}; | '(' ref_list ')' opt_on_delete {};
ref_list: ref_list:
...@@ -1273,11 +1273,24 @@ alter_list_item: ...@@ -1273,11 +1273,24 @@ alter_list_item:
lex->change= $3.str; lex->simple_alter=0; lex->change= $3.str; lex->simple_alter=0;
} }
field_spec opt_place field_spec opt_place
| MODIFY_SYM opt_column field_spec | MODIFY_SYM opt_column field_ident
{ {
Lex->simple_alter=0; LEX *lex=Lex;
} lex->length=lex->dec=0; lex->type=0; lex->interval=0;
opt_place lex->default_value=lex->comment=0;
lex->simple_alter=0;
}
type opt_attribute
{
LEX *lex=Lex;
if (add_field_to_list($3.str,
(enum enum_field_types) $5,
lex->length,lex->dec,lex->type,
lex->default_value, lex->comment,
$3.str, lex->interval))
YYABORT;
}
opt_place
| DROP opt_column field_ident opt_restrict | DROP opt_column field_ident opt_restrict
{ {
LEX *lex=Lex; LEX *lex=Lex;
......
...@@ -49,7 +49,7 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, ...@@ -49,7 +49,7 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag,
interval_count,interval_parts,read_length,db_create_options; interval_count,interval_parts,read_length,db_create_options;
uint key_info_length, com_length; uint key_info_length, com_length;
ulong pos; ulong pos;
char index_file[FN_REFLEN], *names,*keynames; char index_file[FN_REFLEN], *names, *keynames, *comment_pos;
uchar head[288],*disk_buff,new_field_pack_flag; uchar head[288],*disk_buff,new_field_pack_flag;
my_string record; my_string record;
const char **int_array; const char **int_array;
...@@ -58,7 +58,7 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, ...@@ -58,7 +58,7 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag,
Field **field_ptr,*reg_field; Field **field_ptr,*reg_field;
KEY *keyinfo; KEY *keyinfo;
KEY_PART_INFO *key_part; KEY_PART_INFO *key_part;
uchar *null_pos, *comment_pos; uchar *null_pos;
uint null_bit, new_frm_ver, field_pack_length; uint null_bit, new_frm_ver, field_pack_length;
SQL_CRYPT *crypted=0; SQL_CRYPT *crypted=0;
DBUG_ENTER("openfrm"); DBUG_ENTER("openfrm");
...@@ -292,7 +292,7 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, ...@@ -292,7 +292,7 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag,
outparam->field=field_ptr; outparam->field=field_ptr;
read_length=(uint) (outparam->fields * field_pack_length + read_length=(uint) (outparam->fields * field_pack_length +
pos+ (uint) (n_length+int_length)); pos+ (uint) (n_length+int_length+com_length));
if (read_string(file,(gptr*) &disk_buff,read_length)) if (read_string(file,(gptr*) &disk_buff,read_length))
goto err_not_open; /* purecov: inspected */ goto err_not_open; /* purecov: inspected */
if (crypted) if (crypted)
...@@ -302,7 +302,6 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, ...@@ -302,7 +302,6 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag,
crypted=0; crypted=0;
} }
strpos= disk_buff+pos; strpos= disk_buff+pos;
comment_pos=disk_buff+read_length-com_length;
outparam->intervals= (TYPELIB*) (field_ptr+outparam->fields+1); outparam->intervals= (TYPELIB*) (field_ptr+outparam->fields+1);
int_array= (const char **) (outparam->intervals+interval_count); int_array= (const char **) (outparam->intervals+interval_count);
...@@ -311,6 +310,8 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, ...@@ -311,6 +310,8 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag,
outparam->intervals=0; // For better debugging outparam->intervals=0; // For better debugging
memcpy((char*) names, strpos+(outparam->fields*field_pack_length), memcpy((char*) names, strpos+(outparam->fields*field_pack_length),
(uint) (n_length+int_length)); (uint) (n_length+int_length));
comment_pos=names+(n_length+int_length);
memcpy(comment_pos, disk_buff+read_length-com_length, com_length);
fix_type_pointers(&int_array,&outparam->fieldnames,1,&names); fix_type_pointers(&int_array,&outparam->fieldnames,1,&names);
fix_type_pointers(&int_array,outparam->intervals,interval_count, fix_type_pointers(&int_array,outparam->intervals,interval_count,
...@@ -489,13 +490,13 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, ...@@ -489,13 +490,13 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag,
keyinfo->key_length ? UNIQUE_KEY_FLAG : MULTIPLE_KEY_FLAG); keyinfo->key_length ? UNIQUE_KEY_FLAG : MULTIPLE_KEY_FLAG);
if (i == 0) if (i == 0)
field->key_start|= ((key_map) 1 << key); field->key_start|= ((key_map) 1 << key);
if ((ha_option & HA_HAVE_KEY_READ_ONLY) && if (field->key_length() == key_part->length &&
field->key_length() == key_part->length &&
field->type() != FIELD_TYPE_BLOB) field->type() != FIELD_TYPE_BLOB)
{ {
if (field->key_type() != HA_KEYTYPE_TEXT || if ((ha_option & HA_HAVE_KEY_READ_ONLY) &&
(!(ha_option & HA_KEY_READ_WRONG_STR) && (field->key_type() != HA_KEYTYPE_TEXT ||
!(keyinfo->flags & HA_FULLTEXT))) (!(ha_option & HA_KEY_READ_WRONG_STR) &&
!(keyinfo->flags & HA_FULLTEXT))))
field->part_of_key|= ((key_map) 1 << key); field->part_of_key|= ((key_map) 1 << key);
if ((field->key_type() != HA_KEYTYPE_TEXT || if ((field->key_type() != HA_KEYTYPE_TEXT ||
!(keyinfo->flags & HA_FULLTEXT)) && !(keyinfo->flags & HA_FULLTEXT)) &&
...@@ -1200,7 +1201,7 @@ db_type get_table_type(const char *name) ...@@ -1200,7 +1201,7 @@ db_type get_table_type(const char *name)
error=my_read(file,(byte*) head,4,MYF(MY_NABP)); error=my_read(file,(byte*) head,4,MYF(MY_NABP));
my_close(file,MYF(0)); my_close(file,MYF(0));
if (error || head[0] != (uchar) 254 || head[1] != 1 || if (error || head[0] != (uchar) 254 || head[1] != 1 ||
(head[2] != FRM_VER && head[2] != FRM_VER+1)) (head[2] < FRM_VER && head[2] > FRM_VER+2))
DBUG_RETURN(DB_TYPE_UNKNOWN); DBUG_RETURN(DB_TYPE_UNKNOWN);
DBUG_RETURN(ha_checktype((enum db_type) (uint) *(head+3))); DBUG_RETURN(ha_checktype((enum db_type) (uint) *(head+3)));
} }
......
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
#include "mysql_priv.h" #include "mysql_priv.h"
#include <m_ctype.h> #include <m_ctype.h>
#define FCOMP 11 /* Byte for packed field */ #define FCOMP 15 /* Bytes for a packed field */
static uchar * pack_screens(List<create_field> &create_fields, static uchar * pack_screens(List<create_field> &create_fields,
uint *info_length, uint *screens, bool small_file); uint *info_length, uint *screens, bool small_file);
...@@ -246,7 +246,7 @@ static uchar * pack_screens(List<create_field> &create_fields, ...@@ -246,7 +246,7 @@ static uchar * pack_screens(List<create_field> &create_fields,
static uint pack_keys(uchar *keybuff,uint key_count,KEY *keyinfo) static uint pack_keys(uchar *keybuff,uint key_count,KEY *keyinfo)
{ {
uint key_parts,length; uint key_parts,length;
uchar *pos, *keyname_pos, *key_alg_pos; uchar *pos, *keyname_pos;
KEY *key,*end; KEY *key,*end;
KEY_PART_INFO *key_part,*key_part_end; KEY_PART_INFO *key_part,*key_part_end;
DBUG_ENTER("pack_keys"); DBUG_ENTER("pack_keys");
...@@ -259,6 +259,7 @@ static uint pack_keys(uchar *keybuff,uint key_count,KEY *keyinfo) ...@@ -259,6 +259,7 @@ static uint pack_keys(uchar *keybuff,uint key_count,KEY *keyinfo)
int2store(pos+2,key->key_length); int2store(pos+2,key->key_length);
pos[4]= (uchar) key->key_parts; pos[4]= (uchar) key->key_parts;
pos[5]= (uchar) key->algorithm; pos[5]= (uchar) key->algorithm;
pos[6]=pos[7]=0; // For the future
pos+=8; pos+=8;
key_parts+=key->key_parts; key_parts+=key->key_parts;
DBUG_PRINT("loop",("flags: %d key_parts: %d at %lx", DBUG_PRINT("loop",("flags: %d key_parts: %d at %lx",
...@@ -295,7 +296,7 @@ static uint pack_keys(uchar *keybuff,uint key_count,KEY *keyinfo) ...@@ -295,7 +296,7 @@ static uint pack_keys(uchar *keybuff,uint key_count,KEY *keyinfo)
keybuff[1]=(uchar) key_parts; keybuff[1]=(uchar) key_parts;
length=(uint) (keyname_pos-keybuff); length=(uint) (keyname_pos-keybuff);
int2store(keybuff+2,length); int2store(keybuff+2,length);
length=(uint) (key_alg_pos-keyname_pos); length=(uint) (pos-keyname_pos);
int2store(keybuff+4,length); int2store(keybuff+4,length);
DBUG_RETURN((uint) (pos-keybuff)); DBUG_RETURN((uint) (pos-keybuff));
} /* pack_keys */ } /* pack_keys */
...@@ -309,8 +310,8 @@ static bool pack_header(uchar *forminfo, enum db_type table_type, ...@@ -309,8 +310,8 @@ static bool pack_header(uchar *forminfo, enum db_type table_type,
handler *file) handler *file)
{ {
uint length,int_count,int_length,no_empty, int_parts; uint length,int_count,int_length,no_empty, int_parts;
uint time_stamp_pos,null_fields, com_length; uint time_stamp_pos,null_fields;
ulong reclength,totlength,n_length; ulong reclength, totlength, n_length, com_length;
DBUG_ENTER("pack_header"); DBUG_ENTER("pack_header");
if (create_fields.elements > MAX_FIELDS) if (create_fields.elements > MAX_FIELDS)
...@@ -460,7 +461,7 @@ static bool pack_fields(File file,List<create_field> &create_fields) ...@@ -460,7 +461,7 @@ static bool pack_fields(File file,List<create_field> &create_fields)
buff[11]= (uchar) field->sql_type; buff[11]= (uchar) field->sql_type;
buff[12]= (uchar) (field->charset ? field->charset->number : buff[12]= (uchar) (field->charset ? field->charset->number :
default_charset_info->number); default_charset_info->number);
int2store(buff, field->comment.length); int2store(buff+13, field->comment.length);
comment_length+= field->comment.length; comment_length+= field->comment.length;
set_if_bigger(int_count,field->interval_id); set_if_bigger(int_count,field->interval_id);
if (my_write(file,(byte*) buff,FCOMP,MYF_RW)) if (my_write(file,(byte*) buff,FCOMP,MYF_RW))
......
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