diff options
author | Konstantin Osipov <kostja@sun.com> | 2010-04-14 01:56:19 +0400 |
---|---|---|
committer | Konstantin Osipov <kostja@sun.com> | 2010-04-14 01:56:19 +0400 |
commit | 4288e329dd9676494a9e3927e61492e975277112 (patch) | |
tree | 7a87035df6e8d664a8301574158d57d8305c65cf /sql | |
parent | 71799f6f972d3ba5508c62d8d8ac3e33b25b0f1a (diff) | |
download | mariadb-git-4288e329dd9676494a9e3927e61492e975277112.tar.gz |
A fix for Bug#11918 "SP does not accept variables in LIMIT clause"
Allow stored procedure variables in LIMIT clause.
Only allow variables of INTEGER types.
Handle negative values by means of an implicit cast to UNSIGNED
(similarly to prepared statement placeholders).
Add tests.
Make sure replication works by not doing NAME_CONST substitution
for variables in LIMIT clause.
Add replication tests.
mysql-test/r/sp.result:
Update results (Bug#11918).
mysql-test/suite/rpl/r/rpl_sp.result:
Update results (Bug#11918).
mysql-test/suite/rpl/t/rpl_sp.test:
Add a test case for Bug#11918.
mysql-test/t/sp.test:
Add a test case for Bug#11918.
sql/item.cc:
Mark sp variables in LIMIT clause (a hack for replication).
sql/item.h:
Mark sp variables in LIMIT clause (a hack for replication).
sql/share/errmsg-utf8.txt:
Add a new error message (a type mismatch for LIMIT
clause parameter).
sql/sp_head.cc:
Binlog rewrite sp variables in LIMIT clause without NAME_CONST
substitution.
sql/sql_string.cc:
Implement append_ulonglong method.
sql/sql_string.h:
Declare append_ulonglong().
sql/sql_yacc.yy:
Support stored procedure variables in LIMIT.
Diffstat (limited to 'sql')
-rw-r--r-- | sql/item.cc | 4 | ||||
-rw-r--r-- | sql/item.h | 7 | ||||
-rw-r--r-- | sql/share/errmsg-utf8.txt | 3 | ||||
-rw-r--r-- | sql/sp_head.cc | 11 | ||||
-rw-r--r-- | sql/sql_string.cc | 10 | ||||
-rw-r--r-- | sql/sql_string.h | 1 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 35 |
7 files changed, 68 insertions, 3 deletions
diff --git a/sql/item.cc b/sql/item.cc index 209d5aa0197..4199f8a409a 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -1186,7 +1186,9 @@ Item_splocal::Item_splocal(const LEX_STRING &sp_var_name, enum_field_types sp_var_type, uint pos_in_q, uint len_in_q) :Item_sp_variable(sp_var_name.str, sp_var_name.length), - m_var_idx(sp_var_idx), pos_in_query(pos_in_q), len_in_query(len_in_q) + m_var_idx(sp_var_idx), + limit_clause_param(FALSE), + pos_in_query(pos_in_q), len_in_query(len_in_q) { maybe_null= TRUE; diff --git a/sql/item.h b/sql/item.h index 4241074c659..11b4199da2c 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1302,6 +1302,13 @@ class Item_splocal :public Item_sp_variable, Item_result m_result_type; enum_field_types m_field_type; public: + /* + Is this variable a parameter in LIMIT clause. + Used only during NAME_CONST substitution, to not append + NAME_CONST to the resulting query and thus not break + the slave. + */ + bool limit_clause_param; /* Position of this reference to SP variable in the statement (the statement itself is in sp_instr_stmt::m_query). diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 517782cb0b4..3acf990e00d 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -6327,3 +6327,6 @@ ER_LOCK_ABORTED ER_DATA_OUT_OF_RANGE 22003 eng "%s value is out of range in '%s'" + +ER_WRONG_SPVAR_TYPE_IN_LIMIT + eng "A variable of a non-integer type in LIMIT clause" diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 62d53888149..c91ba2a68b4 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -978,11 +978,20 @@ subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str) res|= qbuf.append(cur + prev_pos, (*splocal)->pos_in_query - prev_pos); prev_pos= (*splocal)->pos_in_query + (*splocal)->len_in_query; + res|= (*splocal)->fix_fields(thd, (Item **) splocal); + if (res) + break; + + if ((*splocal)->limit_clause_param) + { + res|= qbuf.append_ulonglong((*splocal)->val_uint()); + continue; + } + /* append the spvar substitute */ res|= qbuf.append(STRING_WITH_LEN(" NAME_CONST('")); res|= qbuf.append((*splocal)->m_name.str, (*splocal)->m_name.length); res|= qbuf.append(STRING_WITH_LEN("',")); - res|= (*splocal)->fix_fields(thd, (Item **) splocal); if (res) break; diff --git a/sql/sql_string.cc b/sql/sql_string.cc index 75e8ca30cf0..9fbc06b7529 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -405,6 +405,16 @@ bool String::append(const char *s) } + +bool String::append_ulonglong(ulonglong val) +{ + if (realloc(str_length+MAX_BIGINT_WIDTH+2)) + return TRUE; + char *end= (char*) longlong10_to_str(val, (char*) Ptr + str_length, 10); + str_length= end - Ptr; + return FALSE; +} + /* Append a string in the given charset to the string with character set recoding diff --git a/sql/sql_string.h b/sql/sql_string.h index f67d6de9a0f..debfb7aa9c6 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -267,6 +267,7 @@ public: bool append(const char *s); bool append(const char *s,uint32 arg_length); bool append(const char *s,uint32 arg_length, CHARSET_INFO *cs); + bool append_ulonglong(ulonglong val); bool append(IO_CACHE* file, uint32 arg_length); bool append_with_prefill(const char *s, uint32 arg_length, uint32 full_length, char fill_char); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 207e72404ef..a0d64e6a378 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -9830,7 +9830,40 @@ limit_options: ; limit_option: - param_marker + ident + { + Item_splocal *splocal; + THD *thd= YYTHD; + LEX *lex= thd->lex; + Lex_input_stream *lip= & thd->m_parser_state->m_lip; + sp_variable_t *spv; + sp_pcontext *spc = lex->spcont; + if (spc && (spv = spc->find_variable(&$1))) + { + splocal= new (thd->mem_root) + Item_splocal($1, spv->offset, spv->type, + lip->get_tok_start() - lex->sphead->m_tmp_query, + lip->get_ptr() - lip->get_tok_start()); + if (splocal == NULL) + MYSQL_YYABORT; +#ifndef DBUG_OFF + splocal->m_sp= lex->sphead; +#endif + lex->safe_to_cache_query=0; + } + else + { + my_error(ER_SP_UNDECLARED_VAR, MYF(0), $1.str); + MYSQL_YYABORT; + } + if (splocal->type() != Item::INT_ITEM) + { + my_error(ER_WRONG_SPVAR_TYPE_IN_LIMIT, MYF(0)); + MYSQL_YYABORT; + } + splocal->limit_clause_param= TRUE; + $$= splocal; + } | param_marker { ((Item_param *) $1)->limit_clause_param= TRUE; } |