diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index cc268101d38..f9df6445749 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -1370,6 +1370,7 @@ static MYSQL_DATA *read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields, if (pkt_len > 1) /* MySQL 4.1 protocol */ { mysql->warning_count= uint2korr(cp+1); + mysql->server_status= uint2korr(cp+3); DBUG_PRINT("info",("warning_count: %ld", mysql->warning_count)); } DBUG_PRINT("exit",("Got %d rows",result->rows)); @@ -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 > 1) /* MySQL 4.1 protocol */ + { mysql->warning_count= uint2korr(mysql->net.read_pos+1); + mysql->server_status= uint2korr(mysql->net.read_pos+3); + } return 1; /* End of data */ } prev_pos= 0; /* allowed to write at packet[-1] */ @@ -5370,6 +5374,7 @@ static MYSQL_DATA *read_binary_rows(MYSQL_STMT *stmt) if (pkt_len > 1) { mysql->warning_count= uint2korr(cp+1); + mysql->server_status= uint2korr(cp+3); DBUG_PRINT("info",("warning_count: %ld", mysql->warning_count)); } DBUG_PRINT("exit",("Got %d rows",result->rows)); diff --git a/mysql-test/r/sp-error.result b/mysql-test/r/sp-error.result index 5e1815d685f..99bdfc25ba8 100644 --- a/mysql-test/r/sp-error.result +++ b/mysql-test/r/sp-error.result @@ -58,9 +58,14 @@ declare y int; set x = y; end; Referring to uninitialized variable y -create procedure foo(x int) -select * from test.t1; +create procedure foo() +begin +select name from mysql.proc; +select type from mysql.proc; +end; +call foo(); SELECT in a stored procedure must have INTO +drop procedure foo; create procedure foo() return 42; RETURN is only allowed in a FUNCTION diff --git a/mysql-test/t/sp-error.test b/mysql-test/t/sp-error.test index cdeca75cd97..93852b2219d 100644 --- a/mysql-test/t/sp-error.test +++ b/mysql-test/t/sp-error.test @@ -89,10 +89,16 @@ begin set x = y; 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 -create procedure foo(x int) - select * from test.t1| +call foo()| +drop procedure foo| # RETURN in FUNCTION only --error 1269 diff --git a/sql/protocol.cc b/sql/protocol.cc index 99b815a7840..eede2496e0c 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -360,7 +360,7 @@ send_eof(THD *thd, bool no_flush) uint tmp= min(thd->total_warn_count, 65535); buff[0]=254; 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(net_flush(net)); } diff --git a/sql/sp_head.cc b/sql/sp_head.cc index a10fc5792dc..82019186a01 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -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) - : Sql_alloc(), m_simple_case(FALSE) + : Sql_alloc(), m_simple_case(FALSE), m_multi_query(FALSE) { DBUG_ENTER("sp_head::sp_head"); const char *dstr = (const char*)lex->buf; diff --git a/sql/sp_head.h b/sql/sp_head.h index afc7822cca3..b79dfdab8e9 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -46,6 +46,8 @@ public: int m_type; // TYPE_ENUM_FUNCTION or TYPE_ENUM_PROCEDURE enum enum_field_types m_returns; // For FUNCTIONs only 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 // We're not using this at the moment. List m_calls; // Called procedures. diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index f3154277d94..b0a183f0711 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3043,8 +3043,8 @@ mysql_execute_command(THD *thd) net_printf(thd, ER_SP_STORE_FAILED, SP_TYPE_STRING(lex), name); goto error; } + break; } - break; case SQLCOM_CALL: { sp_head *sp; @@ -3057,17 +3057,40 @@ mysql_execute_command(THD *thd) } else { + uint smrx; + LINT_INIT(smrx); + #ifndef EMBEDDED_LIBRARY // When executing substatements, they're assumed to send_error when // it happens, but not to send_ok. my_bool nsok= thd->net.no_send_ok; - thd->net.no_send_ok= TRUE; #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); + #ifndef EMBEDDED_LIBRARY thd->net.no_send_ok= nsok; #endif + if (sp->m_multi_query) + { + if (! smrx) + thd->server_status &= ~SERVER_MORE_RESULTS_EXISTS; + } sp->destroy(); // QQ Free memory. Remove this when caching!!! @@ -3076,8 +3099,8 @@ mysql_execute_command(THD *thd) else goto error; // Substatement should already have sent error } + break; } - break; case SQLCOM_ALTER_PROCEDURE: case SQLCOM_ALTER_FUNCTION: { @@ -3099,8 +3122,8 @@ mysql_execute_command(THD *thd) sp->destroy(); // QQ Free memory. Remove this when caching!!! send_ok(thd); } + break; } - break; case SQLCOM_DROP_PROCEDURE: case SQLCOM_DROP_FUNCTION: { @@ -3149,8 +3172,8 @@ mysql_execute_command(THD *thd) lex->udf.name.str); goto error; } + break; } - break; default: /* Impossible */ send_ok(thd); break; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 04b4ae07bfc..dba829d2183 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -940,6 +940,14 @@ create: lex->spcont= new sp_pcontext(); lex->sphead= new sp_head(&$3, lex); 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 ')' { @@ -947,7 +955,12 @@ create: } 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: lex->spcont= new sp_pcontext(); lex->sphead= new sp_head(&lex->udf.name, lex); 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 ')' { @@ -987,7 +1008,12 @@ create_function_tail: } 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: if (lex->sql_command == SQLCOM_SELECT && !lex->result) { - send_error(YYTHD, ER_SP_BADSELECT); - YYABORT; + /* We maybe have one or more SELECT without INTO */ + 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. - ** (This happens if the SET only contained local variables, - ** which get their set instructions generated separately.) + /* Currently we can't handle queries inside a FUNCTION, + ** because of the way table locking works. + ** 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 || - !lex->var_list.is_empty()) + if (lex->sphead->m_type == TYPE_ENUM_FUNCTION && + lex->sql_command != SQLCOM_SET_OPTION) { - /* Currently we can't handle queries inside a FUNCTION, - ** because of the way table locking works. - ** 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->sphead->m_type == TYPE_ENUM_FUNCTION && - lex->sql_command != SQLCOM_SET_OPTION) - { - send_error(YYTHD, ER_SP_BADQUERY); - YYABORT; - } - else - { - sp_instr_stmt *i= new sp_instr_stmt(lex->sphead->instructions()); + send_error(YYTHD, ER_SP_BADQUERY); + YYABORT; + } + 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); - } + i->set_lex(lex); + lex->sphead->add_instr(i); + } + } + lex->sphead->restore_lex(YYTHD); } | RETURN_SYM expr {