From 9b16dd8962408ac4483cd3a1bf4264b1e81d2780 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 9 Aug 2003 14:39:54 +0300 Subject: [PATCH 01/10] fixed union unlocking problem (BUG#906) mysql-test/r/subselect.result: test of union unlocking problem mysql-test/t/subselect.test: test of union unlocking problem sql/sql_union.cc: prohibit unlocking all tables by fake select execution --- mysql-test/r/subselect.result | 12 ++++++++++++ mysql-test/t/subselect.test | 16 +++++++++++++++- sql/sql_union.cc | 3 ++- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index bba86ff8891..fbf037d5147 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -1225,3 +1225,15 @@ a (select count(distinct t1.b) as sum from t1,t2 where t1.a=t2.a and t2.b > 0 an 2 2 1 2 drop table t1,t2,t3; +create table t1 (a int) type=innodb; +create table t2 (a int) type=innodb; +create table t3 (a int) type=innodb; +insert into t1 values (1),(2),(3),(4); +insert into t2 values (10),(20),(30),(40); +insert into t3 values (1),(2),(10),(50); +select a from t3 where t3.a in (select a from t1 where a <= 3 union select * from t2 where a <= 30); +a +1 +2 +10 +drop table t1,t2; diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index 66d8dd2bc32..3896676f3ab 100644 --- a/mysql-test/t/subselect.test +++ b/mysql-test/t/subselect.test @@ -807,6 +807,7 @@ INSERT INTO t1 VALUES("2f6161e879db43c1a5b82c21ddc49089", "Default", "System", " INSERT INTO t1 VALUES("c373e9f5ad0791724315444553544200", "AddDocumentTest", "admin", "2003-06-09 10:51:25", "Movie Reviews", "0", "2003-06-09 10:51:25", "admin", "0", "2f6161e879db43c1a5b82c21ddc49089", "03eea05112b845949f3fd03278b5fe43", NULL); SELECT 'c373e9f5ad0791a0dab5444553544200' IN(SELECT t1.FOLDERID FROM t1 WHERE t1.PARENTID='2f6161e879db43c1a5b82c21ddc49089' AND t1.FOLDERNAME = 'Level1'); drop table t1; + # # alloc_group_fields() working # @@ -817,4 +818,17 @@ insert into t1 values (0,100),(1,2), (1,3), (2,2), (2,7), (2,-1), (3,10); insert into t2 values (0,0), (1,1), (2,1), (3,1), (4,1); insert into t3 values (3,3), (2,2), (1,1); select a,(select count(distinct t1.b) as sum from t1,t2 where t1.a=t2.a and t2.b > 0 and t1.a <= t3.b group by t1.a order by sum limit 1) from t3; -drop table t1,t2,t3;s +drop table t1,t2,t3; + +# +# UNION unlocking test +# +create table t1 (a int) type=innodb; +create table t2 (a int) type=innodb; +create table t3 (a int) type=innodb; +insert into t1 values (1),(2),(3),(4); +insert into t2 values (10),(20),(30),(40); +insert into t3 values (1),(2),(10),(50); +select a from t3 where t3.a in (select a from t1 where a <= 3 union select * from t2 where a <= 30); +drop table t1,t2; + diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 724cc658b15..bbf0781bc19 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -351,7 +351,8 @@ int st_select_lex_unit::exec() global_parameters->order_list.elements, (ORDER*)global_parameters->order_list.first, (ORDER*) NULL, NULL, (ORDER*) NULL, - thd->options, result, this, fake_select, 0); + thd->options | SELECT_NO_UNLOCK, + result, this, fake_select, 0); if (found_rows_for_union && !res) thd->limit_found_rows = (ulonglong)table->file->records; fake_select->exclude(); From f3f6e5e9b2ecfef9bf6c5e7558af2481e30385ad Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 9 Aug 2003 17:10:58 +0300 Subject: [PATCH 02/10] test for BUG#969 --- mysql-test/r/derived.result | 32 ++++++++++++++++++++++++++++++++ mysql-test/t/derived.test | 16 ++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/mysql-test/r/derived.result b/mysql-test/r/derived.result index 61937ba9266..f3b09164dda 100644 --- a/mysql-test/r/derived.result +++ b/mysql-test/r/derived.result @@ -160,3 +160,35 @@ id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY ALL NULL NULL NULL NULL 3 2 DERIVED t1 ALL NULL NULL NULL NULL 3 Using temporary; Using filesort drop table t1; +create table t1 (mat_id MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, matintnum CHAR(6) NOT NULL, test MEDIUMINT UNSIGNED NULL); +create table t2 (mat_id MEDIUMINT UNSIGNED NOT NULL, pla_id MEDIUMINT UNSIGNED NOT NULL); +insert into t1 values (NULL, 'a', 1), (NULL, 'b', 2), (NULL, 'c', 3), (NULL, 'd', 4), (NULL, 'e', 5), (NULL, 'f', 6), (NULL, 'g', 7), (NULL, 'h', 8), (NULL, 'i', 9); +insert into t2 values (1, 100), (1, 101), (1, 102), (2, 100), (2, 103), (2, 104), (3, 101), (3, 102), (3, 105); +SELECT d.pla_id, m2.mat_id FROM t1 m2 INNER JOIN (SELECT mp.pla_id, MIN(m1.matintnum) AS matintnum FROM t2 mp INNER JOIN t1 m1 ON mp.mat_id=m1.mat_id GROUP BY mp.pla_id) d ON d.matintnum=m2.matintnum; +pla_id mat_id +100 1 +101 1 +102 1 +103 2 +104 2 +105 3 +SELECT d.pla_id, m2.test FROM t1 m2 INNER JOIN (SELECT mp.pla_id, MIN(m1.matintnum) AS matintnum FROM t2 mp INNER JOIN t1 m1 ON mp.mat_id=m1.mat_id GROUP BY mp.pla_id) d ON d.matintnum=m2.matintnum; +pla_id test +100 1 +101 1 +102 1 +103 2 +104 2 +105 3 +explain SELECT d.pla_id, m2.mat_id FROM t1 m2 INNER JOIN (SELECT mp.pla_id, MIN(m1.matintnum) AS matintnum FROM t2 mp INNER JOIN t1 m1 ON mp.mat_id=m1.mat_id GROUP BY mp.pla_id) d ON d.matintnum=m2.matintnum; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY m2 ALL NULL NULL NULL NULL 9 +1 PRIMARY ALL NULL NULL NULL NULL 6 Using where +2 DERIVED mp ALL NULL NULL NULL NULL 9 Using temporary; Using filesort +2 DERIVED m2 index NULL PRIMARY 3 NULL 9 Using index +explain SELECT d.pla_id, m2.test FROM t1 m2 INNER JOIN (SELECT mp.pla_id, MIN(m1.matintnum) AS matintnum FROM t2 mp INNER JOIN t1 m1 ON mp.mat_id=m1.mat_id GROUP BY mp.pla_id) d ON d.matintnum=m2.matintnum; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY m2 ALL NULL NULL NULL NULL 9 +1 PRIMARY ALL NULL NULL NULL NULL 6 Using where +2 DERIVED mp ALL NULL NULL NULL NULL 9 Using temporary; Using filesort +2 DERIVED m2 index NULL PRIMARY 3 NULL 9 Using index diff --git a/mysql-test/t/derived.test b/mysql-test/t/derived.test index 4f0af1edbaa..c998de7ae6e 100644 --- a/mysql-test/t/derived.test +++ b/mysql-test/t/derived.test @@ -74,3 +74,19 @@ create table t1 (id int); insert into t1 values (1),(2),(3); describe select * from (select * from t1 group by id) bar; drop table t1; + +# +# test->used_keys test for derived tables +# +create table t1 (mat_id MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, matintnum CHAR(6) NOT NULL, test MEDIUMINT UNSIGNED NULL); +create table t2 (mat_id MEDIUMINT UNSIGNED NOT NULL, pla_id MEDIUMINT UNSIGNED NOT NULL); +insert into t1 values (NULL, 'a', 1), (NULL, 'b', 2), (NULL, 'c', 3), (NULL, 'd', 4), (NULL, 'e', 5), (NULL, 'f', 6), (NULL, 'g', 7), (NULL, 'h', 8), (NULL, 'i', 9); +insert into t2 values (1, 100), (1, 101), (1, 102), (2, 100), (2, 103), (2, 104), (3, 101), (3, 102), (3, 105); + +SELECT d.pla_id, m2.mat_id FROM t1 m2 INNER JOIN (SELECT mp.pla_id, MIN(m1.matintnum) AS matintnum FROM t2 mp INNER JOIN t1 m1 ON mp.mat_id=m1.mat_id GROUP BY mp.pla_id) d ON d.matintnum=m2.matintnum; +SELECT d.pla_id, m2.test FROM t1 m2 INNER JOIN (SELECT mp.pla_id, MIN(m1.matintnum) AS matintnum FROM t2 mp INNER JOIN t1 m1 ON mp.mat_id=m1.mat_id GROUP BY mp.pla_id) d ON d.matintnum=m2.matintnum; + +explain SELECT d.pla_id, m2.mat_id FROM t1 m2 INNER JOIN (SELECT mp.pla_id, MIN(m1.matintnum) AS matintnum FROM t2 mp INNER JOIN t1 m1 ON mp.mat_id=m1.mat_id GROUP BY mp.pla_id) d ON d.matintnum=m2.matintnum; +explain SELECT d.pla_id, m2.test FROM t1 m2 INNER JOIN (SELECT mp.pla_id, MIN(m1.matintnum) AS matintnum FROM t2 mp INNER JOIN t1 m1 ON mp.mat_id=m1.mat_id GROUP BY mp.pla_id) d ON d.matintnum=m2.matintnum; + +drop table t1,t2 From 0960c3eb26861238d9102845d370f3eb66d91290 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 11 Aug 2003 18:18:34 +0500 Subject: [PATCH 03/10] Fix: create table t1 (a char(10) character set cp1251) SELECT _koi8r'blabla' as a The above query created a field of koi8r charset, not cp1251 Change: CREATE TABLE a (a CHAR(1) CHARACTER SET utf8) Length now means character length, not byte length. The above creates a field that guarantees can store a multibyte value 1 character long. For utf8 the above creates a field that can store 3 bytes. --- mysql-test/r/ctype_mb.result | 21 +++++++++++++ mysql-test/r/ctype_recoding.result | 14 +++++++++ mysql-test/r/func_system.result | 6 ++-- mysql-test/t/ctype_mb.test | 8 +++++ mysql-test/t/ctype_recoding.test | 7 +++++ sql/field.cc | 24 +++++++++++++-- sql/field.h | 1 + sql/item.h | 4 +-- sql/sql_parse.cc | 16 ++++++++-- sql/sql_table.cc | 47 +++++++++++++++++++----------- sql/unireg.h | 7 ++++- 11 files changed, 127 insertions(+), 28 deletions(-) create mode 100644 mysql-test/r/ctype_mb.result create mode 100644 mysql-test/t/ctype_mb.test diff --git a/mysql-test/r/ctype_mb.result b/mysql-test/r/ctype_mb.result new file mode 100644 index 00000000000..4b3b464dea1 --- /dev/null +++ b/mysql-test/r/ctype_mb.result @@ -0,0 +1,21 @@ +CREATE TABLE t1 SELECT _utf8'test' as c1, _utf8'тест' as c2; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` char(4) character set utf8 NOT NULL default '', + `c2` char(4) character set utf8 NOT NULL default '' +) TYPE=MyISAM CHARSET=latin1 +DELETE FROM t1; +ALTER TABLE t1 ADD c3 CHAR(4) CHARACTER SET utf8; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` char(4) character set utf8 NOT NULL default '', + `c2` char(4) character set utf8 NOT NULL default '', + `c3` char(4) character set utf8 default NULL +) TYPE=MyISAM CHARSET=latin1 +INSERT INTO t1 VALUES ('aaaabbbbccccdddd','aaaabbbbccccdddd','aaaabbbbccccdddd'); +SELECT * FROM t1; +c1 c2 c3 +aaaabbbbcccc aaaabbbbcccc aaaabbbbcccc +DROP TABLE t1; diff --git a/mysql-test/r/ctype_recoding.result b/mysql-test/r/ctype_recoding.result index b89a90cc6ba..624eab4cf0b 100644 --- a/mysql-test/r/ctype_recoding.result +++ b/mysql-test/r/ctype_recoding.result @@ -1,5 +1,19 @@ SET CHARACTER SET koi8r; DROP TABLE IF EXISTS ; +SET CHARACTER SET koi8r; +CREATE TABLE t1 (a CHAR(10) CHARACTER SET cp1251) SELECT _koi8r'' AS a; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` char(10) character set cp1251 default NULL +) TYPE=MyISAM CHARSET=latin1 +SELECT a FROM t1; +a + +SELECT HEX(a) FROM t1; +HEX(a) +EFF0EEE1E0 +DROP TABLE t1; CREATE TABLE ( CHAR(32) CHARACTER SET koi8r NOT NULL diff --git a/mysql-test/r/func_system.result b/mysql-test/r/func_system.result index cde21ead33c..a52d5613c04 100644 --- a/mysql-test/r/func_system.result +++ b/mysql-test/r/func_system.result @@ -41,9 +41,9 @@ create table t1 (version char(40)) select database(), user(), version() as 'vers show create table t1; Table Create Table t1 CREATE TABLE `t1` ( - `database()` char(102) character set utf8 NOT NULL default '', - `user()` char(231) character set utf8 NOT NULL default '', - `version` char(40) character set utf8 default NULL + `database()` char(34) character set utf8 NOT NULL default '', + `user()` char(77) character set utf8 NOT NULL default '', + `version` char(40) default NULL ) TYPE=MyISAM CHARSET=latin1 drop table t1; select TRUE,FALSE,NULL; diff --git a/mysql-test/t/ctype_mb.test b/mysql-test/t/ctype_mb.test new file mode 100644 index 00000000000..5c3e67eec01 --- /dev/null +++ b/mysql-test/t/ctype_mb.test @@ -0,0 +1,8 @@ +CREATE TABLE t1 SELECT _utf8'test' as c1, _utf8'тест' as c2; +SHOW CREATE TABLE t1; +DELETE FROM t1; +ALTER TABLE t1 ADD c3 CHAR(4) CHARACTER SET utf8; +SHOW CREATE TABLE t1; +INSERT INTO t1 VALUES ('aaaabbbbccccdddd','aaaabbbbccccdddd','aaaabbbbccccdddd'); +SELECT * FROM t1; +DROP TABLE t1; diff --git a/mysql-test/t/ctype_recoding.test b/mysql-test/t/ctype_recoding.test index 8fa31b1f17b..25df9c0a86b 100644 --- a/mysql-test/t/ctype_recoding.test +++ b/mysql-test/t/ctype_recoding.test @@ -4,6 +4,13 @@ SET CHARACTER SET koi8r; DROP TABLE IF EXISTS ; --enable_warnings +SET CHARACTER SET koi8r; +CREATE TABLE t1 (a CHAR(10) CHARACTER SET cp1251) SELECT _koi8r'' AS a; +SHOW CREATE TABLE t1; +SELECT a FROM t1; +SELECT HEX(a) FROM t1; +DROP TABLE t1; + CREATE TABLE ( CHAR(32) CHARACTER SET koi8r NOT NULL diff --git a/sql/field.cc b/sql/field.cc index 2f89dd43c3f..c25f1170b00 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -4012,7 +4012,7 @@ void Field_string::sql_type(String &res) const (table->db_options_in_use & HA_OPTION_PACK_RECORD) ? "varchar" : "char"), - (int) field_length); + (int) field_length / charset()->mbmaxlen); res.length(length); } @@ -4178,7 +4178,7 @@ void Field_varstring::sql_type(String &res) const CHARSET_INFO *cs=res.charset(); ulong length= cs->cset->snprintf(cs,(char*) res.ptr(), res.alloced_length(),"varchar(%u)", - field_length); + field_length / charset()->mbmaxlen); res.length(length); } @@ -5267,6 +5267,26 @@ bool Field_num::eq_def(Field *field) ** Handling of field and create_field *****************************************************************************/ +void create_field::create_length_to_internal_length(void) +{ + switch (sql_type) + { + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_STRING: + length*= charset->mbmaxlen; + pack_length= calc_pack_length(sql_type == FIELD_TYPE_VAR_STRING ? + FIELD_TYPE_STRING : sql_type, length); + break; + default: + /* do nothing */ + break; + } +} + /* Make a field from the .frm file info */ diff --git a/sql/field.h b/sql/field.h index 79c45a99a43..794b4a89542 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1066,6 +1066,7 @@ public: uint offset,pack_flag; create_field() :after(0) {} create_field(Field *field, Field *orig_field); + void create_length_to_internal_length(void); }; diff --git a/sql/item.h b/sql/item.h index 621dc017d25..3412b43da44 100644 --- a/sql/item.h +++ b/sql/item.h @@ -419,7 +419,7 @@ public: { collation.set(cs, dv); str_value.set(str,length,cs); - max_length=length; + max_length= str_value.numchars()*cs->mbmaxlen; set_name(str, length, cs); decimals=NOT_FIXED_DEC; } @@ -428,7 +428,7 @@ public: { collation.set(cs, dv); str_value.set(str,length,cs); - max_length=length; + max_length= str_value.numchars()*cs->mbmaxlen; set_name(name_par,0,cs); decimals=NOT_FIXED_DEC; } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 5880a9d4c8a..ed68f487924 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2078,10 +2078,20 @@ mysql_execute_command(THD *thd) res= mysql_create_like_table(thd, tables, &lex->create_info, (Table_ident *)lex->name); else + { + List_iterator fields(lex->create_list); + create_field *field; + while ((field= fields++)) + { + if (!field->charset) + field->charset= lex->create_info.table_charset; + field->create_length_to_internal_length(); + } res= mysql_create_table(thd,tables->db ? tables->db : thd->db, tables->real_name, &lex->create_info, lex->create_list, lex->key_list,0,0,0); // do logging + } if (!res) send_ok(thd); } @@ -3791,7 +3801,7 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, break; case FIELD_TYPE_STRING: case FIELD_TYPE_VAR_STRING: - if (new_field->length < MAX_FIELD_WIDTH || default_value) + if (new_field->length <= MAX_FIELD_CHARLENGTH || default_value) break; /* Convert long CHAR() and VARCHAR columns to TEXT or BLOB */ new_field->sql_type= FIELD_TYPE_BLOB; @@ -3957,13 +3967,13 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, } } - if (new_field->length >= MAX_FIELD_WIDTH || + if (new_field->length > MAX_FIELD_CHARLENGTH || (!new_field->length && !(new_field->flags & BLOB_FLAG) && type != FIELD_TYPE_STRING && type != FIELD_TYPE_VAR_STRING && type != FIELD_TYPE_GEOMETRY)) { net_printf(thd,ER_TOO_BIG_FIELDLENGTH,field_name, - MAX_FIELD_WIDTH-1); /* purecov: inspected */ + MAX_FIELD_CHARLENGTH); /* purecov: inspected */ DBUG_RETURN(1); /* purecov: inspected */ } type_modifier&= AUTO_INCREMENT_FLAG; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index e16d7a0067d..7cb8dfaae0d 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -457,12 +457,14 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, else { /* Field redefined */ + sql_field->sql_type= dup_field->sql_type; + sql_field->charset= dup_field->charset ? dup_field->charset : create_info->table_charset; sql_field->length= dup_field->length; + sql_field->pack_length= dup_field->pack_length; + sql_field->create_length_to_internal_length(); sql_field->decimals= dup_field->decimals; sql_field->flags= dup_field->flags; - sql_field->pack_length= dup_field->pack_length; sql_field->unireg_check= dup_field->unireg_check; - sql_field->sql_type= dup_field->sql_type; it2.remove(); // Remove first (create) definition select_field_pos--; break; @@ -480,10 +482,7 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, while ((sql_field=it++)) { if (!sql_field->charset) - sql_field->charset = create_info->table_charset ? - create_info->table_charset : - thd->variables.character_set_database; - + sql_field->charset = create_info->table_charset; switch (sql_field->sql_type) { case FIELD_TYPE_BLOB: case FIELD_TYPE_MEDIUM_BLOB: @@ -1891,18 +1890,42 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, } /* Full alter table */ + + /* let new create options override the old ones */ + if (!(used_fields & HA_CREATE_USED_MIN_ROWS)) + create_info->min_rows=table->min_rows; + if (!(used_fields & HA_CREATE_USED_MAX_ROWS)) + create_info->max_rows=table->max_rows; + if (!(used_fields & HA_CREATE_USED_AVG_ROW_LENGTH)) + create_info->avg_row_length=table->avg_row_length; + if (!(used_fields & HA_CREATE_USED_CHARSET)) + create_info->table_charset=table->table_charset; + restore_record(table,default_values); // Empty record for DEFAULT List_iterator drop_it(drop_list); List_iterator def_it(fields); List_iterator alter_it(alter_list); List create_list; // Add new fields here List key_list; // Add new keys here + create_field *def; + + /* + For each column set charset to the table + default if the column charset hasn't been specified + explicitely. Change CREATE length into internal length + */ + def_it.rewind(); + while ((def= def_it++)) + { + if (!def->charset) + def->charset= create_info->table_charset; + def->create_length_to_internal_length(); + } /* First collect all fields from table which isn't in drop_list */ - create_field *def; Field **f_ptr,*field; for (f_ptr=table->field ; (field= *f_ptr) ; f_ptr++) { @@ -2121,16 +2144,6 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, if (!create_info->comment) create_info->comment=table->comment; - /* let new create options override the old ones */ - if (!(used_fields & HA_CREATE_USED_MIN_ROWS)) - create_info->min_rows=table->min_rows; - if (!(used_fields & HA_CREATE_USED_MAX_ROWS)) - create_info->max_rows=table->max_rows; - if (!(used_fields & HA_CREATE_USED_AVG_ROW_LENGTH)) - create_info->avg_row_length=table->avg_row_length; - if (!(used_fields & HA_CREATE_USED_CHARSET)) - create_info->table_charset=table->table_charset; - table->file->update_create_info(create_info); if ((create_info->table_options & (HA_OPTION_PACK_KEYS | HA_OPTION_NO_PACK_KEYS)) || diff --git a/sql/unireg.h b/sql/unireg.h index 4bbfa8b0fae..4920d4b609a 100644 --- a/sql/unireg.h +++ b/sql/unireg.h @@ -58,7 +58,12 @@ #endif #define MAX_HOSTNAME 61 /* len+1 in mysql.user */ -#define MAX_FIELD_WIDTH 256 /* Max column width +1 */ +#define MAX_MBWIDTH 3 /* Max multibyte sequence */ +#define MAX_FIELD_CHARLENGTH 255 +/* Max column width +1 */ +#define MAX_FIELD_WIDTH (MAX_FIELD_CHARLENGTH*MAX_MBWIDTH+1) + + #define MAX_TABLES (sizeof(table_map)*8-2) /* Max tables in join */ #define OUTER_REF_TABLE_BIT (((table_map) 1) << (sizeof(table_map)*8-2)) #define RAND_TABLE_BIT (((table_map) 1) << (sizeof(table_map)*8-1)) From 2ad06dc68e3ca572d2abbe89b470f2a510466ad3 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 11 Aug 2003 23:43:01 +0400 Subject: [PATCH 04/10] Implemented UTC_TIME, UTC_DATE and UTC_TIMESTAMP functions (WL#345) configure.in: ./configure now tests if gmtime_r is present include/config-os2.h: Supposing that OS/2 have gmtime_r include/my_pthread.h: Use our imeplementation of gmtime_r if system lacks one mysql-test/r/func_time.result: Added UTC_* functions to test mysql-test/t/func_time.test: Added UTC_* functions to test mysys/my_pthread.c: Our implementation of gmtime_r mysys/my_thr_init.c: Now we also need LOCK_locktime_r if gmtime_r is absent sql/item_timefunc.cc: Generalized classes for CURDATE, CURTIME and NOW, abstracted them from timezone. Added new children classes for implementing these and UTC_* functions. sql/item_timefunc.h: Generalized classes for CURDATE, CURTIME and NOW, abstracted them from timezone. Added new children classes for implementing these and UTC_* functions. sql/lex.h: Added tokens for UTC_TIME, UTC_DATE and UTC_TIMESTAMP sql/sql_yacc.yy: Added UTC_* functions to grammar. Current functions are using classes now. --- configure.in | 2 +- include/config-os2.h | 3 + include/my_pthread.h | 9 +++ mysql-test/r/func_time.result | 18 +++++ mysql-test/t/func_time.test | 9 +++ mysys/my_pthread.c | 21 ++++- mysys/my_thr_init.c | 6 +- sql/item_timefunc.cc | 148 ++++++++++++++++++++++++---------- sql/item_timefunc.h | 80 ++++++++++++++++-- sql/lex.h | 3 + sql/sql_yacc.yy | 19 +++-- 11 files changed, 260 insertions(+), 58 deletions(-) diff --git a/configure.in b/configure.in index 63b33e391b2..12615a49f8f 100644 --- a/configure.in +++ b/configure.in @@ -1812,7 +1812,7 @@ AC_CHECK_FUNCS(alarm bmove \ gethostbyaddr_r gethostbyname_r getpwnam \ bfill bzero bcmp strstr strpbrk strerror \ tell atod memcpy memmove \ - setupterm strcasecmp sighold vidattr lrand48 localtime_r \ + setupterm strcasecmp sighold vidattr lrand48 localtime_r gmtime_r \ sigset sigthreadmask pthread_sigmask pthread_setprio pthread_setprio_np \ pthread_setschedparam pthread_attr_setprio pthread_attr_setschedparam \ pthread_attr_create pthread_getsequence_np pthread_attr_setstacksize \ diff --git a/include/config-os2.h b/include/config-os2.h index 7e9684ae3f5..0402074acc0 100644 --- a/include/config-os2.h +++ b/include/config-os2.h @@ -463,6 +463,9 @@ typedef unsigned long long os_off_t; /* Define if you have the getwd function. */ #define HAVE_GETWD 1 +/* Define to 1 if you have the `gmtime_r' function. */ +#define HAVE_GMTIME_R 1 + /* Define if you have the index function. */ #define HAVE_INDEX 1 diff --git a/include/my_pthread.h b/include/my_pthread.h index bff82ef7320..d8374cad314 100644 --- a/include/my_pthread.h +++ b/include/my_pthread.h @@ -100,6 +100,8 @@ int pthread_attr_setstacksize(pthread_attr_t *connect_att,DWORD stack); int pthread_attr_setprio(pthread_attr_t *connect_att,int priority); int pthread_attr_destroy(pthread_attr_t *connect_att); struct tm *localtime_r(const time_t *timep,struct tm *tmp); +struct tm *gmtime_r(const time_t *timep,struct tm *tmp); + void pthread_exit(void *a); /* was #define pthread_exit(A) ExitThread(A)*/ @@ -109,6 +111,7 @@ void pthread_exit(void *a); /* was #define pthread_exit(A) ExitThread(A)*/ #endif #define pthread_self() win_pthread_self #define HAVE_LOCALTIME_R 1 +#define HAVE_GMTIME_R 1 #define _REENTRANT 1 #define HAVE_PTHREAD_ATTR_SETSTACKSIZE 1 @@ -276,6 +279,8 @@ extern int my_pthread_create_detached; #define USE_ALARM_THREAD #undef HAVE_LOCALTIME_R #define HAVE_LOCALTIME_R +#undef HAVE_GMTIME_R +#define HAVE_GMTIME_R #undef HAVE_PTHREAD_ATTR_SETSCOPE #define HAVE_PTHREAD_ATTR_SETSCOPE #undef HAVE_GETHOSTBYNAME_R_GLIBC2_STYLE /* If we are running linux */ @@ -376,6 +381,10 @@ void *my_pthread_getspecific_imp(pthread_key_t key); struct tm *localtime_r(const time_t *clock, struct tm *res); #endif +#ifndef HAVE_GMTIME_R +struct tm *gmtime_r(const time_t *clock, struct tm *res); +#endif + #ifdef HAVE_PTHREAD_CONDATTR_CREATE /* DCE threads on HPUX 10.20 */ #define pthread_condattr_init pthread_condattr_create diff --git a/mysql-test/r/func_time.result b/mysql-test/r/func_time.result index 38fe97a79a6..c65dbbb926f 100644 --- a/mysql-test/r/func_time.result +++ b/mysql-test/r/func_time.result @@ -465,3 +465,21 @@ select date_add(time,INTERVAL 1 SECOND) from t1; date_add(time,INTERVAL 1 SECOND) 2006-07-08 00:00:01 drop table t1; +select strcmp(date_sub(localtimestamp(), interval 3 hour), utc_timestamp())=0; +strcmp(date_sub(localtimestamp(), interval 3 hour), utc_timestamp())=0 +1 +select strcmp(date_format(date_sub(localtimestamp(), interval 3 hour),"%T"), utc_time())=0; +strcmp(date_format(date_sub(localtimestamp(), interval 3 hour),"%T"), utc_time())=0 +1 +select strcmp(date_format(date_sub(localtimestamp(), interval 3 hour),"%Y-%m-%d"), utc_date())=0; +strcmp(date_format(date_sub(localtimestamp(), interval 3 hour),"%Y-%m-%d"), utc_date())=0 +1 +select strcmp(date_format(utc_timestamp(),"%T"), utc_time())=0; +strcmp(date_format(utc_timestamp(),"%T"), utc_time())=0 +1 +select strcmp(date_format(utc_timestamp(),"%Y-%m-%d"), utc_date())=0; +strcmp(date_format(utc_timestamp(),"%Y-%m-%d"), utc_date())=0 +1 +select strcmp(concat(utc_date(),' ',utc_time()),utc_timestamp())=0; +strcmp(concat(utc_date(),' ',utc_time()),utc_timestamp())=0 +1 diff --git a/mysql-test/t/func_time.test b/mysql-test/t/func_time.test index af222b0b3cc..4be6570d410 100644 --- a/mysql-test/t/func_time.test +++ b/mysql-test/t/func_time.test @@ -220,3 +220,12 @@ select date_add(date,INTERVAL "1 1:1:1" DAY_SECOND) from t1; # The following is not as one would expect... select date_add(time,INTERVAL 1 SECOND) from t1; drop table t1; + +# Test SAPDB UTC_% functions. This part is TZ dependant (It is supposed that +# TZ variable set to GMT-3 +select strcmp(date_sub(localtimestamp(), interval 3 hour), utc_timestamp())=0; +select strcmp(date_format(date_sub(localtimestamp(), interval 3 hour),"%T"), utc_time())=0; +select strcmp(date_format(date_sub(localtimestamp(), interval 3 hour),"%Y-%m-%d"), utc_date())=0; +select strcmp(date_format(utc_timestamp(),"%T"), utc_time())=0; +select strcmp(date_format(utc_timestamp(),"%Y-%m-%d"), utc_date())=0; +select strcmp(concat(utc_date(),' ',utc_time()),utc_timestamp())=0; diff --git a/mysys/my_pthread.c b/mysys/my_pthread.c index 4f472f61593..32528707480 100644 --- a/mysys/my_pthread.c +++ b/mysys/my_pthread.c @@ -133,10 +133,13 @@ int my_sigwait(const sigset_t *set,int *sig) /* localtime_r for SCO 3.2V4.2 */ -#ifndef HAVE_LOCALTIME_R +#if !defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R) extern pthread_mutex_t LOCK_localtime_r; +#endif + +#if !defined(HAVE_LOCALTIME_R) struct tm *localtime_r(const time_t *clock, struct tm *res) { struct tm *tmp; @@ -148,6 +151,22 @@ struct tm *localtime_r(const time_t *clock, struct tm *res) } #endif +#if !defined(HAVE_GMTIME_R) +/* + Reentrant version of standard gmtime() function. + Needed on some systems which don't implement it. +*/ + +struct tm *gmtime_r(const time_t *clock, struct tm *res) +{ + struct tm *tmp; + pthread_mutex_lock(&LOCK_localtime_r); + tmp= gmtime(clock); + *res= *tmp; + pthread_mutex_unlock(&LOCK_localtime_r); + return res; +} +#endif /**************************************************************************** ** Replacement of sigwait if the system doesn't have one (like BSDI 3.0) diff --git a/mysys/my_thr_init.c b/mysys/my_thr_init.c index 59466083b28..32bc8ea3724 100644 --- a/mysys/my_thr_init.c +++ b/mysys/my_thr_init.c @@ -31,7 +31,7 @@ pthread_key(struct st_my_thread_var, THR_KEY_mysys); pthread_mutex_t THR_LOCK_malloc,THR_LOCK_open,THR_LOCK_keycache, THR_LOCK_lock,THR_LOCK_isam,THR_LOCK_myisam,THR_LOCK_heap, THR_LOCK_net, THR_LOCK_charset; -#ifndef HAVE_LOCALTIME_R +#if !defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R) pthread_mutex_t LOCK_localtime_r; #endif #ifndef HAVE_GETHOSTBYNAME_R @@ -73,7 +73,7 @@ my_bool my_thread_global_init(void) #if defined( __WIN__) || defined(OS2) win_pthread_init(); #endif -#ifndef HAVE_LOCALTIME_R +#if !defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R) pthread_mutex_init(&LOCK_localtime_r,MY_MUTEX_INIT_SLOW); #endif #ifndef HAVE_GETHOSTBYNAME_R @@ -103,7 +103,7 @@ void my_thread_global_end(void) pthread_mutex_destroy(&THR_LOCK_heap); pthread_mutex_destroy(&THR_LOCK_net); pthread_mutex_destroy(&THR_LOCK_charset); -#ifndef HAVE_LOCALTIME_R +#if !defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R) pthread_mutex_destroy(&LOCK_localtime_r); #endif #ifndef HAVE_GETHOSTBYNAME_R diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 62d8afd7ec0..2948438d76f 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -571,21 +571,21 @@ longlong Item_func_from_days::val_int() void Item_func_curdate::fix_length_and_dec() { - struct tm tm_tmp,*start; - time_t query_start=current_thd->query_start(); + struct tm start; collation.set(default_charset()); decimals=0; max_length=10*default_charset()->mbmaxlen; - localtime_r(&query_start,&tm_tmp); - start=&tm_tmp; - value=(longlong) ((ulong) ((uint) start->tm_year+1900)*10000L+ - ((uint) start->tm_mon+1)*100+ - (uint) start->tm_mday); + + store_now_in_tm(current_thd->query_start(),&start); + + value=(longlong) ((ulong) ((uint) start.tm_year+1900)*10000L+ + ((uint) start.tm_mon+1)*100+ + (uint) start.tm_mday); /* For getdate */ - ltime.year= start->tm_year+1900; - ltime.month= start->tm_mon+1; - ltime.day= start->tm_mday; + ltime.year= start.tm_year+1900; + ltime.month= start.tm_mon+1; + ltime.day= start.tm_mday; ltime.hour= 0; ltime.minute= 0; ltime.second= 0; @@ -594,6 +594,7 @@ void Item_func_curdate::fix_length_and_dec() ltime.time_type=TIMESTAMP_DATE; } + bool Item_func_curdate::get_date(TIME *res, bool fuzzy_date __attribute__((unused))) { @@ -601,6 +602,27 @@ bool Item_func_curdate::get_date(TIME *res, return 0; } + +/* + Converts time in time_t to struct tm represenatation for local timezone. + Defines timezone (local) used for whole CURDATE function +*/ +void Item_func_curdate_local::store_now_in_tm(time_t now, struct tm *now_tm) +{ + localtime_r(&now,now_tm); +} + + +/* + Converts time in time_t to struct tm represenatation for UTC + Defines timezone (UTC) used for whole UTC_DATE function +*/ +void Item_func_curdate_utc::store_now_in_tm(time_t now, struct tm *now_tm) +{ + gmtime_r(&now,now_tm); +} + + String *Item_func_curtime::val_str(String *str) { str_value.set(buff,buff_length,default_charset()); @@ -609,23 +631,43 @@ String *Item_func_curtime::val_str(String *str) void Item_func_curtime::fix_length_and_dec() { - struct tm tm_tmp,*start; - time_t query_start=current_thd->query_start(); - CHARSET_INFO *cs=default_charset(); + struct tm start; + CHARSET_INFO *cs= default_charset(); decimals=0; max_length=8*cs->mbmaxlen; - localtime_r(&query_start,&tm_tmp); - start=&tm_tmp; collation.set(cs); - value=(longlong) ((ulong) ((uint) start->tm_hour)*10000L+ - (ulong) (((uint) start->tm_min)*100L+ - (uint) start->tm_sec)); + + store_now_in_tm(current_thd->query_start(),&start); + + value=(longlong) ((ulong) ((uint) start.tm_hour)*10000L+ + (ulong) (((uint) start.tm_min)*100L+ + (uint) start.tm_sec)); buff_length=cs->cset->snprintf(cs,buff,sizeof(buff),"%02d:%02d:%02d", - (int) start->tm_hour, - (int) start->tm_min, - (int) start->tm_sec); + (int) start.tm_hour, + (int) start.tm_min, + (int) start.tm_sec); +} + + +/* + Converts time in time_t to struct tm represenatation for local timezone. + Defines timezone (local) used for whole CURTIME function +*/ +void Item_func_curtime_local::store_now_in_tm(time_t now, struct tm *now_tm) +{ + localtime_r(&now,now_tm); +} + + +/* + Converts time in time_t to struct tm represenatation for UTC. + Defines timezone (UTC) used for whole UTC_TIME function +*/ +void Item_func_curtime_utc::store_now_in_tm(time_t now, struct tm *now_tm) +{ + gmtime_r(&now,now_tm); } @@ -638,37 +680,37 @@ String *Item_func_now::val_str(String *str) void Item_func_now::fix_length_and_dec() { - struct tm tm_tmp,*start; - time_t query_start=current_thd->query_start(); + struct tm start; CHARSET_INFO *cs= &my_charset_bin; decimals=0; max_length=19*cs->mbmaxlen; collation.set(cs); - localtime_r(&query_start,&tm_tmp); - start=&tm_tmp; - value=((longlong) ((ulong) ((uint) start->tm_year+1900)*10000L+ - (((uint) start->tm_mon+1)*100+ - (uint) start->tm_mday))*(longlong) 1000000L+ - (longlong) ((ulong) ((uint) start->tm_hour)*10000L+ - (ulong) (((uint) start->tm_min)*100L+ - (uint) start->tm_sec))); + + store_now_in_tm(current_thd->query_start(),&start); + + value=((longlong) ((ulong) ((uint) start.tm_year+1900)*10000L+ + (((uint) start.tm_mon+1)*100+ + (uint) start.tm_mday))*(longlong) 1000000L+ + (longlong) ((ulong) ((uint) start.tm_hour)*10000L+ + (ulong) (((uint) start.tm_min)*100L+ + (uint) start.tm_sec))); buff_length= (uint) cs->cset->snprintf(cs,buff, sizeof(buff), "%04d-%02d-%02d %02d:%02d:%02d", - ((int) (start->tm_year+1900)) % 10000, - (int) start->tm_mon+1, - (int) start->tm_mday, - (int) start->tm_hour, - (int) start->tm_min, - (int) start->tm_sec); + ((int) (start.tm_year+1900)) % 10000, + (int) start.tm_mon+1, + (int) start.tm_mday, + (int) start.tm_hour, + (int) start.tm_min, + (int) start.tm_sec); /* For getdate */ - ltime.year= start->tm_year+1900; - ltime.month= start->tm_mon+1; - ltime.day= start->tm_mday; - ltime.hour= start->tm_hour; - ltime.minute= start->tm_min; - ltime.second= start->tm_sec; + ltime.year= start.tm_year+1900; + ltime.month= start.tm_mon+1; + ltime.day= start.tm_mday; + ltime.hour= start.tm_hour; + ltime.minute= start.tm_min; + ltime.second= start.tm_sec; ltime.second_part=0; ltime.neg=0; ltime.time_type=TIMESTAMP_FULL; @@ -690,6 +732,26 @@ int Item_func_now::save_in_field(Field *to, bool no_conversions) } +/* + Converts time in time_t to struct tm represenatation for local timezone. + Defines timezone (local) used for whole CURRENT_TIMESTAMP function +*/ +void Item_func_now_local::store_now_in_tm(time_t now, struct tm *now_tm) +{ + localtime_r(&now,now_tm); +} + + +/* + Converts time in time_t to struct tm represenatation for UTC. + Defines timezone (UTC) used for whole UTC_TIMESTAMP function +*/ +void Item_func_now_utc::store_now_in_tm(time_t now, struct tm *now_tm) +{ + gmtime_r(&now,now_tm); +} + + String *Item_func_sec_to_time::val_str(String *str) { char buff[23*2]; diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index a6fb9b11de4..2196f83884a 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -350,6 +350,8 @@ public: }; +/* Abstract CURTIME function. Children should define what timezone is used */ + class Item_func_curtime :public Item_func { longlong value; @@ -363,29 +365,77 @@ public: double val() { return (double) value; } longlong val_int() { return value; } String *val_str(String *str); - const char *func_name() const { return "curtime"; } void fix_length_and_dec(); Field *tmp_table_field() { return result_field; } Field *tmp_table_field(TABLE *t_arg) { return (new Field_time(maybe_null, name, t_arg, default_charset())); - } + } + /* + Abstract method that defines which time zone is used for conversion. + Converts time from time_t representation to broken down representation + in struct tm using gmtime_r or localtime_r functions. + */ + virtual void store_now_in_tm(time_t now, struct tm *now_tm)=0; }; +class Item_func_curtime_local :public Item_func_curtime +{ +public: + Item_func_curtime_local() :Item_func_curtime() {} + Item_func_curtime_local(Item *a) :Item_func_curtime(a) {} + const char *func_name() const { return "curtime"; } + void store_now_in_tm(time_t now, struct tm *now_tm); +}; + + +class Item_func_curtime_utc :public Item_func_curtime +{ +public: + Item_func_curtime_utc() :Item_func_curtime() {} + Item_func_curtime_utc(Item *a) :Item_func_curtime(a) {} + const char *func_name() const { return "utc_time"; } + void store_now_in_tm(time_t now, struct tm *now_tm); +}; + + +/* Abstract CURDATE function. See also Item_func_curtime. */ + class Item_func_curdate :public Item_date { longlong value; TIME ltime; public: Item_func_curdate() :Item_date() {} + void set_result_from_tm(struct tm *now); longlong val_int() { return (value) ; } - const char *func_name() const { return "curdate"; } - void fix_length_and_dec(); /* Retrieves curtime */ + void fix_length_and_dec(); bool get_date(TIME *res,bool fuzzy_date); + virtual void store_now_in_tm(time_t now, struct tm *now_tm)=0; }; +class Item_func_curdate_local :public Item_func_curdate +{ +public: + Item_func_curdate_local() :Item_func_curdate() {} + const char *func_name() const { return "curdate"; } + void store_now_in_tm(time_t now, struct tm *now_tm); +}; + + +class Item_func_curdate_utc :public Item_func_curdate +{ +public: + Item_func_curdate_utc() :Item_func_curdate() {} + const char *func_name() const { return "utc_date"; } + void store_now_in_tm(time_t now, struct tm *now_tm); +}; + + +/* Abstract CURRENT_TIMESTAMP function. See also Item_func_curtime */ + class Item_func_now :public Item_date_func { longlong value; @@ -400,9 +450,29 @@ public: longlong val_int() { return value; } int save_in_field(Field *to, bool no_conversions); String *val_str(String *str); - const char *func_name() const { return "now"; } void fix_length_and_dec(); bool get_date(TIME *res,bool fuzzy_date); + virtual void store_now_in_tm(time_t now, struct tm *now_tm)=0; +}; + + +class Item_func_now_local :public Item_func_now +{ +public: + Item_func_now_local() :Item_func_now() {} + Item_func_now_local(Item *a) :Item_func_now(a) {} + const char *func_name() const { return "now"; } + void store_now_in_tm(time_t now, struct tm *now_tm); +}; + + +class Item_func_now_utc :public Item_func_now +{ +public: + Item_func_now_utc() :Item_func_now() {} + Item_func_now_utc(Item *a) :Item_func_now(a) {} + const char *func_name() const { return "utc_timestamp"; } + void store_now_in_tm(time_t now, struct tm *now_tm); }; diff --git a/sql/lex.h b/sql/lex.h index c2860f4551a..35c4d990b32 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -404,6 +404,9 @@ static SYMBOL symbols[] = { { "USE_FRM", SYM(USE_FRM),0,0}, { "USER", SYM(USER),0,0}, { "USING", SYM(USING),0,0}, + { "UTC_DATE", SYM(UTC_DATE_SYM),0,0}, + { "UTC_TIME", SYM(UTC_TIME_SYM),0,0}, + { "UTC_TIMESTAMP", SYM(UTC_TIMESTAMP_SYM),0,0}, { "UPDATE", SYM(UPDATE_SYM),0,0}, { "USAGE", SYM(USAGE),0,0}, { "VALUE", SYM(VALUE_SYM),0,0}, diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index aef58e6cb94..856cc3842fc 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -526,6 +526,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token UNIQUE_USERS %token UNIX_TIMESTAMP %token USER +%token UTC_DATE_SYM +%token UTC_TIME_SYM +%token UTC_TIMESTAMP_SYM %token WEEK_SYM %token WHEN_SYM %token WORK_SYM @@ -2372,12 +2375,12 @@ simple_expr: | CONCAT_WS '(' expr ',' expr_list ')' { $$= new Item_func_concat_ws($3, *$5); } | CURDATE optional_braces - { $$= new Item_func_curdate(); Lex->safe_to_cache_query=0; } + { $$= new Item_func_curdate_local(); Lex->safe_to_cache_query=0; } | CURTIME optional_braces - { $$= new Item_func_curtime(); Lex->safe_to_cache_query=0; } + { $$= new Item_func_curtime_local(); Lex->safe_to_cache_query=0; } | CURTIME '(' expr ')' { - $$= new Item_func_curtime($3); + $$= new Item_func_curtime_local($3); Lex->safe_to_cache_query=0; } | DATE_ADD_INTERVAL '(' expr ',' interval_expr interval ')' @@ -2539,9 +2542,9 @@ simple_expr: { $$= new Item_func_spatial_collection(* $3, Geometry::wkbMultiPolygon, Geometry::wkbPolygon ); } | NOW_SYM optional_braces - { $$= new Item_func_now(); Lex->safe_to_cache_query=0;} + { $$= new Item_func_now_local(); Lex->safe_to_cache_query=0;} | NOW_SYM '(' expr ')' - { $$= new Item_func_now($3); Lex->safe_to_cache_query=0;} + { $$= new Item_func_now_local($3); Lex->safe_to_cache_query=0;} | PASSWORD '(' expr ')' { $$= new Item_func_password($3); } | PASSWORD '(' expr ',' expr ')' @@ -2669,6 +2672,12 @@ simple_expr: { $$= new Item_func_unix_timestamp($3); } | USER '(' ')' { $$= new Item_func_user(); Lex->safe_to_cache_query=0; } + | UTC_DATE_SYM optional_braces + { $$= new Item_func_curdate_utc(); Lex->safe_to_cache_query=0;} + | UTC_TIME_SYM optional_braces + { $$= new Item_func_curtime_utc(); Lex->safe_to_cache_query=0;} + | UTC_TIMESTAMP_SYM optional_braces + { $$= new Item_func_now_utc(); Lex->safe_to_cache_query=0;} | WEEK_SYM '(' expr ')' { $$= new Item_func_week($3,new Item_int((char*) "0", From 2338e2eaf9392ece5faea1f19147d4402590314d Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 12 Aug 2003 16:42:52 +0500 Subject: [PATCH 05/10] LIKE didn't work with non-default charset --- mysql-test/r/func_like.result | 67 +++++++++++++++++++++++++++++++++++ mysql-test/t/func_like.test | 20 +++++++++++ sql/item_cmpfunc.cc | 2 +- 3 files changed, 88 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/func_like.result b/mysql-test/r/func_like.result index f90e694f5f0..cf07ba88477 100644 --- a/mysql-test/r/func_like.result +++ b/mysql-test/r/func_like.result @@ -37,3 +37,70 @@ select * from t1 where a like "%abc\d%"; a abcd drop table t1; +SET NAMES koi8r; +CREATE TABLE t1 (a VARCHAR(10) CHARACTER SET koi8r); +INSERT INTO t1 VALUES (''),(''),(''),(''),(''),(''); +INSERT INTO t1 VALUES (''),(''),(''),(''); +INSERT INTO t1 VALUES (''),(''),(''),(''); +INSERT INTO t1 VALUES (''),(''),(''),(''); +SELECT * FROM t1 WHERE a LIKE '%%'; +a + + + + + + + + + + + + + + + + + + +SELECT * FROM t1 WHERE a LIKE '%%'; +a + + + + + + + + + + + + + + + + + + +SELECT * FROM t1 WHERE a LIKE '%'; +a + + + + + + + + + + + + + + + + + + +DROP TABLE t1; diff --git a/mysql-test/t/func_like.test b/mysql-test/t/func_like.test index 90b376e34df..0cd85385df4 100644 --- a/mysql-test/t/func_like.test +++ b/mysql-test/t/func_like.test @@ -24,3 +24,23 @@ select * from t1 where a like "%abcd%"; select * from t1 where a like "%abc\d%"; drop table t1; + + +# +# Test like with non-default character set +# + +SET NAMES koi8r; + +CREATE TABLE t1 (a VARCHAR(10) CHARACTER SET koi8r); + +INSERT INTO t1 VALUES (''),(''),(''),(''),(''),(''); +INSERT INTO t1 VALUES (''),(''),(''),(''); +INSERT INTO t1 VALUES (''),(''),(''),(''); +INSERT INTO t1 VALUES (''),(''),(''),(''); + +SELECT * FROM t1 WHERE a LIKE '%%'; +SELECT * FROM t1 WHERE a LIKE '%%'; +SELECT * FROM t1 WHERE a LIKE '%'; + +DROP TABLE t1; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 7d8da16338b..642be5491aa 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -2139,7 +2139,7 @@ bool Item_func_like::turboBM_matches(const char* text, int text_len) const int shift = pattern_len; int j = 0; int u = 0; - CHARSET_INFO *cs=system_charset_info; // QQ Needs to be fixed + CHARSET_INFO *cs= cmp.cmp_collation.collation; // QQ Needs to be fixed const int plm1= pattern_len - 1; const int tlmpl= text_len - pattern_len; From 45b145a96e9a5992b8a5c8a0a1a23d99951058ca Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 12 Aug 2003 15:04:49 +0300 Subject: [PATCH 06/10] fixed problem with reference on derived table fields (BUG#1031) mysql-test/r/derived.result: test of BUG#1031 mysql-test/t/derived.test: test of BUG#1031 sql/item_sum.cc: 'alias' parameter added to create_tmp_table sql/sql_derived.cc: Derived table should be named (to pass it name to Field and then to Item_field) sql/sql_select.cc: 'alias' parameter added to create_tmp_table sql/sql_select.h: 'alias' parameter added to create_tmp_table sql/sql_union.cc: 'alias' parameter added to create_tmp_table sql/sql_update.cc: 'alias' parameter added to create_tmp_table --- mysql-test/r/derived.result | 4 ++++ mysql-test/t/derived.test | 6 +++++- sql/item_sum.cc | 5 +++-- sql/sql_derived.cc | 3 ++- sql/sql_select.cc | 14 +++++++------- sql/sql_select.h | 3 ++- sql/sql_union.cc | 2 +- sql/sql_update.cc | 3 ++- 8 files changed, 26 insertions(+), 14 deletions(-) diff --git a/mysql-test/r/derived.result b/mysql-test/r/derived.result index f3b09164dda..f9e52174469 100644 --- a/mysql-test/r/derived.result +++ b/mysql-test/r/derived.result @@ -192,3 +192,7 @@ id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY ALL NULL NULL NULL NULL 6 Using where 2 DERIVED mp ALL NULL NULL NULL NULL 9 Using temporary; Using filesort 2 DERIVED m2 index NULL PRIMARY 3 NULL 9 Using index +drop table t1,t2; +SELECT a.x FROM (SELECT 1 AS x) AS a HAVING a.x = 1; +x +1 diff --git a/mysql-test/t/derived.test b/mysql-test/t/derived.test index c998de7ae6e..c3edbabcd53 100644 --- a/mysql-test/t/derived.test +++ b/mysql-test/t/derived.test @@ -88,5 +88,9 @@ SELECT d.pla_id, m2.test FROM t1 m2 INNER JOIN (SELECT mp.pla_id, MIN(m1.matint explain SELECT d.pla_id, m2.mat_id FROM t1 m2 INNER JOIN (SELECT mp.pla_id, MIN(m1.matintnum) AS matintnum FROM t2 mp INNER JOIN t1 m1 ON mp.mat_id=m1.mat_id GROUP BY mp.pla_id) d ON d.matintnum=m2.matintnum; explain SELECT d.pla_id, m2.test FROM t1 m2 INNER JOIN (SELECT mp.pla_id, MIN(m1.matintnum) AS matintnum FROM t2 mp INNER JOIN t1 m1 ON mp.mat_id=m1.mat_id GROUP BY mp.pla_id) d ON d.matintnum=m2.matintnum; +drop table t1,t2; -drop table t1,t2 +# +# derived table reference +# +SELECT a.x FROM (SELECT 1 AS x) AS a HAVING a.x = 1; diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 0da2725f3ab..71c4fe9f4b8 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -1147,7 +1147,7 @@ bool Item_sum_count_distinct::setup(THD *thd) if (!(table= create_tmp_table(thd, tmp_table_param, list, (ORDER*) 0, 1, 0, select_lex->options | thd->options, - HA_POS_ERROR))) + HA_POS_ERROR, (char*)""))) return 1; table->file->extra(HA_EXTRA_NO_ROWS); // Don't update rows table->no_rows=1; @@ -1834,7 +1834,8 @@ bool Item_func_group_concat::setup(THD *thd) (types, sizes and so on). */ if (!(table=create_tmp_table(thd, tmp_table_param, all_fields, 0, - 0, 0, 0,select_lex->options | thd->options))) + 0, 0, 0,select_lex->options | thd->options, + (char *) ""))) DBUG_RETURN(1); table->file->extra(HA_EXTRA_NO_ROWS); table->no_rows= 1; diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index 81439a19918..7f70cecdb04 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -157,7 +157,8 @@ int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *unit, is_union && !unit->union_option, 1, (select_cursor->options | thd->options | TMP_TABLE_ALL_COLUMNS), - HA_POS_ERROR))) + HA_POS_ERROR, + org_table_list->alias))) { res= -1; goto exit; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 71c0d0bdddc..a2ef96b7990 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -787,7 +787,8 @@ JOIN::optimize() group_list && simple_group, select_options, (order == 0 || skip_sort_order) ? select_limit : - HA_POS_ERROR))) + HA_POS_ERROR, + (char *) ""))) DBUG_RETURN(1); /* @@ -1120,7 +1121,8 @@ JOIN::exec() curr_join->select_distinct && !curr_join->group_list, 1, curr_join->select_options, - HA_POS_ERROR))) + HA_POS_ERROR, + (char *) ""))) DBUG_VOID_RETURN; curr_join->exec_tmp_table2= exec_tmp_table2; } @@ -4321,7 +4323,8 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, TABLE * create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List &fields, ORDER *group, bool distinct, bool save_sum_fields, - ulong select_options, ha_rows rows_limit) + ulong select_options, ha_rows rows_limit, + char *table_alias) { TABLE *table; uint i,field_count,reclength,null_count,null_pack_length, @@ -4410,10 +4413,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List &fields, table->field=reg_field; table->blob_field= (Field_blob**) blob_field; table->real_name=table->path=tmpname; - /* - This must be "" as field may refer to it after tempory table is dropped - */ - table->table_name= (char*) ""; + table->table_name= table_alias; table->reginfo.lock_type=TL_WRITE; /* Will be updated */ table->db_stat=HA_OPEN_KEYFILE+HA_OPEN_RNDFILE; table->blob_ptr_size=mi_portable_sizeof_char_ptr; diff --git a/sql/sql_select.h b/sql/sql_select.h index 76960876158..72ea42db87c 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -274,7 +274,8 @@ void TEST_join(JOIN *join); bool store_val_in_field(Field *field,Item *val); TABLE *create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List &fields, ORDER *group, bool distinct, bool save_sum_fields, - ulong select_options, ha_rows rows_limit); + ulong select_options, ha_rows rows_limit, + char* alias); void free_tmp_table(THD *thd, TABLE *entry); void count_field_types(TMP_TABLE_PARAM *param, List &fields, bool reset_with_sum_func); diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 41fc754e754..16c70d046dc 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -178,7 +178,7 @@ int st_select_lex_unit::prepare(THD *thd, select_result *sel_result, (ORDER*) 0, !union_option, 1, (select_cursor->options | thd->options | TMP_TABLE_ALL_COLUMNS), - HA_POS_ERROR))) + HA_POS_ERROR, (char*) ""))) goto err; table->file->extra(HA_EXTRA_WRITE_CACHE); table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); diff --git a/sql/sql_update.cc b/sql/sql_update.cc index e1c28dd0e4d..21d0a5aeb42 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -640,7 +640,8 @@ multi_update::initialize_tables(JOIN *join) temp_fields, (ORDER*) &group, 0, 0, TMP_TABLE_ALL_COLUMNS, - HA_POS_ERROR))) + HA_POS_ERROR, + (char *) ""))) DBUG_RETURN(1); tmp_tables[cnt]->file->extra(HA_EXTRA_WRITE_CACHE); } From abbcc4ffbba233a9a7eeeb037655de2bd92e7914 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 12 Aug 2003 17:40:11 +0300 Subject: [PATCH 07/10] fixed layout --- sql/sql_yacc.yy | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 856cc3842fc..c484a2d6f19 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1918,7 +1918,8 @@ select: LEX *lex= Lex; lex->sql_command= SQLCOM_SELECT; lex->select_lex.resolve_mode= SELECT_LEX::SELECT_MODE; - }; + } + ; /* Need select_init2 for subselects. */ select_init: From 95728904c9587d009e7d803384663c87ed1e79fd Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 14 Aug 2003 13:19:17 +0400 Subject: [PATCH 08/10] added compile-pentium-maintainer - equivalent of compile-pentium-debug but with --enable-maintainer-mode configure option Maybe it's worth to merge compile-mentium-debug and compile-pentium-maintainer to a single script --- BUILD/compile-pentium-maintainer | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100755 BUILD/compile-pentium-maintainer diff --git a/BUILD/compile-pentium-maintainer b/BUILD/compile-pentium-maintainer new file mode 100755 index 00000000000..1265154dc76 --- /dev/null +++ b/BUILD/compile-pentium-maintainer @@ -0,0 +1,13 @@ +#! /bin/sh + +path=`dirname $0` +. "$path/SETUP.sh" + +extra_flags="$pentium_cflags $debug_cflags" +c_warnings="$c_warnings $debug_extra_warnings" +cxx_warnings="$cxx_warnings $debug_extra_warnings" +extra_configs="$pentium_configs $debug_configs " + +extra_configs="$extra_configs --enable-maintainer-mode" + +. "$path/FINISH.sh" From 10ab9f273a26c333ccd9f1ff6ffa02738adeb637 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 18 Aug 2003 17:24:50 +0500 Subject: [PATCH 09/10] Fix strnxfrm_multiplye from 0 to 1 for charsets that do not use strnxfrm --- strings/ctype-big5.c | 2 +- strings/ctype-bin.c | 2 +- strings/ctype-euc_kr.c | 4 ++-- strings/ctype-gb2312.c | 4 ++-- strings/ctype-gbk.c | 2 +- strings/ctype-latin1.c | 4 ++-- strings/ctype-sjis.c | 2 +- strings/ctype-tis620.c | 2 +- strings/ctype-ucs2.c | 2 +- strings/ctype-ujis.c | 4 ++-- strings/ctype-utf8.c | 2 +- 11 files changed, 15 insertions(+), 15 deletions(-) diff --git a/strings/ctype-big5.c b/strings/ctype-big5.c index 10b6898ce24..88309aadaa0 100644 --- a/strings/ctype-big5.c +++ b/strings/ctype-big5.c @@ -6300,7 +6300,7 @@ CHARSET_INFO my_charset_big5_bin= NULL, /* tab_from_uni */ "", "", - 0, /* strxfrm_multiply */ + 1, /* strxfrm_multiply */ 2, /* mbmaxlen */ 0, &my_charset_big5_handler, diff --git a/strings/ctype-bin.c b/strings/ctype-bin.c index ebe73016db7..10ea23e07eb 100644 --- a/strings/ctype-bin.c +++ b/strings/ctype-bin.c @@ -312,7 +312,7 @@ CHARSET_INFO my_charset_bin = NULL, /* tab_to_uni */ NULL, /* tab_from_uni */ "","", - 0, /* strxfrm_multiply */ + 1, /* strxfrm_multiply */ 1, /* mbmaxlen */ (char) 255, /* max_sort_char */ &my_charset_handler, diff --git a/strings/ctype-euc_kr.c b/strings/ctype-euc_kr.c index 8e288ee8e55..e8d2c8c4d1d 100644 --- a/strings/ctype-euc_kr.c +++ b/strings/ctype-euc_kr.c @@ -8686,7 +8686,7 @@ CHARSET_INFO my_charset_euckr_korean_ci= NULL, /* tab_from_uni */ "", "", - 0, /* strxfrm_multiply */ + 1, /* strxfrm_multiply */ 2, /* mbmaxlen */ 0, &my_charset_handler, @@ -8709,7 +8709,7 @@ CHARSET_INFO my_charset_euckr_bin= NULL, /* tab_from_uni */ "", "", - 0, /* strxfrm_multiply */ + 1, /* strxfrm_multiply */ 2, /* mbmaxlen */ 0, &my_charset_handler, diff --git a/strings/ctype-gb2312.c b/strings/ctype-gb2312.c index 378931eecc7..72548305ad1 100644 --- a/strings/ctype-gb2312.c +++ b/strings/ctype-gb2312.c @@ -5736,7 +5736,7 @@ CHARSET_INFO my_charset_gb2312_chinese_ci= NULL, /* tab_from_uni */ "", "", - 0, /* strxfrm_multiply */ + 1, /* strxfrm_multiply */ 2, /* mbmaxlen */ 0, &my_charset_handler, @@ -5758,7 +5758,7 @@ CHARSET_INFO my_charset_gb2312_bin= NULL, /* tab_from_uni */ "", "", - 0, /* strxfrm_multiply */ + 1, /* strxfrm_multiply */ 2, /* mbmaxlen */ 0, &my_charset_handler, diff --git a/strings/ctype-gbk.c b/strings/ctype-gbk.c index 58d281ae485..84819d2e1a8 100644 --- a/strings/ctype-gbk.c +++ b/strings/ctype-gbk.c @@ -9955,7 +9955,7 @@ CHARSET_INFO my_charset_gbk_bin= NULL, /* tab_from_uni */ "", "", - 0, /* strxfrm_multiply */ + 1, /* strxfrm_multiply */ 2, /* mbmaxlen */ 0, &my_charset_handler, diff --git a/strings/ctype-latin1.c b/strings/ctype-latin1.c index b0c60e2e3eb..24e06df19a1 100644 --- a/strings/ctype-latin1.c +++ b/strings/ctype-latin1.c @@ -213,7 +213,7 @@ CHARSET_INFO my_charset_latin1= latin1_uni, /* tab_to_uni */ NULL, /* tab_from_uni */ "","", - 0, /* strxfrm_multiply */ + 1, /* strxfrm_multiply */ 1, /* mbmaxlen */ 0, &my_charset_handler, @@ -477,7 +477,7 @@ CHARSET_INFO my_charset_latin1_bin= NULL, /* tab_from_uni */ "", "", - 0, /* strxfrm_multiply */ + 1, /* strxfrm_multiply */ 1, /* mbmaxlen */ 0, &my_charset_handler, diff --git a/strings/ctype-sjis.c b/strings/ctype-sjis.c index 00d51dd23b5..d8443165462 100644 --- a/strings/ctype-sjis.c +++ b/strings/ctype-sjis.c @@ -4542,7 +4542,7 @@ CHARSET_INFO my_charset_sjis_bin= NULL, /* tab_from_uni */ "", "", - 0, /* strxfrm_multiply */ + 1, /* strxfrm_multiply */ 2, /* mbmaxlen */ 0, &my_charset_handler, diff --git a/strings/ctype-tis620.c b/strings/ctype-tis620.c index ab51ddd013f..643ea77c73e 100644 --- a/strings/ctype-tis620.c +++ b/strings/ctype-tis620.c @@ -762,7 +762,7 @@ CHARSET_INFO my_charset_tis620_bin= NULL, /* tab_from_uni */ "", "", - 0, /* strxfrm_multiply */ + 1, /* strxfrm_multiply */ 1, /* mbmaxlen */ 0, &my_charset_handler, diff --git a/strings/ctype-ucs2.c b/strings/ctype-ucs2.c index 5d78ab5dcff..6c0cfd3e874 100644 --- a/strings/ctype-ucs2.c +++ b/strings/ctype-ucs2.c @@ -1077,7 +1077,7 @@ CHARSET_INFO my_charset_ucs2_bin= NULL, /* tab_from_uni */ "", "", - 0, /* strxfrm_multiply */ + 1, /* strxfrm_multiply */ 2, /* mbmaxlen */ 0, &my_charset_handler, diff --git a/strings/ctype-ujis.c b/strings/ctype-ujis.c index eb31c49013a..c6cf6ff79ba 100644 --- a/strings/ctype-ujis.c +++ b/strings/ctype-ujis.c @@ -8477,7 +8477,7 @@ CHARSET_INFO my_charset_ujis_japanese_ci= NULL, /* tab_to_uni */ NULL, /* tab_from_uni */ "","", - 0, /* strxfrm_multiply */ + 1, /* strxfrm_multiply */ 3, /* mbmaxlen */ 0, &my_charset_handler, @@ -8499,7 +8499,7 @@ CHARSET_INFO my_charset_ujis_bin= NULL, /* tab_to_uni */ NULL, /* tab_from_uni */ "","", - 0, /* strxfrm_multiply */ + 1, /* strxfrm_multiply */ 3, /* mbmaxlen */ 0, &my_charset_handler, diff --git a/strings/ctype-utf8.c b/strings/ctype-utf8.c index 1982b9e3ba8..4ac397c4c48 100644 --- a/strings/ctype-utf8.c +++ b/strings/ctype-utf8.c @@ -2026,7 +2026,7 @@ CHARSET_INFO my_charset_utf8_bin= NULL, /* tab_from_uni */ "", "", - 0, /* strxfrm_multiply */ + 1, /* strxfrm_multiply */ 3, /* mbmaxlen */ 0, &my_charset_handler, From a7c82c5693110bfa744bd4cf481b02eaabb7a0aa Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 18 Aug 2003 17:27:09 +0500 Subject: [PATCH 10/10] Adding Gerogian GEOSTD8 character set --- sql/share/charsets/Index.xml | 9 ++- sql/share/charsets/geostd8.xml | 121 +++++++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 sql/share/charsets/geostd8.xml diff --git a/sql/share/charsets/Index.xml b/sql/share/charsets/Index.xml index c5a2ae5dfb6..3661784325c 100644 --- a/sql/share/charsets/Index.xml +++ b/sql/share/charsets/Index.xml @@ -1,6 +1,6 @@ - + This file lists all of the available character sets. @@ -522,6 +522,13 @@ To make maintaining easier please: + + South Asian + GEOSTD8 Georgian + + + + Binary pseudo charset diff --git a/sql/share/charsets/geostd8.xml b/sql/share/charsets/geostd8.xml new file mode 100644 index 00000000000..caf01af58d0 --- /dev/null +++ b/sql/share/charsets/geostd8.xml @@ -0,0 +1,121 @@ + + + + + + + + + 00 + 20 20 20 20 20 20 20 20 20 28 28 28 28 28 20 20 + 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 + 48 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 + 84 84 84 84 84 84 84 84 84 84 10 10 10 10 10 10 + 10 81 81 81 81 81 81 01 01 01 01 01 01 01 01 01 + 01 01 01 01 01 01 01 01 01 01 01 10 10 10 10 10 + 10 82 82 82 82 82 82 02 02 02 02 02 02 02 02 02 + 02 02 02 02 02 02 02 02 02 02 02 10 10 10 10 20 + 00 00 10 00 10 10 10 10 00 10 00 10 00 00 00 00 + 00 10 10 10 10 10 10 10 00 00 00 10 00 00 00 00 + 48 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 + 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 + 03 03 03 03 03 03 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + + + + + + + 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F + 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F + 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F + 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F + 40 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F + 70 71 72 73 74 75 76 77 78 79 7A 5B 5C 5D 5E 5F + 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F + 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F + 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F + 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F + A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF + B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF + C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF + D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF + E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF + F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF + + + + + + + 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F + 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F + 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F + 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F + 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F + 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F + 60 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F + 50 51 52 53 54 55 56 57 58 59 5A 7B 7C 7D 7E 7F + 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F + 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F + A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF + B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF + C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF + D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF + E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF + F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF + + + + + + +0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000A 000B 000C 000D 000E 000F +0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001A 001B 001C 001D 001E 001F +0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002A 002B 002C 002D 002E 002F +0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003A 003B 003C 003D 003E 003F +0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004A 004B 004C 004D 004E 004F +0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005A 005B 005C 005D 005E 005F +0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006A 006B 006C 006D 006E 006F +0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007A 007B 007C 007D 007E 007F +20AC 0000 201A 0000 201E 2026 2020 2021 0000 2030 0000 2039 0000 0000 0000 0000 +0000 2018 2019 201C 201D 2022 2013 2014 0000 0000 0000 203A 0000 0000 0000 0000 +00A0 00A1 00A2 00A3 00A4 00A5 00A6 00A7 00A8 00A9 00AA 00AB 00AC 00AD 00AE 00AF +00B0 00B1 00B2 00B3 00B4 00B5 00B6 00B7 00B8 00B9 00BA 00BB 00BC 00BD 00BE 00BF +10D0 10D1 10D2 10D3 10D4 10D5 10D6 10F1 10D7 10D8 10D9 10DA 10DB 10DC 10F2 10DD +10DE 10DF 10E0 10E1 10E2 10F3 10E3 10E4 10E5 10E6 10E7 10E8 10E9 10EA 10EB 10EC +10ED 10EE 10F4 10EF 10F0 10F5 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 +0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 2116 0000 0000 + + + + + + + 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F + 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F + 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F + 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F + 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F + 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F + 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F + 50 51 52 53 54 55 56 57 58 59 5A 7B 7C 7D 7E 7F + 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F + 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F + A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF + B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF + C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF + D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF + E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF + F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF + + + + + + + +