Commit a4e06592 authored by pem@mysql.com's avatar pem@mysql.com

Made multiple queries (SELECT without INTO) work in SPs.

This included bug fixes in the 4.1 protocol (actually send and receive the
server_status flags).
parent 7b5df9ed
...@@ -1370,6 +1370,7 @@ static MYSQL_DATA *read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields, ...@@ -1370,6 +1370,7 @@ static MYSQL_DATA *read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields,
if (pkt_len > 1) /* MySQL 4.1 protocol */ if (pkt_len > 1) /* MySQL 4.1 protocol */
{ {
mysql->warning_count= uint2korr(cp+1); mysql->warning_count= uint2korr(cp+1);
mysql->server_status= uint2korr(cp+3);
DBUG_PRINT("info",("warning_count: %ld", mysql->warning_count)); DBUG_PRINT("info",("warning_count: %ld", mysql->warning_count));
} }
DBUG_PRINT("exit",("Got %d rows",result->rows)); DBUG_PRINT("exit",("Got %d rows",result->rows));
...@@ -1395,7 +1396,10 @@ read_one_row(MYSQL *mysql,uint fields,MYSQL_ROW row, ulong *lengths) ...@@ -1395,7 +1396,10 @@ read_one_row(MYSQL *mysql,uint fields,MYSQL_ROW row, ulong *lengths)
if (pkt_len <= 8 && mysql->net.read_pos[0] == 254) if (pkt_len <= 8 && mysql->net.read_pos[0] == 254)
{ {
if (pkt_len > 1) /* MySQL 4.1 protocol */ if (pkt_len > 1) /* MySQL 4.1 protocol */
{
mysql->warning_count= uint2korr(mysql->net.read_pos+1); mysql->warning_count= uint2korr(mysql->net.read_pos+1);
mysql->server_status= uint2korr(mysql->net.read_pos+3);
}
return 1; /* End of data */ return 1; /* End of data */
} }
prev_pos= 0; /* allowed to write at packet[-1] */ prev_pos= 0; /* allowed to write at packet[-1] */
...@@ -5370,6 +5374,7 @@ static MYSQL_DATA *read_binary_rows(MYSQL_STMT *stmt) ...@@ -5370,6 +5374,7 @@ static MYSQL_DATA *read_binary_rows(MYSQL_STMT *stmt)
if (pkt_len > 1) if (pkt_len > 1)
{ {
mysql->warning_count= uint2korr(cp+1); mysql->warning_count= uint2korr(cp+1);
mysql->server_status= uint2korr(cp+3);
DBUG_PRINT("info",("warning_count: %ld", mysql->warning_count)); DBUG_PRINT("info",("warning_count: %ld", mysql->warning_count));
} }
DBUG_PRINT("exit",("Got %d rows",result->rows)); DBUG_PRINT("exit",("Got %d rows",result->rows));
......
...@@ -58,9 +58,14 @@ declare y int; ...@@ -58,9 +58,14 @@ declare y int;
set x = y; set x = y;
end; end;
Referring to uninitialized variable y Referring to uninitialized variable y
create procedure foo(x int) create procedure foo()
select * from test.t1; begin
select name from mysql.proc;
select type from mysql.proc;
end;
call foo();
SELECT in a stored procedure must have INTO SELECT in a stored procedure must have INTO
drop procedure foo;
create procedure foo() create procedure foo()
return 42; return 42;
RETURN is only allowed in a FUNCTION RETURN is only allowed in a FUNCTION
......
...@@ -89,10 +89,16 @@ begin ...@@ -89,10 +89,16 @@ begin
set x = y; set x = y;
end| end|
# We require INTO in SELECTs (for now; this might change in the future) # We require INTO in SELECTs for some older clients (as mysql and mysqltest,
# for now).
create procedure foo()
begin
select name from mysql.proc;
select type from mysql.proc;
end|
--error 1268 --error 1268
create procedure foo(x int) call foo()|
select * from test.t1| drop procedure foo|
# RETURN in FUNCTION only # RETURN in FUNCTION only
--error 1269 --error 1269
......
...@@ -360,7 +360,7 @@ send_eof(THD *thd, bool no_flush) ...@@ -360,7 +360,7 @@ send_eof(THD *thd, bool no_flush)
uint tmp= min(thd->total_warn_count, 65535); uint tmp= min(thd->total_warn_count, 65535);
buff[0]=254; buff[0]=254;
int2store(buff+1, tmp); int2store(buff+1, tmp);
int2store(buff+3, 0); // No flags yet int2store(buff+3, thd->server_status);
VOID(my_net_write(net,(char*) buff,5)); VOID(my_net_write(net,(char*) buff,5));
VOID(net_flush(net)); VOID(net_flush(net));
} }
......
...@@ -92,7 +92,7 @@ eval_func_item(THD *thd, Item *it, enum enum_field_types type) ...@@ -92,7 +92,7 @@ eval_func_item(THD *thd, Item *it, enum enum_field_types type)
} }
sp_head::sp_head(LEX_STRING *name, LEX *lex) sp_head::sp_head(LEX_STRING *name, LEX *lex)
: Sql_alloc(), m_simple_case(FALSE) : Sql_alloc(), m_simple_case(FALSE), m_multi_query(FALSE)
{ {
DBUG_ENTER("sp_head::sp_head"); DBUG_ENTER("sp_head::sp_head");
const char *dstr = (const char*)lex->buf; const char *dstr = (const char*)lex->buf;
......
...@@ -46,6 +46,8 @@ public: ...@@ -46,6 +46,8 @@ public:
int m_type; // TYPE_ENUM_FUNCTION or TYPE_ENUM_PROCEDURE int m_type; // TYPE_ENUM_FUNCTION or TYPE_ENUM_PROCEDURE
enum enum_field_types m_returns; // For FUNCTIONs only enum enum_field_types m_returns; // For FUNCTIONs only
my_bool m_simple_case; // TRUE if parsing simple case, FALSE otherwise my_bool m_simple_case; // TRUE if parsing simple case, FALSE otherwise
my_bool m_multi_query; // TRUE if a procedure with SELECT(s)
uint m_old_cmq; // Old CLIENT_MULTI_QUERIES value
#if 0 #if 0
// We're not using this at the moment. // We're not using this at the moment.
List<char *> m_calls; // Called procedures. List<char *> m_calls; // Called procedures.
......
...@@ -3043,8 +3043,8 @@ mysql_execute_command(THD *thd) ...@@ -3043,8 +3043,8 @@ mysql_execute_command(THD *thd)
net_printf(thd, ER_SP_STORE_FAILED, SP_TYPE_STRING(lex), name); net_printf(thd, ER_SP_STORE_FAILED, SP_TYPE_STRING(lex), name);
goto error; goto error;
} }
break;
} }
break;
case SQLCOM_CALL: case SQLCOM_CALL:
{ {
sp_head *sp; sp_head *sp;
...@@ -3057,17 +3057,40 @@ mysql_execute_command(THD *thd) ...@@ -3057,17 +3057,40 @@ mysql_execute_command(THD *thd)
} }
else else
{ {
uint smrx;
LINT_INIT(smrx);
#ifndef EMBEDDED_LIBRARY #ifndef EMBEDDED_LIBRARY
// When executing substatements, they're assumed to send_error when // When executing substatements, they're assumed to send_error when
// it happens, but not to send_ok. // it happens, but not to send_ok.
my_bool nsok= thd->net.no_send_ok; my_bool nsok= thd->net.no_send_ok;
thd->net.no_send_ok= TRUE; thd->net.no_send_ok= TRUE;
#endif #endif
if (sp->m_multi_query)
{
if (! (thd->client_capabilities & CLIENT_MULTI_QUERIES))
{
send_error(thd, ER_SP_BADSELECT);
#ifndef EMBEDDED_LIBRARY
thd->net.no_send_ok= nsok;
#endif
sp->destroy(); // QQ Free memory. Remove this when caching!!!
goto error;
}
smrx= thd->server_status & SERVER_MORE_RESULTS_EXISTS;
thd->server_status |= SERVER_MORE_RESULTS_EXISTS;
}
res= sp->execute_procedure(thd, &lex->value_list); res= sp->execute_procedure(thd, &lex->value_list);
#ifndef EMBEDDED_LIBRARY #ifndef EMBEDDED_LIBRARY
thd->net.no_send_ok= nsok; thd->net.no_send_ok= nsok;
#endif #endif
if (sp->m_multi_query)
{
if (! smrx)
thd->server_status &= ~SERVER_MORE_RESULTS_EXISTS;
}
sp->destroy(); // QQ Free memory. Remove this when caching!!! sp->destroy(); // QQ Free memory. Remove this when caching!!!
...@@ -3076,8 +3099,8 @@ mysql_execute_command(THD *thd) ...@@ -3076,8 +3099,8 @@ mysql_execute_command(THD *thd)
else else
goto error; // Substatement should already have sent error goto error; // Substatement should already have sent error
} }
break;
} }
break;
case SQLCOM_ALTER_PROCEDURE: case SQLCOM_ALTER_PROCEDURE:
case SQLCOM_ALTER_FUNCTION: case SQLCOM_ALTER_FUNCTION:
{ {
...@@ -3099,8 +3122,8 @@ mysql_execute_command(THD *thd) ...@@ -3099,8 +3122,8 @@ mysql_execute_command(THD *thd)
sp->destroy(); // QQ Free memory. Remove this when caching!!! sp->destroy(); // QQ Free memory. Remove this when caching!!!
send_ok(thd); send_ok(thd);
} }
break;
} }
break;
case SQLCOM_DROP_PROCEDURE: case SQLCOM_DROP_PROCEDURE:
case SQLCOM_DROP_FUNCTION: case SQLCOM_DROP_FUNCTION:
{ {
...@@ -3149,8 +3172,8 @@ mysql_execute_command(THD *thd) ...@@ -3149,8 +3172,8 @@ mysql_execute_command(THD *thd)
lex->udf.name.str); lex->udf.name.str);
goto error; goto error;
} }
break;
} }
break;
default: /* Impossible */ default: /* Impossible */
send_ok(thd); send_ok(thd);
break; break;
......
...@@ -940,6 +940,14 @@ create: ...@@ -940,6 +940,14 @@ create:
lex->spcont= new sp_pcontext(); lex->spcont= new sp_pcontext();
lex->sphead= new sp_head(&$3, lex); lex->sphead= new sp_head(&$3, lex);
lex->sphead->m_type= TYPE_ENUM_PROCEDURE; lex->sphead->m_type= TYPE_ENUM_PROCEDURE;
/*
* We have to turn of CLIENT_MULTI_QUERIES while parsing a
* stored procedure, otherwise yylex will chop it into pieces
* at each ';'.
*/
lex->sphead->m_old_cmq=
YYTHD->client_capabilities & CLIENT_MULTI_QUERIES;
YYTHD->client_capabilities &= (~CLIENT_MULTI_QUERIES);
} }
'(' sp_pdparam_list ')' '(' sp_pdparam_list ')'
{ {
...@@ -947,7 +955,12 @@ create: ...@@ -947,7 +955,12 @@ create:
} }
sp_proc_stmt sp_proc_stmt
{ {
Lex->sql_command= SQLCOM_CREATE_PROCEDURE; LEX *lex= Lex;
lex->sql_command= SQLCOM_CREATE_PROCEDURE;
/* Restore flag if it was cleared above */
if (lex->sphead->m_old_cmq)
YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES;
} }
; ;
...@@ -976,6 +989,14 @@ create_function_tail: ...@@ -976,6 +989,14 @@ create_function_tail:
lex->spcont= new sp_pcontext(); lex->spcont= new sp_pcontext();
lex->sphead= new sp_head(&lex->udf.name, lex); lex->sphead= new sp_head(&lex->udf.name, lex);
lex->sphead->m_type= TYPE_ENUM_FUNCTION; lex->sphead->m_type= TYPE_ENUM_FUNCTION;
/*
* We have to turn of CLIENT_MULTI_QUERIES while parsing a
* stored procedure, otherwise yylex will chop it into pieces
* at each ';'.
*/
lex->sphead->m_old_cmq=
YYTHD->client_capabilities & CLIENT_MULTI_QUERIES;
YYTHD->client_capabilities &= ~CLIENT_MULTI_QUERIES;
} }
sp_fdparam_list ')' sp_fdparam_list ')'
{ {
...@@ -987,7 +1008,12 @@ create_function_tail: ...@@ -987,7 +1008,12 @@ create_function_tail:
} }
sp_proc_stmt sp_proc_stmt
{ {
Lex->sql_command = SQLCOM_CREATE_SPFUNCTION; LEX *lex= Lex;
lex->sql_command = SQLCOM_CREATE_SPFUNCTION;
/* Restore flag if it was cleared above */
if (lex->sphead->m_old_cmq)
YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES;
} }
; ;
...@@ -1140,40 +1166,37 @@ sp_proc_stmt: ...@@ -1140,40 +1166,37 @@ sp_proc_stmt:
if (lex->sql_command == SQLCOM_SELECT && !lex->result) if (lex->sql_command == SQLCOM_SELECT && !lex->result)
{ {
send_error(YYTHD, ER_SP_BADSELECT); /* We maybe have one or more SELECT without INTO */
YYABORT; lex->sphead->m_multi_query= TRUE;
} }
else /* Don't add an instruction for empty SET statements.
** (This happens if the SET only contained local variables,
** which get their set instructions generated separately.)
*/
if (lex->sql_command != SQLCOM_SET_OPTION ||
!lex->var_list.is_empty())
{ {
/* Don't add an instruction for empty SET statements. /* Currently we can't handle queries inside a FUNCTION,
** (This happens if the SET only contained local variables, ** because of the way table locking works.
** which get their set instructions generated separately.) ** This is unfortunate, and limits the usefulness of functions
** a great deal, but it's nothing we can do about this at the
** moment.
*/ */
if (lex->sql_command != SQLCOM_SET_OPTION || if (lex->sphead->m_type == TYPE_ENUM_FUNCTION &&
!lex->var_list.is_empty()) lex->sql_command != SQLCOM_SET_OPTION)
{ {
/* Currently we can't handle queries inside a FUNCTION, send_error(YYTHD, ER_SP_BADQUERY);
** because of the way table locking works. YYABORT;
** This is unfortunate, and limits the usefulness of functions }
** a great deal, but it's nothing we can do about this at the else
** moment. {
*/ sp_instr_stmt *i=new sp_instr_stmt(lex->sphead->instructions());
if (lex->sphead->m_type == TYPE_ENUM_FUNCTION &&
lex->sql_command != SQLCOM_SET_OPTION) i->set_lex(lex);
{ lex->sphead->add_instr(i);
send_error(YYTHD, ER_SP_BADQUERY); }
YYABORT; }
} lex->sphead->restore_lex(YYTHD);
else
{
sp_instr_stmt *i= new sp_instr_stmt(lex->sphead->instructions());
i->set_lex(lex);
lex->sphead->add_instr(i);
}
}
lex->sphead->restore_lex(YYTHD);
}
} }
| RETURN_SYM expr | RETURN_SYM expr
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment