diff options
45 files changed, 910 insertions, 349 deletions
diff --git a/Build-tools/mysql-copyright b/Build-tools/mysql-copyright index a798ee7ab65..004476ff92c 100755 --- a/Build-tools/mysql-copyright +++ b/Build-tools/mysql-copyright @@ -101,8 +101,12 @@ sub main unlink("$destdir/PUBLIC", "$destdir/README"); copy("$WD/Docs/MySQLEULA.txt", "$destdir"); - # remove readline subdir - `rm -rf $destdir/cmd-line-utils/readline`; + # remove readline subdir and update configure accordingly + system("rm -rf $destdir/cmd-line-utils/readline"); + unlink ("$destdir/configure") or die "Can't delete $destdir/configure: $!\n"; + `(cd $destdir ; sed -e 's!\ cmd-line-utils\/readline\/Makefile\ dnl!!g' < configure.in > configure.in.new)`; + rename ("$destdir/configure.in.new","$destdir/configure.in") or die "Can't rename $destdir/configure.in.new: $!\n";; + `(cd $destdir ; autoconf)`; # fix file copyrights &fix_usage_copyright(); diff --git a/include/myisam.h b/include/myisam.h index f4ef4695016..ed4f4aff8fe 100644 --- a/include/myisam.h +++ b/include/myisam.h @@ -38,7 +38,7 @@ extern "C" { /* The following defines can be increased if necessary */ #define MI_MAX_KEY 32 /* Max allowed keys */ #define MI_MAX_KEY_SEG 16 /* Max segments for key */ -#define MI_MAX_KEY_LENGTH 500 +#define MI_MAX_KEY_LENGTH 1000 #define MI_MAX_KEY_BUFF (MI_MAX_KEY_LENGTH+MI_MAX_KEY_SEG*6+8+8) #define MI_MAX_POSSIBLE_KEY_BUFF (1024+6+6) /* For myisam_chk */ diff --git a/innobase/data/data0type.c b/innobase/data/data0type.c index 39c80b773a2..71ce5ff3d58 100644 --- a/innobase/data/data0type.c +++ b/innobase/data/data0type.c @@ -18,11 +18,93 @@ column definitions, or records in the insert buffer, we use this charset-collation code for them. */ ulint data_mysql_default_charset_coll = 99999999; +ulint data_mysql_latin1_swedish_charset_coll = 99999999; -dtype_t dtype_binary_val = {DATA_BINARY, 0, 0, 0, 0}; +dtype_t dtype_binary_val = {DATA_BINARY, 0, 0, 0}; dtype_t* dtype_binary = &dtype_binary_val; /************************************************************************* +Checks if a data main type is a string type. Also a BLOB is considered a +string type. */ + +ibool +dtype_is_string_type( +/*=================*/ + /* out: TRUE if string type */ + ulint mtype) /* in: InnoDB main data type code: DATA_CHAR, ... */ +{ + if (mtype <= DATA_BLOB + || mtype == DATA_MYSQL + || mtype == DATA_VARMYSQL) { + + return(TRUE); + } + + return(FALSE); +} + +/************************************************************************* +Checks if a type is a binary string type. Note that for tables created with +< 4.0.14, we do not know if a DATA_BLOB column is a BLOB or a TEXT column. For +those DATA_BLOB columns this function currently returns FALSE. */ + +ibool +dtype_is_binary_string_type( +/*========================*/ + /* out: TRUE if binary string type */ + ulint mtype, /* in: main data type */ + ulint prtype) /* in: precise type */ +{ + if ((mtype == DATA_FIXBINARY) + || (mtype == DATA_BINARY) + || (mtype == DATA_BLOB && (prtype & DATA_BINARY_TYPE))) { + + return(TRUE); + } + + return(FALSE); +} + +/************************************************************************* +Checks if a type is a non-binary string type. That is, dtype_is_string_type is +TRUE and dtype_is_binary_string_type is FALSE. Note that for tables created +with < 4.0.14, we do not know if a DATA_BLOB column is a BLOB or a TEXT column. +For those DATA_BLOB columns this function currently returns TRUE. */ + +ibool +dtype_is_non_binary_string_type( +/*============================*/ + /* out: TRUE if non-binary string type */ + ulint mtype, /* in: main data type */ + ulint prtype) /* in: precise type */ +{ + if (dtype_is_string_type(mtype) == TRUE + && dtype_is_binary_string_type(mtype, prtype) == FALSE) { + + return(TRUE); + } + + return(FALSE); +} + +/************************************************************************* +Forms a precise type from the < 4.1.2 format precise type plus the +charset-collation code. */ + +ulint +dtype_form_prtype( +/*==============*/ + ulint old_prtype, /* in: the MySQL type code and the flags + DATA_BINARY_TYPE etc. */ + ulint charset_coll) /* in: MySQL charset-collation code */ +{ + ut_a(old_prtype < 256 * 256); + ut_a(charset_coll < 256); + + return(old_prtype + (charset_coll << 16)); +} + +/************************************************************************* Validates a data type structure. */ ibool diff --git a/innobase/include/data0type.h b/innobase/include/data0type.h index 0b8d77ee542..2b27ead5fac 100644 --- a/innobase/include/data0type.h +++ b/innobase/include/data0type.h @@ -12,6 +12,7 @@ Created 1/16/1996 Heikki Tuuri #include "univ.i" extern ulint data_mysql_default_charset_coll; +extern ulint data_mysql_latin1_swedish_charset_coll; /* SQL data type struct */ typedef struct dtype_struct dtype_t; @@ -30,8 +31,8 @@ extern dtype_t* dtype_binary; #define DATA_BINARY 4 /* binary string */ #define DATA_BLOB 5 /* binary large object, or a TEXT type; if prtype & DATA_BINARY_TYPE == 0, then this is - actually a TEXT column; see also below about - the flag DATA_NONLATIN1 */ + actually a TEXT column (or a BLOB created + with < 4.0.14) */ #define DATA_INT 6 /* integer: can be any size 1 - 8 bytes */ #define DATA_SYS_CHILD 7 /* address of the child page in node pointer */ #define DATA_SYS 8 /* system column */ @@ -59,7 +60,7 @@ Tables created by a MySQL user have the following convention: code (not applicable for system columns). - In the second least significant byte we OR flags DATA_NOT_NULL, -DATA_UNSIGNED, DATA_BINARY_TYPE, DATA_NONLATIN1. +DATA_UNSIGNED, DATA_BINARY_TYPE. - In the third least significant byte of the precise type of string types we store the MySQL charset-collation code. In DATA_BLOB columns created with @@ -73,6 +74,23 @@ installation. If the stored charset code is 0 in the system table SYS_COLUMNS of InnoDB, that means that the default charset of this MySQL installation should be used. +When loading a table definition from the system tables to the InnoDB data +dictionary cache in main memory, InnoDB versions >= 4.1.2 and >= 5.0.1 check +if the stored charset-collation is 0, and if that is the case and the type is +a non-binary string, replace that 0 by the default charset-collation code of +this MySQL installation. In short, in old tables, the charset-collation code +in the system tables on disk can be 0, but in in-memory data structures +(dtype_t), the charset-collation code is always != 0 for non-binary string +types. + +In new tables, in binary string types, the charset-collation code is the +MySQL code for the 'binary charset', that is, != 0. + +For binary string types and for DATA_CHAR, DATA_VARCHAR, and for those +DATA_BLOB which are binary or have the charset-collation latin1_swedish_ci, +InnoDB performs all comparisons internally, without resorting to the MySQL +comparison functions. This is to save CPU time. + InnoDB's own internal system tables have different precise types for their columns, and for them the precise type is usually not used at all. */ @@ -112,14 +130,10 @@ be less than 256 */ string, this is ORed to the precise type: this only holds for tables created with >= MySQL-4.0.14 */ -#define DATA_NONLATIN1 2048 /* If the data type is DATA_BLOB with - the prtype & DATA_BINARY_TYPE == 0, that is, - TEXT, then in versions 4.0.14 - 4.0.xx this - flag is set to 1, if the charset is not - latin1. In version 4.1.1 this was set - to 1 for all TEXT columns. In versions >= 4.1.2 - this is set to 1 if the charset-collation of a - TEXT column is not latin1_swedish_ci. */ +/* #define DATA_NONLATIN1 2048 This is a relic from < 4.1.2 and < 5.0.1. + In earlier versions this was set for some + BLOB columns. +*/ /*-------------------------------------------*/ /* This many bytes we need to store the type information affecting the @@ -133,7 +147,7 @@ store the charset-collation number; one byte is left unused, though */ /************************************************************************* Checks if a data main type is a string type. Also a BLOB is considered a string type. */ -UNIV_INLINE + ibool dtype_is_string_type( /*=================*/ @@ -143,7 +157,7 @@ dtype_is_string_type( Checks if a type is a binary string type. Note that for tables created with < 4.0.14, we do not know if a DATA_BLOB column is a BLOB or a TEXT column. For those DATA_BLOB columns this function currently returns FALSE. */ -UNIV_INLINE + ibool dtype_is_binary_string_type( /*========================*/ @@ -154,9 +168,8 @@ dtype_is_binary_string_type( Checks if a type is a non-binary string type. That is, dtype_is_string_type is TRUE and dtype_is_binary_string_type is FALSE. Note that for tables created with < 4.0.14, we do not know if a DATA_BLOB column is a BLOB or a TEXT column. -For those DATA_BLOB columns this function currently returns FALSE. */ +For those DATA_BLOB columns this function currently returns TRUE. */ -UNIV_INLINE ibool dtype_is_non_binary_string_type( /*============================*/ @@ -206,11 +219,12 @@ dtype_get_charset_coll( /************************************************************************* Forms a precise type from the < 4.1.2 format precise type plus the charset-collation code. */ + ulint dtype_form_prtype( /*==============*/ ulint old_prtype, /* in: the MySQL type code and the flags - DATA_NONLATIN1 etc. */ + DATA_BINARY_TYPE etc. */ ulint charset_coll); /* in: MySQL charset-collation code */ /************************************************************************* Gets the type length. */ diff --git a/innobase/include/data0type.ic b/innobase/include/data0type.ic index 51a1d593d4b..21f617c7590 100644 --- a/innobase/include/data0type.ic +++ b/innobase/include/data0type.ic @@ -9,70 +9,6 @@ Created 1/16/1996 Heikki Tuuri #include "mach0data.h" /************************************************************************* -Checks if a data main type is a string type. Also a BLOB is considered a -string type. */ - -ibool -dtype_is_string_type( -/*=================*/ - /* out: TRUE if string type */ - ulint mtype) /* in: InnoDB main data type code: DATA_CHAR, ... */ -{ - if (mtype <= DATA_BLOB - || mtype == DATA_MYSQL - || mtype == DATA_VARMYSQL) { - - return(TRUE); - } - - return(FALSE); -} - -/************************************************************************* -Checks if a type is a binary string type. Note that for tables created with -< 4.0.14, we do not know if a DATA_BLOB column is a BLOB or a TEXT column. For -those DATA_BLOB columns this function currently returns FALSE. */ -UNIV_INLINE -ibool -dtype_is_binary_string_type( -/*========================*/ - /* out: TRUE if binary string type */ - ulint mtype, /* in: main data type */ - ulint prtype) /* in: precise type */ -{ - if ((mtype == DATA_FIXBINARY) - || (mtype == DATA_BINARY) - || (mtype == DATA_BLOB && (prtype & DATA_BINARY_TYPE))) { - - return(TRUE); - } - - return(FALSE); -} - -/************************************************************************* -Checks if a type is a non-binary string type. That is, dtype_is_string_type is -TRUE and dtype_is_binary_string_type is FALSE. Note that for tables created -with < 4.0.14, we do not know if a DATA_BLOB column is a BLOB or a TEXT column. -For those DATA_BLOB columns this function currently returns FALSE. */ -UNIV_INLINE -ibool -dtype_is_non_binary_string_type( -/*============================*/ - /* out: TRUE if non-binary string type */ - ulint mtype, /* in: main data type */ - ulint prtype) /* in: precise type */ -{ - if (dtype_is_string_type(mtype) == TRUE - && dtype_is_binary_string_type(mtype, prtype) == FALSE) { - - return(TRUE); - } - - return(FALSE); -} - -/************************************************************************* Sets a data type structure. */ UNIV_INLINE void @@ -143,23 +79,7 @@ dtype_get_charset_coll( /*===================*/ ulint prtype) /* in: precise data type */ { - return((prtype >> 16) & 0xFF); -} - -/************************************************************************* -Forms a precise type from the < 4.1.2 format precise type plus the -charset-collation code. */ -ulint -dtype_form_prtype( -/*==============*/ - ulint old_prtype, /* in: the MySQL type code and the flags - DATA_NONLATIN1 etc. */ - ulint charset_coll) /* in: MySQL charset-collation code */ -{ - ut_a(old_prtype < 256 * 256); - ut_a(charset_coll < 256); - - return(old_prtype + (charset_coll << 16)); + return((prtype >> 16) & 0xFFUL); } /************************************************************************* @@ -237,9 +157,10 @@ dtype_new_store_for_order_and_null_size( buf[0] = buf[0] | 128; } - if (type->prtype & DATA_NONLATIN1) { - buf[0] = buf[0] | 64; - } + /* In versions < 4.1.2 we had: if (type->prtype & DATA_NONLATIN1) { + buf[0] = buf[0] | 64; + } + */ buf[1] = (byte)(type->prtype & 0xFFUL); @@ -271,10 +192,6 @@ dtype_read_for_order_and_null_size( type->prtype = type->prtype | DATA_BINARY_TYPE; } - if (buf[0] & 64) { - type->prtype = type->prtype | DATA_NONLATIN1; - } - type->len = mach_read_from_2(buf + 2); type->prtype = dtype_form_prtype(type->prtype, @@ -303,10 +220,6 @@ dtype_new_read_for_order_and_null_size( type->prtype = type->prtype | DATA_BINARY_TYPE; } - if (buf[0] & 64) { - type->prtype = type->prtype | DATA_NONLATIN1; - } - type->len = mach_read_from_2(buf + 2); mach_read_from_2(buf + 4); diff --git a/innobase/rem/rem0cmp.c b/innobase/rem/rem0cmp.c index 16d5c33cb35..fb8732f35be 100644 --- a/innobase/rem/rem0cmp.c +++ b/innobase/rem/rem0cmp.c @@ -331,7 +331,9 @@ cmp_data_data_slow( if (cur_type->mtype >= DATA_FLOAT || (cur_type->mtype == DATA_BLOB - && (cur_type->prtype & DATA_NONLATIN1))) { + && 0 == (cur_type->prtype & DATA_BINARY_TYPE) + && dtype_get_charset_coll(cur_type->prtype) != + data_mysql_latin1_swedish_charset_coll)) { return(cmp_whole_field(cur_type, data1, len1, data2, len2)); } @@ -532,8 +534,10 @@ cmp_dtuple_rec_with_match( } if (cur_type->mtype >= DATA_FLOAT - || (cur_type->mtype == DATA_BLOB - && (cur_type->prtype & DATA_NONLATIN1))) { + || (cur_type->mtype == DATA_BLOB + && 0 == (cur_type->prtype & DATA_BINARY_TYPE) + && dtype_get_charset_coll(cur_type->prtype) != + data_mysql_latin1_swedish_charset_coll)) { ret = cmp_whole_field(cur_type, dfield_get_data(dtuple_field), dtuple_f_len, @@ -854,8 +858,10 @@ cmp_rec_rec_with_match( } if (cur_type->mtype >= DATA_FLOAT - || (cur_type->mtype == DATA_BLOB - && (cur_type->prtype & DATA_NONLATIN1))) { + || (cur_type->mtype == DATA_BLOB + && 0 == (cur_type->prtype & DATA_BINARY_TYPE) + && dtype_get_charset_coll(cur_type->prtype) != + data_mysql_latin1_swedish_charset_coll)) { ret = cmp_whole_field(cur_type, rec1_b_ptr, rec1_f_len, diff --git a/libmysql/client_settings.h b/libmysql/client_settings.h index 4558f0f2abe..5ce0e021782 100644 --- a/libmysql/client_settings.h +++ b/libmysql/client_settings.h @@ -22,7 +22,7 @@ extern my_string mysql_unix_port; CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION) sig_handler pipe_sig_handler(int sig __attribute__((unused))); -my_bool stmt_close(MYSQL_STMT *stmt, my_bool skip_list); +my_bool stmt_close(MYSQL_STMT *stmt, my_bool skip_list, my_bool skip_free); void read_user_name(char *name); my_bool send_file_to_server(MYSQL *mysql, const char *filename); diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index 548375a7de7..e88e34f8925 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -89,7 +89,7 @@ static void append_wild(char *to,char *end,const char *wild); sig_handler pipe_sig_handler(int sig); static ulong mysql_sub_escape_string(CHARSET_INFO *charset_info, char *to, const char *from, ulong length); -my_bool stmt_close(MYSQL_STMT *stmt, my_bool skip_list); +my_bool stmt_close(MYSQL_STMT *stmt, my_bool skip_list, my_bool skip_free); static my_bool mysql_client_init= 0; static my_bool org_my_init_done= 0; @@ -1667,14 +1667,14 @@ mysql_prepare(MYSQL *mysql, const char *query, ulong length) } if (simple_command(mysql, COM_PREPARE, query, length, 1)) { - stmt_close(stmt, 1); + stmt_close(stmt, 1, 0); DBUG_RETURN(0); } init_alloc_root(&stmt->mem_root,8192,0); if ((*mysql->methods->read_prepare_result)(mysql, stmt)) { - stmt_close(stmt, 1); + stmt_close(stmt, 1, 0); DBUG_RETURN(0); } @@ -3313,7 +3313,7 @@ my_bool STDCALL mysql_stmt_free_result(MYSQL_STMT *stmt) } -my_bool stmt_close(MYSQL_STMT *stmt, my_bool skip_list) +my_bool stmt_close(MYSQL_STMT *stmt, my_bool skip_list, my_bool skip_free) { MYSQL *mysql; DBUG_ENTER("mysql_stmt_close"); @@ -3322,7 +3322,8 @@ my_bool stmt_close(MYSQL_STMT *stmt, my_bool skip_list) if (!(mysql= stmt->mysql)) { - my_free((gptr) stmt, MYF(MY_WME)); + if (!skip_free) + my_free((gptr) stmt, MYF(MY_WME)); DBUG_RETURN(0); } mysql_stmt_free_result(stmt); @@ -3330,7 +3331,7 @@ my_bool stmt_close(MYSQL_STMT *stmt, my_bool skip_list) { char buff[4]; int4store(buff, stmt->stmt_id); - if (simple_command(mysql, COM_CLOSE_STMT, buff, 4, 1)) + if (skip_free || simple_command(mysql, COM_CLOSE_STMT, buff, 4, 1)) { set_stmt_errmsg(stmt, mysql->net.last_error, mysql->net.last_errno, mysql->net.sqlstate); @@ -3351,7 +3352,7 @@ my_bool stmt_close(MYSQL_STMT *stmt, my_bool skip_list) my_bool STDCALL mysql_stmt_close(MYSQL_STMT *stmt) { - return stmt_close(stmt, 0); + return stmt_close(stmt, 0, 0); } /* diff --git a/myisam/mi_check.c b/myisam/mi_check.c index 4f6aa0b46d4..4d29838ecd0 100644 --- a/myisam/mi_check.c +++ b/myisam/mi_check.c @@ -409,7 +409,7 @@ int chk_key(MI_CHECK *param, register MI_INFO *info) if (chk_index(param,info,keyinfo,share->state.key_root[key],info->buff, &keys, param->key_crc+key,1)) DBUG_RETURN(-1); - if(!(keyinfo->flag & HA_FULLTEXT)) + if(!(keyinfo->flag & (HA_FULLTEXT | HA_SPATIAL))) { if (keys != info->state->records) { @@ -558,6 +558,10 @@ static int chk_index(MI_CHECK *param, MI_INFO *info, MI_KEYDEF *keyinfo, DBUG_ENTER("chk_index"); DBUG_DUMP("buff",(byte*) buff,mi_getint(buff)); + /* TODO: implement appropriate check for RTree keys */ + if (keyinfo->flag & HA_SPATIAL) + DBUG_RETURN(0); + if (!(temp_buff=(uchar*) my_alloca((uint) keyinfo->block_length))) { mi_check_print_error(param,"Not enough memory for keyblock"); @@ -1073,7 +1077,7 @@ int chk_data_link(MI_CHECK *param, MI_INFO *info,int extend) for (key=0 ; key < info->s->base.keys; key++) { if (key_checksum[key] != param->key_crc[key] && - !(info->s->keyinfo[key].flag & HA_FULLTEXT)) + !(info->s->keyinfo[key].flag & (HA_FULLTEXT | HA_SPATIAL))) { mi_check_print_error(param,"Checksum for key: %2d doesn't match checksum for records", key+1); diff --git a/mysql-test/r/ctype_utf8.result b/mysql-test/r/ctype_utf8.result index ad1f7785527..ddb5355f9ec 100644 --- a/mysql-test/r/ctype_utf8.result +++ b/mysql-test/r/ctype_utf8.result @@ -167,5 +167,5 @@ select hex(s1) from t1; hex(s1) 41 drop table t1; -create table t1 (a char(160) character set utf8, primary key(a)); -ERROR 42000: Specified key was too long; max key length is 255 bytes +create table t1 (a text character set utf8, primary key(a(360))); +ERROR 42000: Specified key was too long; max key length is 1000 bytes diff --git a/mysql-test/r/gis-rtree.result b/mysql-test/r/gis-rtree.result index 2a47d0c048d..ab5338d383b 100644 --- a/mysql-test/r/gis-rtree.result +++ b/mysql-test/r/gis-rtree.result @@ -710,3 +710,43 @@ SELECT count(*) FROM t2; count(*) 0 DROP TABLE t2; +drop table if exists t1; +Warnings: +Note 1051 Unknown table 't1' +CREATE TABLE t1 (a geometry NOT NULL, SPATIAL (a)); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +check table t1; +Table Op Msg_type Msg_text +test.t1 check status OK +analyze table t1; +Table Op Msg_type Msg_text +test.t1 analyze status OK +drop table t1; diff --git a/mysql-test/r/key.result b/mysql-test/r/key.result index dca7b580378..115f15bacb6 100644 --- a/mysql-test/r/key.result +++ b/mysql-test/r/key.result @@ -185,3 +185,24 @@ NULL 2 a 1 a 2 drop table t1; +create table t1 (i int, a char(200), b text, unique (a), unique (b(300))) charset utf8; +insert t1 values (1, repeat('a',210), repeat('b', 310)); +Warnings: +Warning 1265 Data truncated for column 'a' at row 1 +insert t1 values (2, repeat(0xD0B1,215), repeat(0xD0B1, 310)); +Warnings: +Warning 1265 Data truncated for column 'a' at row 1 +select i, length(a), length(b), char_length(a), char_length(b) from t1; +i length(a) length(b) char_length(a) char_length(b) +1 200 310 200 310 +2 400 620 200 310 +select i from t1 where a=repeat(_utf8 'a',200); +i +1 +select i from t1 where a=repeat(_utf8 0xD0B1,200); +i +2 +select i from t1 where b=repeat(_utf8 'b',310); +i +1 +drop table t1; diff --git a/mysql-test/r/myisam.result b/mysql-test/r/myisam.result index 1f3e12a33aa..1433c50f25c 100644 --- a/mysql-test/r/myisam.result +++ b/mysql-test/r/myisam.result @@ -322,11 +322,11 @@ CHECK TABLE t1; Table Op Msg_type Msg_text test.t1 check status OK drop table t1; -CREATE TABLE t1 (a varchar(255), b varchar(255), c varchar(255), KEY t1 (a, b, c)); -ERROR 42000: Specified key was too long; max key length is 500 bytes -CREATE TABLE t1 (a varchar(255), b varchar(255), c varchar(255)); -ALTER TABLE t1 ADD INDEX t1 (a, b, c); -ERROR 42000: Specified key was too long; max key length is 500 bytes +CREATE TABLE t1 (a varchar(255), b varchar(255), c varchar(255), d varchar(255), e varchar(255), KEY t1 (a, b, c, d, e)); +ERROR 42000: Specified key was too long; max key length is 1000 bytes +CREATE TABLE t1 (a varchar(255), b varchar(255), c varchar(255), d varchar(255), e varchar(255)); +ALTER TABLE t1 ADD INDEX t1 (a, b, c, d, e); +ERROR 42000: Specified key was too long; max key length is 1000 bytes DROP TABLE t1; CREATE TABLE t1 (a int not null, b int, c int, key(b), key(c), key(a,b), key(c,a)); INSERT into t1 values (0, null, 0), (0, null, 1), (0, null, 2), (0, null,3), (1,1,4); diff --git a/mysql-test/r/type_blob.result b/mysql-test/r/type_blob.result index a895325d1fc..d44face6512 100644 --- a/mysql-test/r/type_blob.result +++ b/mysql-test/r/type_blob.result @@ -346,16 +346,16 @@ HELLO MY 1 a 1 hello 1 drop table t1; -create table t1 (a text, unique (a(300))); -ERROR 42000: Specified key was too long; max key length is 255 bytes -create table t1 (a text, key (a(300))); +create table t1 (a text, unique (a(2100))); +ERROR 42000: Specified key was too long; max key length is 1000 bytes +create table t1 (a text, key (a(2100))); Warnings: -Warning 1071 Specified key was too long; max key length is 255 bytes +Warning 1071 Specified key was too long; max key length is 1000 bytes show create table t1; Table Create Table t1 CREATE TABLE `t1` ( `a` text, - KEY `a` (`a`(255)) + KEY `a` (`a`(1000)) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1; CREATE TABLE t1 ( diff --git a/mysql-test/t/ctype_utf8.test b/mysql-test/t/ctype_utf8.test index 6361f49fe55..797af89c6ad 100644 --- a/mysql-test/t/ctype_utf8.test +++ b/mysql-test/t/ctype_utf8.test @@ -104,4 +104,4 @@ drop table t1; # UTF8 breaks primary keys for cols > 85 characters # --error 1071 -create table t1 (a char(160) character set utf8, primary key(a)); +create table t1 (a text character set utf8, primary key(a(360))); diff --git a/mysql-test/t/gis-rtree.test b/mysql-test/t/gis-rtree.test index 08ba8329b48..629a07a4913 100644 --- a/mysql-test/t/gis-rtree.test +++ b/mysql-test/t/gis-rtree.test @@ -67,3 +67,39 @@ while ($1) } DROP TABLE t2; + +drop table if exists t1; +CREATE TABLE t1 (a geometry NOT NULL, SPATIAL (a)); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); +check table t1; +analyze table t1; +drop table t1; + diff --git a/mysql-test/t/key.test b/mysql-test/t/key.test index 109cbb91e2f..cdaf6062771 100644 --- a/mysql-test/t/key.test +++ b/mysql-test/t/key.test @@ -191,3 +191,16 @@ SELECT * FROM t1; INSERT INTO t1 (c) VALUES ('a'),('a'); SELECT * FROM t1; drop table t1; + +# +# longer keys +# +create table t1 (i int, a char(200), b text, unique (a), unique (b(300))) charset utf8; +insert t1 values (1, repeat('a',210), repeat('b', 310)); +insert t1 values (2, repeat(0xD0B1,215), repeat(0xD0B1, 310)); +select i, length(a), length(b), char_length(a), char_length(b) from t1; +select i from t1 where a=repeat(_utf8 'a',200); +select i from t1 where a=repeat(_utf8 0xD0B1,200); +select i from t1 where b=repeat(_utf8 'b',310); +drop table t1; + diff --git a/mysql-test/t/myisam.test b/mysql-test/t/myisam.test index c407cba4800..65bc4f1fff6 100644 --- a/mysql-test/t/myisam.test +++ b/mysql-test/t/myisam.test @@ -337,10 +337,10 @@ drop table t1; # --error 1071 -CREATE TABLE t1 (a varchar(255), b varchar(255), c varchar(255), KEY t1 (a, b, c)); -CREATE TABLE t1 (a varchar(255), b varchar(255), c varchar(255)); +CREATE TABLE t1 (a varchar(255), b varchar(255), c varchar(255), d varchar(255), e varchar(255), KEY t1 (a, b, c, d, e)); +CREATE TABLE t1 (a varchar(255), b varchar(255), c varchar(255), d varchar(255), e varchar(255)); --error 1071 -ALTER TABLE t1 ADD INDEX t1 (a, b, c); +ALTER TABLE t1 ADD INDEX t1 (a, b, c, d, e); DROP TABLE t1; # diff --git a/mysql-test/t/type_blob.test b/mysql-test/t/type_blob.test index 97c38057e72..cae64b9dd27 100644 --- a/mysql-test/t/type_blob.test +++ b/mysql-test/t/type_blob.test @@ -122,8 +122,8 @@ select d,count(*) from t1 group by d; drop table t1; -- error 1071 -create table t1 (a text, unique (a(300))); # should give an error -create table t1 (a text, key (a(300))); # key is auto-truncated +create table t1 (a text, unique (a(2100))); # should give an error +create table t1 (a text, key (a(2100))); # key is auto-truncated show create table t1; drop table t1; diff --git a/mysys/my_init.c b/mysys/my_init.c index a7899c20442..fc178b0308b 100644 --- a/mysys/my_init.c +++ b/mysys/my_init.c @@ -19,10 +19,6 @@ #include "mysys_err.h" #include <m_string.h> #include <m_ctype.h> -#ifdef HAVE_GETRUSAGE -#include <sys/resource.h> -/* extern int getrusage(int, struct rusage *); */ -#endif #include <signal.h> #ifdef VMS #include <my_static.c> diff --git a/mysys/mysys_priv.h b/mysys/mysys_priv.h index f79431a0b0b..6abadd48aeb 100644 --- a/mysys/mysys_priv.h +++ b/mysys/mysys_priv.h @@ -21,6 +21,10 @@ #include "system_wrappers.h" #endif +#ifdef HAVE_GETRUSAGE +#include <sys/resource.h> +#endif + #ifdef THREAD #include <my_pthread.h> extern pthread_mutex_t THR_LOCK_malloc, THR_LOCK_open, THR_LOCK_keycache; @@ -29,3 +33,4 @@ extern pthread_mutex_t THR_LOCK_charset; #else #include <my_no_pthread.h> #endif + diff --git a/sql-common/client.c b/sql-common/client.c index fc5004c4a82..a22ac22f08a 100644 --- a/sql-common/client.c +++ b/sql-common/client.c @@ -2199,7 +2199,7 @@ void STDCALL mysql_close(MYSQL *mysql) for (element= mysql->stmts; element; element= next_element) { next_element= element->next; - stmt_close((MYSQL_STMT *)element->data, 0); + stmt_close((MYSQL_STMT *)element->data, 0, 1); } mysql->stmts= 0; } diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index 1fbc468866c..3d226e42d82 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -755,11 +755,6 @@ innobase_init(void) srv_set_thread_priorities = TRUE; srv_query_thread_priority = QUERY_PRIOR; } - - /* Store the default charset-collation number of this MySQL - installation */ - - data_mysql_default_charset_coll = (ulint)default_charset_info->number; /* Set InnoDB initialization parameters according to the values read from MySQL .cnf file */ @@ -875,12 +870,20 @@ innobase_init(void) srv_print_verbose_log = mysql_embedded ? 0 : 1; + /* Store the default charset-collation number of this MySQL + installation */ + + data_mysql_default_charset_coll = (ulint)default_charset_info->number; + + data_mysql_latin1_swedish_charset_coll = + (ulint)my_charset_latin1.number; + /* Store the latin1_swedish_ci character ordering table to InnoDB. For non-latin1_swedish_ci charsets we use the MySQL comparison functions, and consequently we do not need to know the ordering internally in InnoDB. */ - ut_a(0 == ut_strcmp((char*)my_charset_latin1.name, + ut_a(0 == strcmp((char*)my_charset_latin1.name, (char*)"latin1_swedish_ci")); memcpy(srv_latin1_ordering, my_charset_latin1.sort_order, 256); @@ -3260,7 +3263,6 @@ create_table_def( ulint nulls_allowed; ulint unsigned_type; ulint binary_type; - ulint nonlatin1_type; ulint charset_no; ulint i; @@ -3290,17 +3292,8 @@ create_table_def( unsigned_type = 0; } - if (col_type == DATA_BLOB - && strcmp(field->charset()->name, - "latin1_swedish_ci") != 0) { - nonlatin1_type = DATA_NONLATIN1; - } else { - nonlatin1_type = 0; - } - if (field->binary()) { binary_type = DATA_BINARY_TYPE; - nonlatin1_type = 0; } else { binary_type = 0; } @@ -3319,7 +3312,7 @@ create_table_def( col_type, dtype_form_prtype( (ulint)field->type() | nulls_allowed | unsigned_type - | nonlatin1_type | binary_type, + | binary_type, + charset_no), field->pack_length(), 0); } diff --git a/sql/ha_myisam.h b/sql/ha_myisam.h index 06663516011..ddffcfecc29 100644 --- a/sql/ha_myisam.h +++ b/sql/ha_myisam.h @@ -65,6 +65,7 @@ class ha_myisam: public handler uint max_keys() const { return MI_MAX_KEY; } uint max_key_parts() const { return MAX_REF_PARTS; } uint max_key_length() const { return MI_MAX_KEY_LENGTH; } + uint max_key_part_length() { return MI_MAX_KEY_LENGTH; } uint checksum() const; int open(const char *name, int mode, uint test_if_locked); diff --git a/sql/hostname.cc b/sql/hostname.cc index c9cb2a43963..c74d230bbcb 100644 --- a/sql/hostname.cc +++ b/sql/hostname.cc @@ -27,9 +27,6 @@ extern "C" { // Because of SCO 3.2V4.2 #endif #if !defined( __WIN__) && !defined(OS2) -#if !defined(__NETWARE__) -#include <sys/resource.h> -#endif /* __NETWARE__ */ #ifdef HAVE_SYS_UN_H #include <sys/un.h> #endif diff --git a/sql/item.cc b/sql/item.cc index eb2550fdbcc..5aaeffff5d2 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -969,8 +969,10 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) void Item_field::cleanup() { + DBUG_ENTER("Item_field::cleanup"); Item_ident::cleanup(); field= result_field= 0; + DBUG_VOID_RETURN; } void Item::init_make_field(Send_field *tmp_field, @@ -1613,9 +1615,11 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference) void Item_ref::cleanup() { + DBUG_ENTER("Item_ref::cleanup"); Item_ident::cleanup(); if (hook_ptr) *hook_ptr= orig_item; + DBUG_VOID_RETURN; } diff --git a/sql/item.h b/sql/item.h index 4d081905e0d..b06bc85d573 100644 --- a/sql/item.h +++ b/sql/item.h @@ -128,7 +128,13 @@ public: virtual ~Item() { name=0; } /*lint -e1509 */ void set_name(const char *str,uint length, CHARSET_INFO *cs); void init_make_field(Send_field *tmp_field,enum enum_field_types type); - virtual void cleanup() { fixed=0; } + virtual void cleanup() + { + DBUG_ENTER("Item::cleanup"); + DBUG_PRINT("info", ("Type: %d", (int)type())); + fixed=0; + DBUG_VOID_RETURN; + } virtual void make_field(Send_field *field); virtual bool fix_fields(THD *, struct st_table_list *, Item **); virtual int save_in_field(Field *field, bool no_conversions); @@ -413,6 +419,7 @@ public: int save_in_field(Field *field, bool no_conversions); bool basic_const_item() const { return 1; } Item *new_item() { return new Item_int(name,value,max_length); } + void cleanup() { fixed= 1; } // to prevent drop fixed flag void print(String *str); }; @@ -900,6 +907,8 @@ public: enum Type type() const { return CACHE_ITEM; } static Item_cache* get_cache(Item_result type); table_map used_tables() const { return used_table_map; } + virtual void keep_array() {} + void cleanup() { fixed= 1; } // to prevent drop fixed flag void print(String *str); }; @@ -952,8 +961,10 @@ class Item_cache_row: public Item_cache { Item_cache **values; uint item_count; + bool save_array; public: - Item_cache_row(): Item_cache(), values(0), item_count(2) {} + Item_cache_row() + :Item_cache(), values(0), item_count(2), save_array(0) {} /* 'allocate' used only in row transformer, to preallocate space for row @@ -994,10 +1005,16 @@ public: bool check_cols(uint c); bool null_inside(); void bring_value(); + void keep_array() { save_array= 1; } void cleanup() { + DBUG_ENTER("Item_cache_row::cleanup"); Item_cache::cleanup(); - values= 0; + if (save_array) + bzero(values, item_count*sizeof(Item**)); + else + values= 0; + DBUG_VOID_RETURN; } }; @@ -1023,8 +1040,10 @@ public: Field *example() { return field_example; } void cleanup() { + DBUG_ENTER("Item_type_holder::cleanup"); Item::cleanup(); item_type= orig_type; + DBUG_VOID_RETURN; } }; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 1bba934cf8f..f0bc73e9501 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -500,7 +500,6 @@ bool Item_in_optimizer::fix_left(THD *thd, } - bool Item_in_optimizer::fix_fields(THD *thd, struct st_table_list *tables, Item ** ref) { @@ -526,6 +525,7 @@ bool Item_in_optimizer::fix_fields(THD *thd, struct st_table_list *tables, return 0; } + longlong Item_in_optimizer::val_int() { cache->store(args[0]); @@ -539,18 +539,38 @@ longlong Item_in_optimizer::val_int() return tmp; } + +void Item_in_optimizer::keep_top_level_cache() +{ + cache->keep_array(); + save_cache= 1; +} + + +void Item_in_optimizer::cleanup() +{ + DBUG_ENTER("Item_in_optimizer::cleanup"); + Item_bool_func::cleanup(); + if (!save_cache) + cache= 0; + DBUG_VOID_RETURN; +} + + bool Item_in_optimizer::is_null() { cache->store(args[0]); return (null_value= (cache->null_value || args[1]->is_null())); } + longlong Item_func_eq::val_int() { int value= cmp.compare(); return value == 0 ? 1 : 0; } + /* Same as Item_func_eq, but NULL = NULL */ void Item_func_equal::fix_length_and_dec() diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 3c70a50502a..9d39ddf4e76 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -93,9 +93,10 @@ class Item_in_optimizer: public Item_bool_func { protected: Item_cache *cache; + bool save_cache; public: Item_in_optimizer(Item *a, Item_in_subselect *b): - Item_bool_func(a, (Item *)b), cache(0) {} + Item_bool_func(a, (Item *)b), cache(0), save_cache(0) {} bool fix_fields(THD *, struct st_table_list *, Item **); bool fix_left(THD *thd, struct st_table_list *tables, Item **ref); bool is_null(); @@ -107,8 +108,10 @@ public: Item_in_optimizer return NULL, else it evaluate Item_in_subselect. */ longlong val_int(); + void cleanup(); const char *func_name() const { return "<in_optimizer>"; } Item_cache **get_cache() { return &cache; } + void keep_top_level_cache(); }; class Comp_creator @@ -209,9 +212,11 @@ public: } void cleanup() { + DBUG_ENTER("Item_bool_rowready_func2::cleanup"); Item_bool_func2::cleanup(); tmp_arg[0]= orig_a; tmp_arg[1]= orig_b; + DBUG_VOID_RETURN; } }; @@ -720,10 +725,12 @@ class Item_func_in :public Item_int_func void fix_length_and_dec(); void cleanup() { + DBUG_ENTER("Item_func_in::cleanup"); delete array; delete in_item; array= 0; in_item= 0; + DBUG_VOID_RETURN; } optimize_type select_optimize() const { return array ? OPTIMIZE_KEY : OPTIMIZE_NONE; } diff --git a/sql/item_func.h b/sql/item_func.h index 30f817d133b..3890e7c6de5 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -999,6 +999,7 @@ public: join_key(0), ft_handler(0), table(0), master(0), concat(0) { } void cleanup() { + DBUG_ENTER("Item_func_match"); if (!master && ft_handler) { ft_handler->please->close_search(ft_handler); @@ -1008,7 +1009,11 @@ public: table->fulltext_searched=0; } if (concat) + { delete concat; + concat= 0; + } + DBUG_VOID_RETURN; } enum Functype functype() const { return FT_FUNC; } const char *func_name() const { return "match"; } diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 5b3cc326679..bfe41726f72 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -35,9 +35,9 @@ inline Item * and_items(Item* cond, Item *item) } Item_subselect::Item_subselect(): - Item_result_field(), value_assigned(0), substitution(0), - engine(0), used_tables_cache(0), have_to_be_excluded(0), - const_item_cache(1), engine_changed(0) + Item_result_field(), value_assigned(0), thd(0), substitution(0), + engine(0), old_engine(0), used_tables_cache(0), have_to_be_excluded(0), + const_item_cache(1), engine_changed(0), changed(0) { reset(); /* @@ -54,10 +54,10 @@ void Item_subselect::init(st_select_lex *select_lex, DBUG_ENTER("Item_subselect::init"); DBUG_PRINT("subs", ("select_lex 0x%xl", (ulong) select_lex)); + unit= select_lex->master_unit(); if (select_lex->next_select()) - engine= new subselect_union_engine(select_lex->master_unit(), result, - this); + engine= new subselect_union_engine(unit, result, this); else engine= new subselect_single_select_engine(select_lex, result, this); DBUG_VOID_RETURN; @@ -65,8 +65,26 @@ void Item_subselect::init(st_select_lex *select_lex, void Item_subselect::cleanup() { + DBUG_ENTER("Item_subselect::cleanup"); Item_result_field::cleanup(); - engine->cleanup(); + if (old_engine) + { + engine->cleanup(); + engine= old_engine; + old_engine= 0; + } + engine->cleanup(); + reset(); + value_assigned= 0; + DBUG_VOID_RETURN; +} + +void Item_singlerow_subselect::cleanup() +{ + DBUG_ENTER("Item_singlerow_subselect::cleanup"); + value= 0; row= 0; + Item_subselect::cleanup(); + DBUG_VOID_RETURN; } Item_subselect::~Item_subselect() @@ -85,13 +103,22 @@ Item_subselect::select_transformer(JOIN *join) bool Item_subselect::fix_fields(THD *thd_param, TABLE_LIST *tables, Item **ref) { engine->set_thd((thd= thd_param)); + stmt= thd->current_statement; char const *save_where= thd->where; int res= engine->prepare(); + + // all transformetion is done (used by prepared statements) + changed= 1; + if (!res) { if (substitution) { + // did we changed top item of WHERE condition + if (unit->outer_select()->where == (*ref)) + unit->outer_select()->where= substitution; // correct WHERE for PS + (*ref)= substitution; substitution->name= name; if (have_to_be_excluded) @@ -240,8 +267,12 @@ void Item_singlerow_subselect::reset() Item_subselect::trans_res Item_singlerow_subselect::select_transformer(JOIN *join) { + if (changed) + return RES_OK; + SELECT_LEX *select_lex= join->select_lex; - + Statement backup; + if (!select_lex->master_unit()->first_select()->next_select() && !select_lex->table_list.elements && select_lex->item_list.elements == 1 && @@ -275,20 +306,30 @@ Item_singlerow_subselect::select_transformer(JOIN *join) if (join->conds || join->having) { Item *cond; + if (stmt) + thd->set_n_backup_item_arena(stmt, &backup); + if (!join->having) cond= join->conds; else if (!join->conds) cond= join->having; else if (!(cond= new Item_cond_and(join->conds, join->having))) - return RES_ERROR; + goto err; if (!(substitution= new Item_func_if(cond, substitution, new Item_null()))) - return RES_ERROR; + goto err; } + if (stmt) + thd->restore_backup_item_arena(stmt, &backup); return RES_REDUCE; } return RES_OK; + +err: + if (stmt) + thd->restore_backup_item_arena(stmt, &backup); + return RES_ERROR; } void Item_singlerow_subselect::store(uint i, Item *item) @@ -550,15 +591,22 @@ Item_in_subselect::single_value_transformer(JOIN *join, { DBUG_ENTER("Item_in_subselect::single_value_transformer"); + if (changed) + { + DBUG_RETURN(RES_OK); + } + SELECT_LEX *select_lex= join->select_lex; + Statement backup; - THD *thd_tmp= join->thd; - thd_tmp->where= "scalar IN/ALL/ANY subquery"; + thd->where= "scalar IN/ALL/ANY subquery"; + if (stmt) + thd->set_n_backup_item_arena(stmt, &backup); if (select_lex->item_list.elements > 1) { my_error(ER_OPERAND_COLUMNS, MYF(0), 1); - DBUG_RETURN(RES_ERROR); + goto err; } if ((abort_on_null || (upper_not && upper_not->top_level())) && @@ -567,7 +615,7 @@ Item_in_subselect::single_value_transformer(JOIN *join, if (substitution) { // It is second (third, ...) SELECT of UNION => All is done - DBUG_RETURN(RES_OK); + goto ok; } Item *subs; @@ -597,10 +645,9 @@ Item_in_subselect::single_value_transformer(JOIN *join, select_lex->item_list.empty(); select_lex->item_list.push_back(item); - if (item->fix_fields(thd_tmp, join->tables_list, &item)) - { - DBUG_RETURN(RES_ERROR); - } + if (item->fix_fields(thd, join->tables_list, &item)) + goto err; + subs= new Item_singlerow_subselect(select_lex); } else @@ -611,16 +658,16 @@ Item_in_subselect::single_value_transformer(JOIN *join, subs= new Item_maxmin_subselect(this, select_lex, func->l_op()); } // left expression belong to outer select - SELECT_LEX *current= thd_tmp->lex->current_select, *up; - thd_tmp->lex->current_select= up= current->return_after_parsing(); - if (left_expr->fix_fields(thd_tmp, up->get_table_list(), &left_expr)) + SELECT_LEX *current= thd->lex->current_select, *up; + thd->lex->current_select= up= current->return_after_parsing(); + if (left_expr->fix_fields(thd, up->get_table_list(), &left_expr)) { - thd_tmp->lex->current_select= current; - DBUG_RETURN(RES_ERROR); + thd->lex->current_select= current; + goto err; } - thd_tmp->lex->current_select= current; + thd->lex->current_select= current; substitution= func->create(left_expr, subs); - DBUG_RETURN(RES_OK); + goto ok; } if (!substitution) @@ -629,16 +676,16 @@ Item_in_subselect::single_value_transformer(JOIN *join, SELECT_LEX_UNIT *unit= select_lex->master_unit(); substitution= optimizer= new Item_in_optimizer(left_expr, this); - SELECT_LEX *current= thd_tmp->lex->current_select, *up; + SELECT_LEX *current= thd->lex->current_select, *up; - thd_tmp->lex->current_select= up= current->return_after_parsing(); + thd->lex->current_select= up= current->return_after_parsing(); //optimizer never use Item **ref => we can pass 0 as parameter - if (!optimizer || optimizer->fix_left(thd_tmp, up->get_table_list(), 0)) + if (!optimizer || optimizer->fix_left(thd, up->get_table_list(), 0)) { - thd_tmp->lex->current_select= current; - DBUG_RETURN(RES_ERROR); + thd->lex->current_select= current; + goto err; } - thd_tmp->lex->current_select= current; + thd->lex->current_select= current; /* As far as Item_ref_in_optimizer do not substitude itself on fix_fields @@ -665,12 +712,17 @@ Item_in_subselect::single_value_transformer(JOIN *join, select_lex->ref_pointer_array, (char *)"<ref>", this->full_name())); - join->having= and_items(join->having, item); + /* + AND and comparison functions can't be changed during fix_fields() + we can assign select_lex->having here, and pass 0 as last + argument (reference) to fix_fields() + */ + select_lex->having= join->having= and_items(join->having, item); select_lex->having_fix_field= 1; - if (join->having->fix_fields(thd_tmp, join->tables_list, &join->having)) + if (join->having->fix_fields(thd, join->tables_list, 0)) { select_lex->having_fix_field= 0; - DBUG_RETURN(RES_ERROR); + goto err; } select_lex->having_fix_field= 0; } @@ -687,39 +739,56 @@ Item_in_subselect::single_value_transformer(JOIN *join, if (!abort_on_null) { having= new Item_is_not_null_test(this, having); - join->having= (join->having ? - new Item_cond_and(having, join->having) : - having); + /* + Item_is_not_null_test can't be changed during fix_fields() + we can assign select_lex->having here, and pass 0 as last + argument (reference) to fix_fields() + */ + select_lex->having= + join->having= (join->having ? + new Item_cond_and(having, join->having) : + having); select_lex->having_fix_field= 1; - if (join->having->fix_fields(thd_tmp, join->tables_list, - &join->having)) + if (join->having->fix_fields(thd, join->tables_list, 0)) { select_lex->having_fix_field= 0; - DBUG_RETURN(RES_ERROR); + goto err; } select_lex->having_fix_field= 0; item= new Item_cond_or(item, new Item_func_isnull(isnull)); } item->name= (char *)in_additional_cond; - join->conds= and_items(join->conds, item); - if (join->conds->fix_fields(thd_tmp, join->tables_list, &join->conds)) - DBUG_RETURN(RES_ERROR); + /* + AND can't be changed during fix_fields() + we can assign select_lex->having here, and pass 0 as last + argument (reference) to fix_fields() + */ + select_lex->where= join->conds= and_items(join->conds, item); + if (join->conds->fix_fields(thd, join->tables_list, 0)) + goto err; } else { if (select_lex->master_unit()->first_select()->next_select()) { - join->having= func->create(expr, - new Item_null_helper(this, item, - (char *)"<no matter>", - (char *)"<result>")); + /* + comparison functions can't be changed during fix_fields() + we can assign select_lex->having here, and pass 0 as last + argument (reference) to fix_fields() + */ + select_lex->having= + join->having= + func->create(expr, + new Item_null_helper(this, item, + (char *)"<no matter>", + (char *)"<result>")); select_lex->having_fix_field= 1; - if (join->having->fix_fields(thd_tmp, join->tables_list, - &join->having)) + if (join->having->fix_fields(thd, join->tables_list, + 0)) { select_lex->having_fix_field= 0; - DBUG_RETURN(RES_ERROR); + goto err; } select_lex->having_fix_field= 0; } @@ -730,18 +799,29 @@ Item_in_subselect::single_value_transformer(JOIN *join, // fix_field of item will be done in time of substituting substitution= item; have_to_be_excluded= 1; - if (thd_tmp->lex->describe) + if (thd->lex->describe) { char warn_buff[MYSQL_ERRMSG_SIZE]; sprintf(warn_buff, ER(ER_SELECT_REDUCED), select_lex->select_number); - push_warning(thd_tmp, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_SELECT_REDUCED, warn_buff); } + if (stmt) + thd->restore_backup_item_arena(stmt, &backup); DBUG_RETURN(RES_REDUCE); } } } + +ok: + if (stmt) + thd->restore_backup_item_arena(stmt, &backup); DBUG_RETURN(RES_OK); + +err: + if (stmt) + thd->restore_backup_item_arena(stmt, &backup); + DBUG_RETURN(RES_ERROR); } @@ -750,15 +830,23 @@ Item_in_subselect::row_value_transformer(JOIN *join) { DBUG_ENTER("Item_in_subselect::row_value_transformer"); - THD *thd_tmp= join->thd; - thd_tmp->where= "row IN/ALL/ANY subquery"; + if (changed) + { + DBUG_RETURN(RES_OK); + } + Statement backup; + Item *item= 0; + + thd->where= "row IN/ALL/ANY subquery"; + if (stmt) + thd->set_n_backup_item_arena(stmt, &backup); SELECT_LEX *select_lex= join->select_lex; if (select_lex->item_list.elements != left_expr->cols()) { my_error(ER_OPERAND_COLUMNS, MYF(0), left_expr->cols()); - DBUG_RETURN(RES_ERROR); + goto err; } if (!substitution) @@ -767,62 +855,82 @@ Item_in_subselect::row_value_transformer(JOIN *join) SELECT_LEX_UNIT *unit= select_lex->master_unit(); substitution= optimizer= new Item_in_optimizer(left_expr, this); - SELECT_LEX *current= thd_tmp->lex->current_select, *up; - thd_tmp->lex->current_select= up= current->return_after_parsing(); + SELECT_LEX *current= thd->lex->current_select, *up; + thd->lex->current_select= up= current->return_after_parsing(); //optimizer never use Item **ref => we can pass 0 as parameter - if (!optimizer || optimizer->fix_left(thd_tmp, up->get_table_list(), 0)) + if (!optimizer || optimizer->fix_left(thd, up->get_table_list(), 0)) { - thd_tmp->lex->current_select= current; - DBUG_RETURN(RES_ERROR); + thd->lex->current_select= current; + goto err; } - thd_tmp->lex->current_select= current; + + // we will refer to apper level cache array => we have to save it in PS + optimizer->keep_top_level_cache(); + + thd->lex->current_select= current; unit->uncacheable|= UNCACHEABLE_DEPENDENT; } - uint n= left_expr->cols(); - select_lex->uncacheable|= UNCACHEABLE_DEPENDENT; - select_lex->setup_ref_array(thd_tmp, + select_lex->setup_ref_array(thd, select_lex->order_list.elements + select_lex->group_list.elements); - Item *item= 0; - List_iterator_fast<Item> li(select_lex->item_list); - for (uint i= 0; i < n; i++) { - Item *func= new Item_ref_null_helper(this, - select_lex->ref_pointer_array+i, - (char *) "<no matter>", - (char *) "<list ref>"); - func= - eq_creator.create(new Item_ref((*optimizer->get_cache())-> - addr(i), - NULL, - (char *)"<no matter>", + uint n= left_expr->cols(); + List_iterator_fast<Item> li(select_lex->item_list); + for (uint i= 0; i < n; i++) + { + Item *func= new Item_ref_null_helper(this, + select_lex->ref_pointer_array+i, + (char *) "<no matter>", + (char *) "<list ref>"); + func= + eq_creator.create(new Item_ref((*optimizer->get_cache())-> + addr(i), + NULL, + (char *)"<no matter>", (char *)in_left_expr_name), - func); - item= and_items(item, func); + func); + item= and_items(item, func); + } } - if (join->having || select_lex->with_sum_func || select_lex->group_list.first || !select_lex->table_list.elements) { - join->having= and_items(join->having, item); + /* + AND can't be changed during fix_fields() + we can assign select_lex->having here, and pass 0 as last + argument (reference) to fix_fields() + */ + select_lex->having= join->having= and_items(join->having, item); select_lex->having_fix_field= 1; - if (join->having->fix_fields(thd_tmp, join->tables_list, &join->having)) + if (join->having->fix_fields(thd, join->tables_list, 0)) { select_lex->having_fix_field= 0; - DBUG_RETURN(RES_ERROR); + goto err; } select_lex->having_fix_field= 0; } else { - join->conds= and_items(join->conds, item); - if (join->conds->fix_fields(thd_tmp, join->tables_list, &join->having)) - DBUG_RETURN(RES_ERROR); + /* + AND can't be changed during fix_fields() + we can assign select_lex->having here, and pass 0 as last + argument (reference) to fix_fields() + */ + select_lex->where= join->conds= and_items(join->conds, item); + if (join->conds->fix_fields(thd, join->tables_list, 0)) + goto err; } + if (stmt) + thd->restore_backup_item_arena(stmt, &backup); DBUG_RETURN(RES_OK); + +err: + if (stmt) + thd->restore_backup_item_arena(stmt, &backup); + DBUG_RETURN(RES_ERROR); } @@ -894,13 +1002,31 @@ subselect_single_select_engine(st_select_lex *select, this->select_lex= select_lex; } + void subselect_single_select_engine::cleanup() { - prepared= 0; - optimized= 0; - executed= 0; + DBUG_ENTER("subselect_single_select_engine::cleanup"); + prepared= optimized= executed= 0; + join= 0; + DBUG_VOID_RETURN; +} + + +void subselect_union_engine::cleanup() +{ + DBUG_ENTER("subselect_union_engine::cleanup"); + unit->reinit_exec_mechanism(); + DBUG_VOID_RETURN; +} + + +void subselect_uniquesubquery_engine::cleanup() +{ + DBUG_ENTER("subselect_uniquesubquery_engine::cleanup"); + DBUG_VOID_RETURN; } + subselect_union_engine::subselect_union_engine(st_select_lex_unit *u, select_subselect *result_arg, Item_subselect *item_arg) diff --git a/sql/item_subselect.h b/sql/item_subselect.h index dc3d07540da..d550cde64b7 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -26,6 +26,7 @@ class JOIN; class select_subselect; class subselect_engine; class Item_bool_func2; +class Statement; /* base class for subselects */ @@ -35,22 +36,30 @@ class Item_subselect :public Item_result_field protected: /* thread handler, will be assigned in fix_fields only */ THD *thd; + /* prepared statement, or 0 */ + Statement *stmt; /* substitution instead of subselect in case of optimization */ Item *substitution; + /* unit of subquery */ + st_select_lex_unit *unit; /* engine that perform execution of subselect (single select or union) */ subselect_engine *engine; + /* old engine if engine was changed */ + subselect_engine *old_engine; /* cache of used external tables */ table_map used_tables_cache; /* allowed number of columns (1 for single value subqueries) */ uint max_columns; /* work with 'substitution' */ bool have_to_be_excluded; - /* cache of constante state */ + /* cache of constant state */ bool const_item_cache; public: /* changed engine indicator */ bool engine_changed; + /* subquery is transformed */ + bool changed; enum trans_res {RES_OK, RES_REDUCE, RES_ERROR}; enum subs_type {UNKNOWN_SUBS, SINGLEROW_SUBS, @@ -94,6 +103,7 @@ public: void print(String *str); bool change_engine(subselect_engine *eng) { + old_engine= engine; engine= eng; engine_changed= 1; return eng == 0; @@ -116,6 +126,7 @@ public: Item_singlerow_subselect(st_select_lex *select_lex); Item_singlerow_subselect() :Item_subselect(), value(0), row (0) {} + void cleanup(); subs_type substype() { return SINGLEROW_SUBS; } void reset(); @@ -200,13 +211,6 @@ public: {} - void cleanup() - { - Item_exists_subselect::cleanup(); - abort_on_null= 0; - transformed= 0; - upper_not= 0; - } subs_type substype() { return IN_SUBS; } void reset() { @@ -269,7 +273,7 @@ public: maybe_null= 0; } virtual ~subselect_engine() {}; // to satisfy compiler - virtual void cleanup() {} + virtual void cleanup()= 0; // set_thd should be called before prepare() void set_thd(THD *thd_arg) { thd= thd_arg; } @@ -318,6 +322,7 @@ public: subselect_union_engine(st_select_lex_unit *u, select_subselect *result, Item_subselect *item); + void cleanup(); int prepare(); void fix_length_and_dec(Item_cache** row); int exec(); @@ -345,6 +350,7 @@ public: set_thd(thd_arg); } ~subselect_uniquesubquery_engine(); + void cleanup(); int prepare(); void fix_length_and_dec(Item_cache** row); int exec(); diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 10b50fa5b3b..879c5f99ebd 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -1084,6 +1084,7 @@ int dump_leaf(byte* key, uint32 count __attribute__((unused)), void Item_sum_count_distinct::cleanup() { + DBUG_ENTER("Item_sum_count_distinct::cleanup"); Item_sum_int::cleanup(); /* Free table and tree if they belong to this item (if item have not pointer @@ -1104,6 +1105,7 @@ void Item_sum_count_distinct::cleanup() use_tree= 0; } } + DBUG_VOID_RETURN; } @@ -1672,6 +1674,7 @@ Item_func_group_concat::Item_func_group_concat(bool is_distinct, void Item_func_group_concat::cleanup() { + DBUG_ENTER("Item_func_group_concat::cleanup"); /* Free table and tree if they belong to this item (if item have not pointer to original item from which was made copy => it own its objects ) @@ -1692,6 +1695,7 @@ void Item_func_group_concat::cleanup() delete_tree(tree); } } + DBUG_VOID_RETURN; } diff --git a/sql/item_sum.h b/sql/item_sum.h index 0848886d6d8..11c95100db5 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -60,8 +60,10 @@ public: Item_sum(THD *thd, Item_sum *item); void cleanup() { + DBUG_ENTER("Item_sum::cleanup"); Item_result_field::cleanup(); result_field=0; + DBUG_VOID_RETURN; } enum Type type() const { return SUM_FUNC_ITEM; } diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 43921a1438b..f5031f926af 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -350,6 +350,11 @@ inline THD *_current_thd(void) #include "sql_udf.h" #include "item.h" typedef Comp_creator* (*chooser_compare_func_creator)(bool invert); +/* sql_parse.cc */ +void free_items(Item *item); +void cleanup_items(Item *item); +class THD; +void close_thread_tables(THD *thd, bool locked=0, bool skip_derived=0); #include "sql_class.h" #include "opt_range.h" @@ -411,7 +416,6 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list); bool mysql_change_db(THD *thd,const char *name); void mysql_parse(THD *thd,char *inBuf,uint length); bool is_update_query(enum enum_sql_command command); -void free_items(Item *item); bool alloc_query(THD *thd, char *packet, ulong packet_length); void mysql_init_select(LEX *lex); void mysql_init_query(THD *thd); @@ -688,7 +692,6 @@ bool rm_temporary_table(enum db_type base, char *path); void free_io_cache(TABLE *entry); void intern_close_table(TABLE *entry); bool close_thread_table(THD *thd, TABLE **table_ptr); -void close_thread_tables(THD *thd, bool locked=0, bool skip_derived=0); void close_temporary_tables(THD *thd); TABLE_LIST * find_table_in_list(TABLE_LIST *table, const char *db_name, const char *table_name); @@ -766,9 +769,6 @@ uint check_word(TYPELIB *lib, const char *val, const char *end, bool is_keyword(const char *name, uint len); -/* sql_parse.cc */ -void free_items(Item *item); -void cleanup_items(Item *item); #define MY_DB_OPT_FILE "db.opt" bool load_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 08a1823edd2..37767c555e8 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -2080,6 +2080,14 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, { if (!wild_num) return 0; + Statement *stmt= thd->current_statement, backup; + + /* + If we are in preparing prepared statement phase then we have change + temporary mem_root to statement mem root to save changes of SELECT list + */ + if (stmt) + thd->set_n_backup_item_arena(stmt, &backup); reg2 Item *item; List_iterator<Item> it(fields); while ( wild_num && (item= it++)) @@ -2091,7 +2099,11 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, uint elem= fields.elements; if (insert_fields(thd,tables,((Item_field*) item)->db_name, ((Item_field*) item)->table_name, &it)) + { + if (stmt) + thd->restore_backup_item_arena(stmt, &backup); return (-1); + } if (sum_func_list) { /* @@ -2104,6 +2116,8 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, wild_num--; } } + if (stmt) + thd->restore_backup_item_arena(stmt, &backup); return 0; } diff --git a/sql/sql_class.cc b/sql/sql_class.cc index e7f867ccd61..9ed604033ab 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -83,7 +83,7 @@ extern "C" void free_user_var(user_var_entry *entry) ** Thread specific functions ****************************************************************************/ -THD::THD():user_time(0), is_fatal_error(0), +THD::THD():user_time(0), current_statement(0), is_fatal_error(0), last_insert_id_used(0), insert_id_used(0), rand_used(0), in_lock_tables(0), global_read_lock(0), bootstrap(0) @@ -1247,6 +1247,27 @@ void Statement::set_statement(Statement *stmt) } +void Statement::set_n_backup_item_arena(Statement *set, Statement *backup) +{ + backup->set_item_arena(this); + set_item_arena(set); +} + + +void Statement::restore_backup_item_arena(Statement *set, Statement *backup) +{ + set->set_item_arena(this); + set_item_arena(backup); + // reset backup mem_root to avoid its freeing + init_alloc_root(&backup->mem_root, 0, 0); +} + +void Statement::set_item_arena(Statement *set) +{ + mem_root= set->mem_root; + free_list= set->free_list; +} + Statement::~Statement() { free_root(&mem_root, MYF(0)); diff --git a/sql/sql_class.h b/sql/sql_class.h index b0899428f32..6816d141dac 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -427,7 +427,7 @@ class Statement public: /* FIXME: must be private */ LEX main_lex; -public: + /* Uniquely identifies each statement object in thread scope; change during statement lifetime. FIXME: must be const @@ -476,7 +476,7 @@ public: char *query; uint32 query_length; // current query length /* - List of items created in the parser for this query. Every item puts + List of items created in the parser for this query. Every item puts itself to the list on creation (see Item::Item() for details)) */ Item *free_list; @@ -503,6 +503,32 @@ public: void set_statement(Statement *stmt); /* return class type */ virtual Type type() const; + + inline gptr alloc(unsigned int size) { return alloc_root(&mem_root,size); } + inline gptr calloc(unsigned int size) + { + gptr ptr; + if ((ptr=alloc_root(&mem_root,size))) + bzero((char*) ptr,size); + return ptr; + } + inline char *strdup(const char *str) + { return strdup_root(&mem_root,str); } + inline char *strmake(const char *str, uint size) + { return strmake_root(&mem_root,str,size); } + inline char *memdup(const char *str, uint size) + { return memdup_root(&mem_root,str,size); } + inline char *memdup_w_gap(const char *str, uint size, uint gap) + { + gptr ptr; + if ((ptr=alloc_root(&mem_root,size+gap))) + memcpy(ptr,str,size); + return ptr; + } + + void set_n_backup_item_arena(Statement *set, Statement *backup); + void restore_backup_item_arena(Statement *set, Statement *backup); + void Statement::set_item_arena(Statement *set); }; @@ -690,6 +716,10 @@ public: Vio* active_vio; #endif /* + Current prepared Statement if there one, or 0 + */ + Statement *current_statement; + /* next_insert_id is set on SET INSERT_ID= #. This is used as the next generated auto_increment value in handler.cc */ @@ -850,34 +880,14 @@ public: return 0; #endif } - inline gptr alloc(unsigned int size) { return alloc_root(&mem_root,size); } - inline gptr calloc(unsigned int size) - { - gptr ptr; - if ((ptr=alloc_root(&mem_root,size))) - bzero((char*) ptr,size); - return ptr; - } - inline char *strdup(const char *str) - { return strdup_root(&mem_root,str); } - inline char *strmake(const char *str, uint size) - { return strmake_root(&mem_root,str,size); } - inline char *memdup(const char *str, uint size) - { return memdup_root(&mem_root,str,size); } - inline char *memdup_w_gap(const char *str, uint size, uint gap) - { - gptr ptr; - if ((ptr=alloc_root(&mem_root,size+gap))) - memcpy(ptr,str,size); - return ptr; - } - bool convert_string(LEX_STRING *to, CHARSET_INFO *to_cs, - const char *from, uint from_length, - CHARSET_INFO *from_cs); inline gptr trans_alloc(unsigned int size) { return alloc_root(&transaction.mem_root,size); } + + bool convert_string(LEX_STRING *to, CHARSET_INFO *to_cs, + const char *from, uint from_length, + CHARSET_INFO *from_cs); void add_changed_table(TABLE *table); void add_changed_table(const char *key, long key_length); CHANGED_TABLE_LIST * changed_table_dup(const char *key, long key_length); @@ -900,6 +910,33 @@ public: } inline CHARSET_INFO *charset() { return variables.character_set_client; } void update_charset(); + + inline void allocate_temporary_memory_pool_for_ps_preparing() + { + DBUG_ASSERT(current_statement!=0); + /* + We do not want to have in PS memory all that junk, + which will be created by preparation => substitute memory + from original thread pool. + + We know that PS memory pool is now copied to THD, we move it back + to allow some code use it. + */ + current_statement->set_item_arena(this); + init_sql_alloc(&mem_root, + variables.query_alloc_block_size, + variables.query_prealloc_size); + free_list= 0; + } + inline void free_temporary_memory_pool_for_ps_preparing() + { + DBUG_ASSERT(current_statement!=0); + cleanup_items(current_statement->free_list); + free_items(free_list); + close_thread_tables(this); // to close derived tables + free_root(&mem_root, MYF(0)); + set_item_arena(current_statement); + } }; /* Flags for the THD::system_thread (bitmap) variable */ diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index d93134a3a0c..ce7aa4d02db 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1500,11 +1500,17 @@ bool st_select_lex::setup_ref_array(THD *thd, uint order_group_num) { if (ref_pointer_array) return 0; + + /* + We have to create array in prepared statement memory if it is + prepared statement + */ + Statement *stmt= thd->current_statement ? thd->current_statement : thd; return (ref_pointer_array= - (Item **)thd->alloc(sizeof(Item*) * - (item_list.elements + - select_n_having_items + - order_group_num)* 5)) == 0; + (Item **)stmt->alloc(sizeof(Item*) * + (item_list.elements + + select_n_having_items + + order_group_num)* 5)) == 0; } @@ -1629,7 +1635,11 @@ void st_select_lex::print_limit(THD *thd, String *str) /* There are st_select_lex::add_table_to_list & - st_select_lex::set_lock_for_tables in sql_parse.cc + st_select_lex::set_lock_for_tables are in sql_parse.cc st_select_lex::print is in sql_select.h + + st_select_lex_unit::prepare, st_select_lex_unit::exec, + st_select_lex_unit::cleanup, st_select_lex_unit::reinit_exec_mechanism + are in sql_union.cc */ diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 3f56be18c4a..17cccd75697 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -355,6 +355,8 @@ public: int prepare(THD *thd, select_result *result, ulong additional_options); int exec(); int cleanup(); + inline void unclean() { cleaned= 0; } + void reinit_exec_mechanism(); bool check_updateable(char *db, char *table); void print(String *str); diff --git a/sql/sql_list.h b/sql/sql_list.h index c7faef88358..0bbc0f87944 100644 --- a/sql/sql_list.h +++ b/sql/sql_list.h @@ -286,7 +286,7 @@ protected: inline T** ref(void) { return (T**) 0; } public: - List_iterator_fast(List<T> &a) : base_list_iterator(a) {} + inline List_iterator_fast(List<T> &a) : base_list_iterator(a) {} inline T* operator++(int) { return (T*) base_list_iterator::next_fast(); } inline void rewind(void) { base_list_iterator::rewind(); } void sublist(List<T> &list_arg, uint el_arg) diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 3e51844e8cf..7c2913bc495 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -621,8 +621,18 @@ static bool mysql_test_insert_fields(Prepared_statement *stmt, (grant_option && check_grant(thd,privilege,table_list,0,0))) DBUG_RETURN(1); #endif + + /* + open temporary memory pool for temporary data allocated by derived + tables & preparation procedure + */ + thd->allocate_temporary_memory_pool_for_ps_preparing(); if (open_and_lock_tables(thd, table_list)) - DBUG_RETURN(1); + { + thd->free_temporary_memory_pool_for_ps_preparing(); + DBUG_RETURN(1); + } + table= table_list->table; if ((values= its++)) @@ -631,7 +641,11 @@ static bool mysql_test_insert_fields(Prepared_statement *stmt, ulong counter= 0; if (check_insert_fields(thd,table,fields,*values,1)) + { + thd->free_temporary_memory_pool_for_ps_preparing(); DBUG_RETURN(1); + } + thd->free_temporary_memory_pool_for_ps_preparing(); value_count= values->elements; its.rewind(); @@ -648,6 +662,10 @@ static bool mysql_test_insert_fields(Prepared_statement *stmt, } } } + else + { + thd->free_temporary_memory_pool_for_ps_preparing(); + } if (send_prep_stmt(stmt, 0)) DBUG_RETURN(1); DBUG_RETURN(0); @@ -677,12 +695,21 @@ static bool mysql_test_upd_fields(Prepared_statement *stmt, (grant_option && check_grant(thd,UPDATE_ACL,table_list,0,0))) DBUG_RETURN(1); #endif + + /* + open temporary memory pool for temporary data allocated by derived + tables & preparation procedure + */ + thd->allocate_temporary_memory_pool_for_ps_preparing(); + if (open_and_lock_tables(thd, table_list)) - DBUG_RETURN(1); + goto err; if (setup_tables(table_list) || - setup_fields(thd, 0, table_list, fields, 1, 0, 0) || - setup_conds(thd, table_list, &conds) || thd->net.report_error) - DBUG_RETURN(1); + setup_fields(thd, 0, table_list, fields, 1, 0, 0) || + setup_conds(thd, table_list, &conds) || thd->net.report_error) + goto err; + + thd->free_temporary_memory_pool_for_ps_preparing(); /* Currently return only column list info only, and we are not @@ -691,6 +718,9 @@ static bool mysql_test_upd_fields(Prepared_statement *stmt, if (send_prep_stmt(stmt, 0)) DBUG_RETURN(1); DBUG_RETURN(0); +err: + thd->free_temporary_memory_pool_for_ps_preparing(); + DBUG_RETURN(1); } /* @@ -735,41 +765,51 @@ static bool mysql_test_select_fields(Prepared_statement *stmt, if ((&lex->select_lex != lex->all_selects_list && lex->unit.create_total_list(thd, lex, &tables))) DBUG_RETURN(1); - + + /* + open temporary memory pool for temporary data allocated by derived + tables & preparation procedure + */ + thd->allocate_temporary_memory_pool_for_ps_preparing(); if (open_and_lock_tables(thd, tables)) - DBUG_RETURN(1); + goto err; if (lex->describe) { if (send_prep_stmt(stmt, 0)) - DBUG_RETURN(1); + goto err; } else { if (!result && !(result= new select_send())) { send_error(thd, ER_OUT_OF_RESOURCES); - DBUG_RETURN(1); + goto err; } - JOIN *join= new JOIN(thd, fields, select_options, result); thd->used_tables= 0; // Updated by setup_fields - if (join->prepare(&select_lex->ref_pointer_array, - (TABLE_LIST*)select_lex->get_table_list(), - wild_num, conds, og_num, order, group, having, proc, - select_lex, unit)) - DBUG_RETURN(1); + if (unit->prepare(thd, result, 0)) + goto err_prep; + if (send_prep_stmt(stmt, fields.elements) || thd->protocol_simple.send_fields(&fields, 0) #ifndef EMBEDDED_LIBRARY - || net_flush(&thd->net) + || net_flush(&thd->net) #endif - ) - DBUG_RETURN(1); - join->cleanup(); + ) + goto err_prep; + + unit->cleanup(); } - DBUG_RETURN(0); + thd->free_temporary_memory_pool_for_ps_preparing(); + DBUG_RETURN(0); + +err_prep: + unit->cleanup(); +err: + thd->free_temporary_memory_pool_for_ps_preparing(); + DBUG_RETURN(1); } @@ -898,6 +938,7 @@ bool mysql_stmt_prepare(THD *thd, char *packet, uint packet_length) thd->stmt_backup.set_statement(thd); thd->set_statement(stmt); + thd->current_statement= stmt; if (alloc_query(thd, packet, packet_length)) goto alloc_query_err; @@ -925,9 +966,9 @@ bool mysql_stmt_prepare(THD *thd, char *packet, uint packet_length) sl->prep_where= sl->where; } - cleanup_items(thd->free_list); stmt->set_statement(thd); thd->set_statement(&thd->stmt_backup); + thd->current_statement= 0; if (init_param_items(stmt)) goto init_param_err; @@ -944,8 +985,14 @@ init_param_err: alloc_query_err: /* Statement map deletes statement on erase */ thd->stmt_map.erase(stmt); + thd->current_statement= 0; DBUG_RETURN(1); insert_stmt_err: + stmt->set_statement(thd); + thd->set_statement(&thd->stmt_backup); + /* Statement map deletes statement on erase */ + thd->stmt_map.erase(stmt); + thd->current_statement= 0; delete stmt; DBUG_RETURN(1); } @@ -1010,24 +1057,36 @@ void mysql_stmt_execute(THD *thd, char *packet) /* Fix ORDER list */ for (order=(ORDER *)sl->order_list.first ; order ; order=order->next) order->item= (Item **)(order+1); + + /* + TODO: When the new table structure is ready, then have a status bit + to indicate the table is altered, and re-do the setup_* + and open the tables back. + */ + for (TABLE_LIST *tables= (TABLE_LIST*) sl->table_list.first; + tables; + tables= tables->next) + { + tables->table= 0; // safety - nasty init + tables->table_list= 0; + } + + { + SELECT_LEX_UNIT *unit= sl->master_unit(); + unit->unclean(); + unit->types.empty(); + // for derived tables & PS (which can't be reset by Item_subquery) + unit->reinit_exec_mechanism(); + } } - /* - TODO: When the new table structure is ready, then have a status bit - to indicate the table is altered, and re-do the setup_* - and open the tables back. - */ - for (TABLE_LIST *tables= (TABLE_LIST*) stmt->lex->select_lex.table_list.first; - tables; - tables= tables->next) - tables->table= 0; // safety - nasty init #ifndef EMBEDDED_LIBRARY if (stmt->param_count && setup_params_data(stmt)) - DBUG_VOID_RETURN; + goto end; #else if (stmt->param_count && (*stmt->setup_params_data)(stmt)) - DBUG_VOID_RETURN; + goto end; #endif if (!(specialflag & SPECIAL_NO_PRIOR)) @@ -1048,8 +1107,10 @@ void mysql_stmt_execute(THD *thd, char *packet) free_items(thd->free_list); cleanup_items(stmt->free_list); + close_thread_tables(thd); // to close derived tables free_root(&thd->mem_root, MYF(0)); thd->set_statement(&thd->stmt_backup); +end: DBUG_VOID_RETURN; } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 7ae25efebc6..7c1a5ad67b4 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -327,8 +327,7 @@ JOIN::prepare(Item ***rref_pointer_array, // Is it subselect { Item_subselect *subselect; - if ((subselect= select_lex->master_unit()->item) && - select_lex->linkage != GLOBAL_OPTIONS_TYPE) + if ((subselect= select_lex->master_unit()->item)) { Item_subselect::trans_res res; if ((res= subselect->select_transformer(this)) != @@ -1519,10 +1518,10 @@ JOIN::cleanup() lock=0; // It's faster to unlock later join_free(1); - if (exec_tmp_table1) - free_tmp_table(thd, exec_tmp_table1); - if (exec_tmp_table2) - free_tmp_table(thd, exec_tmp_table2); + if (exec_tmp_table1) + free_tmp_table(thd, exec_tmp_table1); + if (exec_tmp_table2) + free_tmp_table(thd, exec_tmp_table2); delete select; delete_dynamic(&keyuse); delete procedure; diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 2f55ec6e211..a05fa44cfc9 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -453,3 +453,9 @@ int st_select_lex_unit::cleanup() } DBUG_RETURN(error); } + + +void st_select_lex_unit::reinit_exec_mechanism() +{ + prepared= optimized= executed= 0; +} diff --git a/tests/client_test.c b/tests/client_test.c index eda168d7230..d9a180e3c1e 100644 --- a/tests/client_test.c +++ b/tests/client_test.c @@ -8189,6 +8189,90 @@ static void test_bug2247() fprintf(stdout, "OK"); } + + +static void test_subqueries() +{ + MYSQL_STMT *stmt; + int rc, i; + const char *query= "SELECT (SELECT SUM(a+b) FROM t2 where t1.b=t2.b GROUP BY t1.a LIMIT 1) as scalar_s, exists (select 1 from t2 where t2.a/2=t1.a) as exists_s, a in (select a+3 from t2) as in_s, (a-1,b-1) in (select a,b from t2) as in_row_s FROM t1, (select a x, b y from t2) tt WHERE x=a"; + + myheader("test_subquery"); + + rc = mysql_query(mysql, "DROP TABLE IF EXISTS t1,t2"); + myquery(rc); + + rc= mysql_query(mysql,"CREATE TABLE t1 (a int , b int);"); + myquery(rc); + + rc= mysql_query(mysql, + "insert into t1 values (1,1), (2, 2), (3,3), (4,4), (5,5);"); + myquery(rc); + + rc= mysql_query(mysql,"create table t2 select * from t1;"); + myquery(rc); + + stmt= mysql_prepare(mysql, query, strlen(query)); + mystmt_init(stmt); + for (i= 0; i < 3; i++) + { + rc= mysql_execute(stmt); + mystmt(stmt, rc); + assert(5 == my_process_stmt_result(stmt)); + } + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "DROP TABLE t1,t2"); + myquery(rc); +} + + +static void test_bad_union() +{ + MYSQL_STMT *stmt; + const char *query= "SELECT 1, 2 union SELECT 1"; + + myheader("test_bad_union"); + + stmt= mysql_prepare(mysql, query, strlen(query)); + assert(stmt == 0); + myerror(NULL); +} + +static void test_distinct() +{ + MYSQL_STMT *stmt; + int rc, i; + const char *query= + "SELECT 2+count(distinct b), group_concat(a) FROM t1 group by a"; + + myheader("test_subquery"); + + rc = mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + myquery(rc); + + rc= mysql_query(mysql,"CREATE TABLE t1 (a int , b int);"); + myquery(rc); + + rc= mysql_query(mysql, + "insert into t1 values (1,1), (2, 2), (3,3), (4,4), (5,5),\ +(1,10), (2, 20), (3,30), (4,40), (5,50)\;"); + myquery(rc); + + for (i= 0; i < 3; i++) + { + stmt= mysql_prepare(mysql, query, strlen(query)); + mystmt_init(stmt); + rc= mysql_execute(stmt); + mystmt(stmt, rc); + assert(5 == my_process_stmt_result(stmt)); + mysql_stmt_close(stmt); + } + + rc= mysql_query(mysql, "DROP TABLE t1"); + myquery(rc); +} + /* Test for bug#2248 "mysql_fetch without prior mysql_execute hangs" */ @@ -8385,6 +8469,9 @@ int main(int argc, char **argv) test_count= 1; start_time= time((time_t *)0); + + test_subqueries(); + client_query(); /* simple client query test */ #if NOT_YET_WORKING /* Used for internal new development debugging */ @@ -8437,7 +8524,8 @@ int main(int argc, char **argv) client_use_result(); /* usage of mysql_use_result() */ test_tran_bdb(); /* transaction test on BDB table type */ test_tran_innodb(); /* transaction test on InnoDB table type */ - test_prepare_ext(); /* test prepare with all types conversion -- TODO */ + test_prepare_ext(); /* test prepare with all types + conversion -- TODO */ test_prepare_syntax(); /* syntax check for prepares */ test_field_names(); /* test for field names */ test_field_flags(); /* test to help .NET provider team */ @@ -8450,7 +8538,7 @@ int main(int argc, char **argv) test_stmt_close(); /* mysql_stmt_close() test -- hangs */ test_prepare_field_result(); /* prepare meta info */ test_multi_stmt(); /* multi stmt test */ - test_multi_statements(); /* test multi statement execution */ + test_multi_statements();/* test multi statement execution */ test_store_result(); /* test the store_result */ test_store_result1(); /* test store result without buffers */ test_store_result2(); /* test store result for misc case */ @@ -8497,6 +8585,10 @@ int main(int argc, char **argv) test_bug2247(); /* test that mysql_stmt_affected_rows() returns number of rows affected by last prepared statement execution */ + test_subqueries(); /* repeatable subqueries */ + test_bad_union(); /* correct setup of UNION */ + test_distinct(); /* distinct aggregate functions */ + end_time= time((time_t *)0); total_time+= difftime(end_time, start_time); |