summaryrefslogtreecommitdiff
path: root/sql/sql_lex.cc
diff options
context:
space:
mode:
authorDmitry Shulga <Dmitry.Shulga@oracle.com>2011-08-13 13:34:00 +0700
committerDmitry Shulga <Dmitry.Shulga@oracle.com>2011-08-13 13:34:00 +0700
commit28bed7d92bcc6e0b45fd657a32addcf1fb60abc3 (patch)
treefe01e7d32375d087c27c01c181f1087a25256111 /sql/sql_lex.cc
parent1e51c1c841f87970cb2d7158ed022049d6c1e5d2 (diff)
downloadmariadb-git-28bed7d92bcc6e0b45fd657a32addcf1fb60abc3.tar.gz
Fixed Bug#12621017 - CRASH IF A SP VARIABLE IS USED IN THE LIMIT CLAUSE OF A
SET STATEMENT. Server built with debug asserts, without debug crashes if a user tries to run a stored procedure that constains query with subquery that include either LIMIT or LIMIT OFFSET clauses. The problem was that Item::fix_fields() was not called for the items representing LIMIT or OFFSET clauses. The solution is to call Item::fix_fields() right before evaluation in st_select_lex_unit::set_limit().
Diffstat (limited to 'sql/sql_lex.cc')
-rw-r--r--sql/sql_lex.cc59
1 files changed, 57 insertions, 2 deletions
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 3e6052d3561..f22f459e93e 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -2599,7 +2599,47 @@ void st_select_lex_unit::set_limit(st_select_lex *sl)
ulonglong val;
DBUG_ASSERT(! thd->stmt_arena->is_stmt_prepare());
- val= sl->select_limit ? sl->select_limit->val_uint() : HA_POS_ERROR;
+ if (sl->select_limit)
+ {
+ Item *item = sl->select_limit;
+ /*
+ fix_fields() has not been called for sl->select_limit. That's due to the
+ historical reasons -- this item could be only of type Item_int, and
+ Item_int does not require fix_fields(). Thus, fix_fields() was never
+ called for sl->select_limit.
+
+ Some time ago, Item_splocal was also allowed for LIMIT / OFFSET clauses.
+ However, the fix_fields() behavior was not updated, which led to a crash
+ in some cases.
+
+ There is no single place where to call fix_fields() for LIMIT / OFFSET
+ items during the fix-fields-phase. Thus, for the sake of readability,
+ it was decided to do it here, on the evaluation phase (which is a
+ violation of design, but we chose the lesser of two evils).
+
+ We can call fix_fields() here, because sl->select_limit can be of two
+ types only: Item_int and Item_splocal. Item_int::fix_fields() is trivial,
+ and Item_splocal::fix_fields() (or rather Item_sp_variable::fix_fields())
+ has the following specific:
+ 1) it does not affect other items;
+ 2) it does not fail.
+
+ Nevertheless DBUG_ASSERT was added to catch future changes in
+ fix_fields() implementation. Also added runtime check against a result
+ of fix_fields() in order to handle error condition in non-debug build.
+ */
+ bool fix_fields_successful= true;
+ if (!item->fixed)
+ {
+ fix_fields_successful= !item->fix_fields(thd, NULL);
+
+ DBUG_ASSERT(fix_fields_successful);
+ }
+ val= fix_fields_successful ? item->val_uint() : HA_POS_ERROR;
+ }
+ else
+ val= HA_POS_ERROR;
+
select_limit_val= (ha_rows)val;
#ifndef BIG_TABLES
/*
@@ -2609,7 +2649,22 @@ void st_select_lex_unit::set_limit(st_select_lex *sl)
if (val != (ulonglong)select_limit_val)
select_limit_val= HA_POS_ERROR;
#endif
- val= sl->offset_limit ? sl->offset_limit->val_uint() : ULL(0);
+ if (sl->offset_limit)
+ {
+ Item *item = sl->offset_limit;
+ // see comment for sl->select_limit branch.
+ bool fix_fields_successful= true;
+ if (!item->fixed)
+ {
+ fix_fields_successful= !item->fix_fields(thd, NULL);
+
+ DBUG_ASSERT(fix_fields_successful);
+ }
+ val= fix_fields_successful ? item->val_uint() : HA_POS_ERROR;
+ }
+ else
+ val= ULL(0);
+
offset_limit_cnt= (ha_rows)val;
#ifndef BIG_TABLES
/* Check for truncation. */