#At file:///home/kgeorge/mysql/bzr/B32124-5.0-bugteam/
2672 Georgi Kodinov 2008-08-29
Bug #32124: crash if prepared statements refer to variables in the where clause
The code to get read the value of a system variable was extracting its value
on PREPARE stage and was substituting the value (as a constant) into the parse tree.
Note that this must be a reversible transformation, i.e. it must be reversed before
each re-execution.
Unfortunately this cannot be reliably done using the current code, because there are
other non-reversible source tree transformations that can interfere with this
reversible transformation.
Fixed by not resolving the value at PREPARE, but at EXECUTE (as the rest of the
functions operate).
Updated an obsolete related test suite (variables-big) and the code to test the
result type of system variables (as per bug 74).
modified:
mysql-test/r/ps_11bugs.result
mysql-test/r/variables-big.result
mysql-test/r/variables.result
mysql-test/t/ps_11bugs.test
sql/item_func.cc
sql/item_func.h
tests/mysql_client_test.c
per-file messages:
mysql-test/r/ps_11bugs.result
Bug#32124: test case
mysql-test/r/variables-big.result
Bug#32124: updated an obsolete test case
mysql-test/r/variables.result
Bug #32124: no source transformation at prepare anymore : update the test result
mysql-test/t/ps_11bugs.test
Bug#32124: test case
sql/item_func.cc
Bug #32124: moved the retrieval of system variable's value from
PREPARE to EXECUTE
sql/item_func.h
Bug #32124: moved the retrieval of system variable's value from
PREPARE to EXECUTE
tests/mysql_client_test.c
Bug#32124: updated the client test to reflect the changed metadata
for the system variables.
=== modified file 'mysql-test/r/ps_11bugs.result'
--- a/mysql-test/r/ps_11bugs.result 2006-10-04 15:19:23 +0000
+++ b/mysql-test/r/ps_11bugs.result 2008-08-29 15:16:59 +0000
@@ -162,4 +162,10 @@ a b
12 NULL
drop table t1;
drop table t2;
+CREATE TABLE t1 (a INT);
+PREPARE stmt FROM 'select 1 from `t1` where `a` = any (select (@@tmpdir))';
+EXECUTE stmt;
+1
+DEALLOCATE PREPARE stmt;
+DROP TABLE t1;
End of 5.0 tests.
=== modified file 'mysql-test/r/variables-big.result'
--- a/mysql-test/r/variables-big.result 2007-12-21 19:30:23 +0000
+++ b/mysql-test/r/variables-big.result 2008-08-29 15:16:59 +0000
@@ -1,24 +1,20 @@
set session transaction_prealloc_size=1024*1024*1024*1;
show processlist;
Id User Host db Command Time State Info
-6 root localhost test Query 0 NULL show processlist
+1 root localhost test Query 0 NULL show processlist
set session transaction_prealloc_size=1024*1024*1024*2;
show processlist;
Id User Host db Command Time State Info
-6 root localhost test Query 1 NULL show processlist
+1 root localhost test Query 0 NULL show processlist
set session transaction_prealloc_size=1024*1024*1024*3;
show processlist;
Id User Host db Command Time State Info
-6 root localhost test Query 0 NULL show processlist
+1 root localhost test Query 1 NULL show processlist
set session transaction_prealloc_size=1024*1024*1024*4;
-Warnings:
-Warning 1292 Truncated incorrect transaction_prealloc_size value: '4294967296'
show processlist;
Id User Host db Command Time State Info
-6 root localhost test Query 0 NULL show processlist
+1 root localhost test Query 0 NULL show processlist
set session transaction_prealloc_size=1024*1024*1024*5;
-Warnings:
-Warning 1292 Truncated incorrect transaction_prealloc_size value: '5368709120'
show processlist;
Id User Host db Command Time State Info
-6 root localhost test Query 0 NULL show processlist
+1 root localhost test Query 0 NULL show processlist
=== modified file 'mysql-test/r/variables.result'
--- a/mysql-test/r/variables.result 2008-02-17 11:37:39 +0000
+++ b/mysql-test/r/variables.result 2008-08-29 15:16:59 +0000
@@ -143,7 +143,7 @@ explain extended select @@IDENTITY,last_
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
-Note 1003 select 345 AS `@@IDENTITY`,last_insert_id() AS `last_insert_id()`,345 AS `@@identity`
+Note 1003 select (@@IDENTITY) AS `@@IDENTITY`,last_insert_id() AS `last_insert_id()`,(@@identity) AS `@@identity`
set big_tables=OFF, big_tables=ON, big_tables=0, big_tables=1, big_tables="OFF", big_tables="ON";
set global concurrent_insert=2;
show variables like 'concurrent_insert';
=== modified file 'mysql-test/t/ps_11bugs.test'
--- a/mysql-test/t/ps_11bugs.test 2006-10-04 15:19:23 +0000
+++ b/mysql-test/t/ps_11bugs.test 2008-08-29 15:16:59 +0000
@@ -177,4 +177,15 @@ select * from t2;
drop table t1;
drop table t2;
+#
+# Bug #32124: crash if prepared statements refer to variables in the where
+# clause
+#
+
+CREATE TABLE t1 (a INT);
+PREPARE stmt FROM 'select 1 from `t1` where `a` = any (select (@@tmpdir))';
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+DROP TABLE t1;
+
--echo End of 5.0 tests.
=== modified file 'sql/item_func.cc'
--- a/sql/item_func.cc 2008-07-30 11:07:37 +0000
+++ b/sql/item_func.cc 2008-08-29 15:16:59 +0000
@@ -4785,23 +4785,193 @@ Item_func_get_system_var(sys_var *var_ar
}
-bool
-Item_func_get_system_var::fix_fields(THD *thd, Item **ref)
+void Item_func_get_system_var::fix_length_and_dec()
{
- Item *item;
- DBUG_ENTER("Item_func_get_system_var::fix_fields");
+ maybe_null=0;
+ decimals=NOT_FIXED_DEC;
+ max_length=MAX_BLOB_WIDTH;
- /*
- Evaluate the system variable and substitute the result (a basic constant)
- instead of this item. If the variable can not be evaluated,
- the error is reported in sys_var::item().
- */
- if (!(item= var->item(thd, var_type, &component)))
- DBUG_RETURN(1); // Impossible
- item->set_name(name, 0, system_charset_info); // don't allocate a new name
- thd->change_item_tree(ref, item);
+ if (var->check_type(var_type))
+ {
+ if (var_type != OPT_DEFAULT)
+ {
+ my_error(ER_INCORRECT_GLOBAL_LOCAL_VAR, MYF(0),
+ var->name, var_type == OPT_GLOBAL ? "SESSION" : "GLOBAL");
+ return;
+ }
+ /* As there was no local variable, return the global value */
+ var_type= OPT_GLOBAL;
+ }
+
+ switch (var->show_type())
+ {
+ case SHOW_LONG:
+ case SHOW_INT:
+ unsigned_flag= TRUE;
+ max_length= MY_INT64_NUM_DECIMAL_DIGITS;
+ decimals=0;
+ break;
+ case SHOW_HA_ROWS:
+ case SHOW_LONGLONG:
+ unsigned_flag= FALSE;
+ max_length= MY_INT64_NUM_DECIMAL_DIGITS;
+ decimals=0;
+ break;
+ case SHOW_CHAR:
+ max_length= MAX_BLOB_WIDTH;
+ collation.set(system_charset_info, DERIVATION_SYSCONST);
+ break;
+ case SHOW_MY_BOOL:
+ unsigned_flag= TRUE;
+ max_length= 1;
+ decimals=0;
+ break;
+ default:
+ my_error(ER_VAR_CANT_BE_READ, MYF(0), name);
+ break;
+ }
+}
+
+
+void Item_func_get_system_var::print(String *str)
+
+{
+ str->append('(');
+ str->append(name, name_length);
+ str->append(')');
+}
+
+
+enum Item_result Item_func_get_system_var::result_type() const
+{
+ switch (var->show_type())
+ {
+ case SHOW_MY_BOOL:
+ case SHOW_INT:
+ case SHOW_LONG:
+ case SHOW_LONGLONG:
+ case SHOW_HA_ROWS:
+ return INT_RESULT;
+ case SHOW_CHAR: return STRING_RESULT;
+ default:
+ my_error(ER_VAR_CANT_BE_READ, MYF(0), name);
+ break;
+ }
+}
+
+
+enum_field_types Item_func_get_system_var::field_type() const
+{
+ switch (var->show_type())
+ {
+ case SHOW_MY_BOOL:
+ case SHOW_INT:
+ case SHOW_LONG:
+ case SHOW_LONGLONG:
+ case SHOW_HA_ROWS:
+ return MYSQL_TYPE_LONGLONG;
+ case SHOW_CHAR: return MYSQL_TYPE_VARCHAR;
+ default:
+ my_error(ER_VAR_CANT_BE_READ, MYF(0), name);
+ break;
+ }
+}
+
+
+double Item_func_get_system_var::val_real()
+{
+ return (double) val_int();
+}
- DBUG_RETURN(0);
+
+longlong Item_func_get_system_var::val_int()
+{
+ THD *thd= current_thd;
+ switch (var->show_type())
+ {
+ case SHOW_INT:
+ {
+ uint value;
+ pthread_mutex_lock(&LOCK_global_system_variables);
+ value= *(uint*) var->value_ptr(thd, var_type, &component);
+ pthread_mutex_unlock(&LOCK_global_system_variables);
+ return (longlong)(ulonglong) value;
+ }
+ case SHOW_LONG:
+ {
+ ulong value;
+ pthread_mutex_lock(&LOCK_global_system_variables);
+ value= *(ulong*) var->value_ptr(thd, var_type, &component);
+ pthread_mutex_unlock(&LOCK_global_system_variables);
+ return (longlong)(ulonglong) value;
+ }
+ case SHOW_LONGLONG:
+ {
+ longlong value;
+ pthread_mutex_lock(&LOCK_global_system_variables);
+ value= *(longlong*) var->value_ptr(thd, var_type, &component);
+ pthread_mutex_unlock(&LOCK_global_system_variables);
+ return value;
+ }
+ case SHOW_HA_ROWS:
+ {
+ ha_rows value;
+ pthread_mutex_lock(&LOCK_global_system_variables);
+ value= *(ha_rows*) var->value_ptr(thd, var_type, &component);
+ pthread_mutex_unlock(&LOCK_global_system_variables);
+ return (longlong) value;
+ }
+ case SHOW_MY_BOOL:
+ return (longlong) (int32) *(my_bool*)
+ var->value_ptr(thd, var_type, &component);
+ default:
+ my_error(ER_VAR_CANT_BE_READ, MYF(0), name);
+ break;
+ }
+}
+
+
+String* Item_func_get_system_var::val_str(String* str)
+{
+ THD *thd= current_thd;
+ switch (var->show_type())
+ {
+ case SHOW_CHAR:
+ {
+ pthread_mutex_lock(&LOCK_global_system_variables);
+ char *cptr= (char*) var->value_ptr(thd, var_type, &component);
+ if (cptr)
+ {
+ if (str->copy(cptr, strlen(cptr), system_charset_info))
+ str= NULL;
+ }
+ else
+ {
+ null_value= TRUE;
+ str= NULL;
+ }
+ pthread_mutex_unlock(&LOCK_global_system_variables);
+ return str;
+ }
+ default:
+ my_error(ER_VAR_CANT_BE_READ, MYF(0), name);
+ break;
+ }
+ return str;
+}
+
+
+bool Item_func_get_system_var::eq(const Item *item, bool binary_cmp) const
+{
+ /* Assume we don't have rtti */
+ if (this == item)
+ return 1; // Same item is same.
+ /* Check if other type is also a get_user_var() object */
+ if (item->type() != FUNC_ITEM ||
+ ((Item_func*) item)->functype() != functype())
+ return 0;
+ Item_func_get_system_var *other=(Item_func_get_system_var*) item;
+ return (var == other->var && var_type == other->var_type);
}
=== modified file 'sql/item_func.h'
--- a/sql/item_func.h 2008-02-28 13:23:22 +0000
+++ b/sql/item_func.h 2008-08-29 15:16:59 +0000
@@ -55,7 +55,7 @@ public:
NOW_FUNC, TRIG_COND_FUNC,
SUSERVAR_FUNC, GUSERVAR_FUNC, COLLATE_FUNC,
EXTRACT_FUNC, CHAR_TYPECAST_FUNC, FUNC_SP, UDF_FUNC,
- NEG_FUNC };
+ NEG_FUNC, GSYSVAR_FUNC };
enum optimize_type { OPTIMIZE_NONE,OPTIMIZE_KEY,OPTIMIZE_OP, OPTIMIZE_NULL,
OPTIMIZE_EQUAL };
enum Type type() const { return FUNC_ITEM; }
@@ -1401,17 +1401,19 @@ public:
Item_func_get_system_var(sys_var *var_arg, enum_var_type var_type_arg,
LEX_STRING *component_arg, const char *name_arg,
size_t name_len_arg);
- bool fix_fields(THD *thd, Item **ref);
- /*
- Stubs for pure virtual methods. Should never be called: this
- item is always substituted with a constant in fix_fields().
- */
- double val_real() { DBUG_ASSERT(0); return 0.0; }
- longlong val_int() { DBUG_ASSERT(0); return 0; }
- String* val_str(String*) { DBUG_ASSERT(0); return 0; }
- void fix_length_and_dec() { DBUG_ASSERT(0); }
+ enum Functype functype() const { return GSYSVAR_FUNC; }
+ void fix_length_and_dec();
+ void print(String *str);
+ bool const_item() const { return true; }
+ table_map used_tables() const { return 0; }
+ enum Item_result result_type() const;
+ enum_field_types field_type() const;
+ double val_real();
+ longlong val_int();
+ String* val_str(String*);
/* TODO: fix to support views */
const char *func_name() const { return "get_system_var"; }
+ bool eq(const Item *item, bool binary_cmp) const;
};
=== modified file 'tests/mysql_client_test.c'
--- a/tests/mysql_client_test.c 2008-08-20 09:49:28 +0000
+++ b/tests/mysql_client_test.c 2008-08-29 15:16:59 +0000
@@ -37,6 +37,7 @@
#define MAX_TEST_QUERY_LENGTH 300 /* MAX QUERY BUFFER LENGTH */
#define MAX_KEY MAX_INDEXES
#define MAX_SERVER_ARGS 64
+#define MAX_BLOB_WIDTH 8192
/* set default options */
static int opt_testcase = 0;
@@ -6934,6 +6935,9 @@ static void test_field_misc()
char table_type[NAME_LEN];
ulong type_length;
int rc;
+ MYSQL_FIELD *field;
+ CHARSET_INFO *cs;
+
myheader("test_field_misc");
@@ -7012,12 +7016,21 @@ static void test_field_misc()
DIE_UNLESS(1 == my_process_stmt_result(stmt));
+ if (!(field= mysql_fetch_field_direct(result, 0)))
+ {
+ fprintf(stdout, "\n *** ERROR: FAILED TO GET THE RESULT ***");
+ exit(1);
+ }
+ cs= get_charset(field->charsetnr, 0);
+ DIE_UNLESS(cs);
+
verify_prepare_field(result, 0,
"@@table_type", "", /* field and its org name */
mysql_get_server_version(mysql) <= 50000 ?
MYSQL_TYPE_STRING : MYSQL_TYPE_VAR_STRING,
"", "", /* table and its org name */
- "", type_length, 0); /* db name, length */
+ /* db name, length */
+ "", MAX_BLOB_WIDTH/cs->mbmaxlen, 0);
mysql_free_result(result);
mysql_stmt_close(stmt);
@@ -7038,7 +7051,8 @@ static void test_field_misc()
"@@max_error_count", "", /* field and its org name */
MYSQL_TYPE_LONGLONG, /* field type */
"", "", /* table and its org name */
- "", 10, 0); /* db name, length */
+ /* db name, length */
+ "", MY_INT64_NUM_DECIMAL_DIGITS , 0);
mysql_free_result(result);
mysql_stmt_close(stmt);
@@ -7058,7 +7072,8 @@ static void test_field_misc()
"@@max_allowed_packet", "", /* field and its org name */
MYSQL_TYPE_LONGLONG, /* field type */
"", "", /* table and its org name */
- "", 10, 0); /* db name, length */
+ /* db name, length */
+ "", MY_INT64_NUM_DECIMAL_DIGITS, 0);
mysql_free_result(result);
mysql_stmt_close(stmt);