Commit 5f34513c authored by Alexander Barkov's avatar Alexander Barkov

MDEV-18813 PROCEDURE and anonymous blocks silently ignore FETCH GROUP NEXT ROW

Part#2 (final): rewritting the code to pass the correct enum_sp_aggregate_type
to the sp_head constructor, so sp_head never changes its aggregation type
later on. The grammar has been simplified and defragmented.
This allowed to check aggregate specific instructions right after
a routine body has been scanned, by calling new LEX methods:
  sp_body_finalize_{procedure|function|trigger|event}()

Moving some C++ code from *.yy to a few new helper methods in LEX.
parent a71d185a
...@@ -37,7 +37,7 @@ set x=5; ...@@ -37,7 +37,7 @@ set x=5;
fetch group next row; fetch group next row;
return x+1; return x+1;
end | end |
ERROR HY000: Non-aggregate function contains aggregate specific instructions: (FETCH GROUP NEXT ROW) ERROR HY000: Aggregate specific instruction (FETCH GROUP NEXT ROW) used in a wrong context
create aggregate function f1(x INT) returns INT create aggregate function f1(x INT) returns INT
begin begin
declare continue handler for not found return x; declare continue handler for not found return x;
...@@ -1153,3 +1153,36 @@ i sum(i) ...@@ -1153,3 +1153,36 @@ i sum(i)
NULL 8 NULL 8
drop function agg_sum; drop function agg_sum;
drop table t1; drop table t1;
#
# MDEV-18813 PROCEDURE and anonymous blocks silently ignore FETCH GROUP NEXT ROW
#
CREATE PROCEDURE p1()
BEGIN
FETCH GROUP NEXT ROW;
END;
$$
ERROR HY000: Aggregate specific instruction (FETCH GROUP NEXT ROW) used in a wrong context
BEGIN NOT ATOMIC
FETCH GROUP NEXT ROW;
END;
$$
ERROR HY000: Aggregate specific instruction (FETCH GROUP NEXT ROW) used in a wrong context
CREATE DEFINER=root@localhost FUNCTION f1() RETURNS INT
BEGIN
FETCH GROUP NEXT ROW;
RETURN 0;
END;
$$
ERROR HY000: Aggregate specific instruction (FETCH GROUP NEXT ROW) used in a wrong context
CREATE TABLE t1 (a INT);
CREATE TRIGGER tr1
AFTER INSERT ON t1 FOR EACH ROW
FETCH GROUP NEXT ROW;
ERROR HY000: Aggregate specific instruction (FETCH GROUP NEXT ROW) used in a wrong context
DROP TABLE t1;
CREATE EVENT ev1
ON SCHEDULE EVERY 1 HOUR
STARTS CURRENT_TIMESTAMP + INTERVAL 1 MONTH
ENDS CURRENT_TIMESTAMP + INTERVAL 1 MONTH + INTERVAL 1 WEEK
DO FETCH GROUP NEXT ROW;
ERROR HY000: Aggregate specific instruction (FETCH GROUP NEXT ROW) used in a wrong context
...@@ -965,3 +965,54 @@ select i, sum(i) from t1 group by i with rollup; ...@@ -965,3 +965,54 @@ select i, sum(i) from t1 group by i with rollup;
# Cleanup # Cleanup
drop function agg_sum; drop function agg_sum;
drop table t1; drop table t1;
--echo #
--echo # MDEV-18813 PROCEDURE and anonymous blocks silently ignore FETCH GROUP NEXT ROW
--echo #
DELIMITER $$;
--error ER_NOT_AGGREGATE_FUNCTION
CREATE PROCEDURE p1()
BEGIN
FETCH GROUP NEXT ROW;
END;
$$
DELIMITER ;$$
DELIMITER $$;
--error ER_NOT_AGGREGATE_FUNCTION
BEGIN NOT ATOMIC
FETCH GROUP NEXT ROW;
END;
$$
DELIMITER ;$$
DELIMITER $$;
--error ER_NOT_AGGREGATE_FUNCTION
CREATE DEFINER=root@localhost FUNCTION f1() RETURNS INT
BEGIN
FETCH GROUP NEXT ROW;
RETURN 0;
END;
$$
DELIMITER ;$$
CREATE TABLE t1 (a INT);
--error ER_NOT_AGGREGATE_FUNCTION
CREATE TRIGGER tr1
AFTER INSERT ON t1 FOR EACH ROW
FETCH GROUP NEXT ROW;
DROP TABLE t1;
--error ER_NOT_AGGREGATE_FUNCTION
CREATE EVENT ev1
ON SCHEDULE EVERY 1 HOUR
STARTS CURRENT_TIMESTAMP + INTERVAL 1 MONTH
ENDS CURRENT_TIMESTAMP + INTERVAL 1 MONTH + INTERVAL 1 WEEK
DO FETCH GROUP NEXT ROW;
...@@ -5170,7 +5170,7 @@ RETURN CONCAT(']]>, ', c1, '!'); ...@@ -5170,7 +5170,7 @@ RETURN CONCAT(']]>, ', c1, '!');
<routines> <routines>
<routine Function="straße" sql_mode="" character_set_client="utf8" collation_connection="utf8_general_ci" Database_Collation="latin1_swedish_ci"> <routine Function="straße" sql_mode="" character_set_client="utf8" collation_connection="utf8_general_ci" Database_Collation="latin1_swedish_ci">
<![CDATA[ <![CDATA[
CREATE DEFINER=`root`@`localhost` FUNCTION `straße`( c1 CHAR(20)) RETURNS char(50) CHARSET latin1 CREATE DEFINER=`root`@`localhost` FUNCTION `straße`(c1 CHAR(20)) RETURNS char(50) CHARSET latin1
DETERMINISTIC DETERMINISTIC
RETURN CONCAT(']]]]><![CDATA[>, ', c1, '!') RETURN CONCAT(']]]]><![CDATA[>, ', c1, '!')
]]> ]]>
......
...@@ -11,7 +11,7 @@ set x=5; ...@@ -11,7 +11,7 @@ set x=5;
fetch group next row; fetch group next row;
return x+1; return x+1;
end | end |
ERROR HY000: Non-aggregate function contains aggregate specific instructions: (FETCH GROUP NEXT ROW) ERROR HY000: Aggregate specific instruction (FETCH GROUP NEXT ROW) used in a wrong context
CREATE TABLE marks(stud_id INT, grade_count INT); CREATE TABLE marks(stud_id INT, grade_count INT);
INSERT INTO marks VALUES (1,6), (2,4), (3,7), (4,5), (5,8); INSERT INTO marks VALUES (1,6), (2,4), (3,7), (4,5), (5,8);
SELECT * FROM marks; SELECT * FROM marks;
...@@ -56,3 +56,81 @@ aggregate_count(stud_id) ...@@ -56,3 +56,81 @@ aggregate_count(stud_id)
5 5
DROP FUNCTION IF EXISTS aggregate_count; DROP FUNCTION IF EXISTS aggregate_count;
DROP TABLE marks; DROP TABLE marks;
#
# MDEV-18813 PROCEDURE and anonymous blocks silently ignore FETCH GROUP NEXT ROW
#
CREATE PROCEDURE p1 AS
BEGIN
FETCH GROUP NEXT ROW;
END;
$$
ERROR HY000: Aggregate specific instruction (FETCH GROUP NEXT ROW) used in a wrong context
BEGIN NOT ATOMIC
FETCH GROUP NEXT ROW;
END;
$$
ERROR HY000: Aggregate specific instruction (FETCH GROUP NEXT ROW) used in a wrong context
CREATE DEFINER=root@localhost FUNCTION f1 RETURN INT AS
BEGIN
FETCH GROUP NEXT ROW;
RETURN 0;
END;
$$
ERROR HY000: Aggregate specific instruction (FETCH GROUP NEXT ROW) used in a wrong context
CREATE TABLE t1 (a INT);
CREATE TRIGGER tr1
AFTER INSERT ON t1 FOR EACH ROW
FETCH GROUP NEXT ROW;
ERROR HY000: Aggregate specific instruction (FETCH GROUP NEXT ROW) used in a wrong context
DROP TABLE t1;
CREATE EVENT ev1
ON SCHEDULE EVERY 1 HOUR
STARTS CURRENT_TIMESTAMP + INTERVAL 1 MONTH
ENDS CURRENT_TIMESTAMP + INTERVAL 1 MONTH + INTERVAL 1 WEEK
DO FETCH GROUP NEXT ROW;
ERROR HY000: Aggregate specific instruction (FETCH GROUP NEXT ROW) used in a wrong context
CREATE PACKAGE pkg1 AS
PROCEDURE p1;
FUNCTION f1 RETURN INT;
END;
$$
CREATE PACKAGE BODY pkg1 AS
PROCEDURE p1 AS
BEGIN
FETCH GROUP NEXT ROW; -- In a package procedure
END;
FUNCTION f1 RETURN INT AS
BEGIN
RETURN 0;
END;
END;
$$
ERROR HY000: Aggregate specific instruction (FETCH GROUP NEXT ROW) used in a wrong context
CREATE PACKAGE BODY pkg1 AS
PROCEDURE p1 AS
BEGIN
NULL;
END;
FUNCTION f1 RETURN INT AS
BEGIN
FETCH GROUP NEXT ROW; -- In a package function
RETURN 0;
END;
END;
$$
ERROR HY000: Aggregate specific instruction (FETCH GROUP NEXT ROW) used in a wrong context
CREATE PACKAGE BODY pkg1 AS
PROCEDURE p1 AS
BEGIN
NULL;
END;
FUNCTION f1 RETURN INT AS
BEGIN
RETURN 0;
END;
BEGIN
FETCH GROUP NEXT ROW; -- In a package executable section
END;
$$
ERROR HY000: Aggregate specific instruction (FETCH GROUP NEXT ROW) used in a wrong context
DROP PACKAGE pkg1;
...@@ -64,3 +64,107 @@ DROP FUNCTION IF EXISTS aggregate_count; ...@@ -64,3 +64,107 @@ DROP FUNCTION IF EXISTS aggregate_count;
DROP TABLE marks; DROP TABLE marks;
--echo #
--echo # MDEV-18813 PROCEDURE and anonymous blocks silently ignore FETCH GROUP NEXT ROW
--echo #
DELIMITER $$;
--error ER_NOT_AGGREGATE_FUNCTION
CREATE PROCEDURE p1 AS
BEGIN
FETCH GROUP NEXT ROW;
END;
$$
DELIMITER ;$$
DELIMITER $$;
--error ER_NOT_AGGREGATE_FUNCTION
BEGIN NOT ATOMIC
FETCH GROUP NEXT ROW;
END;
$$
DELIMITER ;$$
DELIMITER $$;
--error ER_NOT_AGGREGATE_FUNCTION
CREATE DEFINER=root@localhost FUNCTION f1 RETURN INT AS
BEGIN
FETCH GROUP NEXT ROW;
RETURN 0;
END;
$$
DELIMITER ;$$
CREATE TABLE t1 (a INT);
--error ER_NOT_AGGREGATE_FUNCTION
CREATE TRIGGER tr1
AFTER INSERT ON t1 FOR EACH ROW
FETCH GROUP NEXT ROW;
DROP TABLE t1;
--error ER_NOT_AGGREGATE_FUNCTION
CREATE EVENT ev1
ON SCHEDULE EVERY 1 HOUR
STARTS CURRENT_TIMESTAMP + INTERVAL 1 MONTH
ENDS CURRENT_TIMESTAMP + INTERVAL 1 MONTH + INTERVAL 1 WEEK
DO FETCH GROUP NEXT ROW;
DELIMITER $$;
CREATE PACKAGE pkg1 AS
PROCEDURE p1;
FUNCTION f1 RETURN INT;
END;
$$
--error ER_NOT_AGGREGATE_FUNCTION
CREATE PACKAGE BODY pkg1 AS
PROCEDURE p1 AS
BEGIN
FETCH GROUP NEXT ROW; -- In a package procedure
END;
FUNCTION f1 RETURN INT AS
BEGIN
RETURN 0;
END;
END;
$$
--error ER_NOT_AGGREGATE_FUNCTION
CREATE PACKAGE BODY pkg1 AS
PROCEDURE p1 AS
BEGIN
NULL;
END;
FUNCTION f1 RETURN INT AS
BEGIN
FETCH GROUP NEXT ROW; -- In a package function
RETURN 0;
END;
END;
$$
--error ER_NOT_AGGREGATE_FUNCTION
CREATE PACKAGE BODY pkg1 AS
PROCEDURE p1 AS
BEGIN
NULL;
END;
FUNCTION f1 RETURN INT AS
BEGIN
RETURN 0;
END;
BEGIN
FETCH GROUP NEXT ROW; -- In a package executable section
END;
$$
DELIMITER ;$$
DROP PACKAGE pkg1;
...@@ -7811,7 +7811,7 @@ ER_ARGUMENT_OUT_OF_RANGE ...@@ -7811,7 +7811,7 @@ ER_ARGUMENT_OUT_OF_RANGE
ER_WRONG_TYPE_OF_ARGUMENT ER_WRONG_TYPE_OF_ARGUMENT
eng "%s function only accepts arguments that can be converted to numerical types" eng "%s function only accepts arguments that can be converted to numerical types"
ER_NOT_AGGREGATE_FUNCTION ER_NOT_AGGREGATE_FUNCTION
eng "Non-aggregate function contains aggregate specific instructions: (FETCH GROUP NEXT ROW)" eng "Aggregate specific instruction (FETCH GROUP NEXT ROW) used in a wrong context"
ER_INVALID_AGGREGATE_FUNCTION ER_INVALID_AGGREGATE_FUNCTION
eng "Aggregate specific instruction(FETCH GROUP NEXT ROW) missing from the aggregate function" eng "Aggregate specific instruction(FETCH GROUP NEXT ROW) missing from the aggregate function"
ER_INVALID_VALUE_TO_LIMIT ER_INVALID_VALUE_TO_LIMIT
......
...@@ -489,7 +489,8 @@ sp_head::operator delete(void *ptr, size_t size) throw() ...@@ -489,7 +489,8 @@ sp_head::operator delete(void *ptr, size_t size) throw()
} }
sp_head::sp_head(sp_package *parent, const Sp_handler *sph) sp_head::sp_head(sp_package *parent, const Sp_handler *sph,
enum_sp_aggregate_type agg_type)
:Query_arena(&main_mem_root, STMT_INITIALIZED_FOR_SP), :Query_arena(&main_mem_root, STMT_INITIALIZED_FOR_SP),
Database_qualified_name(&null_clex_str, &null_clex_str), Database_qualified_name(&null_clex_str, &null_clex_str),
m_parent(parent), m_parent(parent),
...@@ -522,6 +523,7 @@ sp_head::sp_head(sp_package *parent, const Sp_handler *sph) ...@@ -522,6 +523,7 @@ sp_head::sp_head(sp_package *parent, const Sp_handler *sph)
m_pcont(new (&main_mem_root) sp_pcontext()), m_pcont(new (&main_mem_root) sp_pcontext()),
m_cont_level(0) m_cont_level(0)
{ {
set_chistics_agg_type(agg_type);
m_first_instance= this; m_first_instance= this;
m_first_free_instance= this; m_first_free_instance= this;
m_last_cached_sp= this; m_last_cached_sp= this;
...@@ -547,7 +549,7 @@ sp_head::sp_head(sp_package *parent, const Sp_handler *sph) ...@@ -547,7 +549,7 @@ sp_head::sp_head(sp_package *parent, const Sp_handler *sph)
sp_package::sp_package(LEX *top_level_lex, sp_package::sp_package(LEX *top_level_lex,
const sp_name *name, const sp_name *name,
const Sp_handler *sph) const Sp_handler *sph)
:sp_head(NULL, sph), :sp_head(NULL, sph, DEFAULT_AGGREGATE),
m_current_routine(NULL), m_current_routine(NULL),
m_top_level_lex(top_level_lex), m_top_level_lex(top_level_lex),
m_rcontext(NULL), m_rcontext(NULL),
...@@ -2681,6 +2683,17 @@ sp_head::set_chistics(const st_sp_chistics &chistics) ...@@ -2681,6 +2683,17 @@ sp_head::set_chistics(const st_sp_chistics &chistics)
m_chistics.comment.length); m_chistics.comment.length);
} }
void
sp_head::set_c_chistics(const st_sp_chistics &chistics)
{
// Set all chistics but preserve agg_type.
enum_sp_aggregate_type save_agg_type= agg_type();
set_chistics(chistics);
set_chistics_agg_type(save_agg_type);
}
void void
sp_head::set_info(longlong created, longlong modified, sp_head::set_info(longlong created, longlong modified,
const st_sp_chistics &chistics, sql_mode_t sql_mode) const st_sp_chistics &chistics, sql_mode_t sql_mode)
...@@ -5134,6 +5147,36 @@ bool sp_head::spvar_fill_table_rowtype_reference(THD *thd, ...@@ -5134,6 +5147,36 @@ bool sp_head::spvar_fill_table_rowtype_reference(THD *thd,
} }
bool sp_head::check_group_aggregate_instructions_forbid() const
{
if (unlikely(m_flags & sp_head::HAS_AGGREGATE_INSTR))
{
my_error(ER_NOT_AGGREGATE_FUNCTION, MYF(0));
return true;
}
return false;
}
bool sp_head::check_group_aggregate_instructions_require() const
{
if (unlikely(!(m_flags & HAS_AGGREGATE_INSTR)))
{
my_error(ER_INVALID_AGGREGATE_FUNCTION, MYF(0));
return true;
}
return false;
}
bool sp_head::check_group_aggregate_instructions_function() const
{
return agg_type() == GROUP_AGGREGATE ?
check_group_aggregate_instructions_require() :
check_group_aggregate_instructions_forbid();
}
/* /*
In Oracle mode stored routines have an optional name In Oracle mode stored routines have an optional name
at the end of a declaration: at the end of a declaration:
......
...@@ -183,6 +183,11 @@ class sp_head :private Query_arena, ...@@ -183,6 +183,11 @@ class sp_head :private Query_arena,
set_chistics() makes sure this. set_chistics() makes sure this.
*/ */
Sp_chistics m_chistics; Sp_chistics m_chistics;
void set_chistics(const st_sp_chistics &chistics);
inline void set_chistics_agg_type(enum enum_sp_aggregate_type type)
{
m_chistics.agg_type= type;
}
public: public:
sql_mode_t m_sql_mode; ///< For SHOW CREATE and execution sql_mode_t m_sql_mode; ///< For SHOW CREATE and execution
bool m_explicit_name; /**< Prepend the db name? */ bool m_explicit_name; /**< Prepend the db name? */
...@@ -319,7 +324,8 @@ class sp_head :private Query_arena, ...@@ -319,7 +324,8 @@ class sp_head :private Query_arena,
static void static void
operator delete(void *ptr, size_t size) throw (); operator delete(void *ptr, size_t size) throw ();
sp_head(sp_package *parent, const Sp_handler *handler); sp_head(sp_package *parent, const Sp_handler *handler,
enum_sp_aggregate_type);
/// Initialize after we have reset mem_root /// Initialize after we have reset mem_root
void void
...@@ -413,6 +419,9 @@ class sp_head :private Query_arena, ...@@ -413,6 +419,9 @@ class sp_head :private Query_arena,
Item *val, LEX *lex); Item *val, LEX *lex);
bool check_package_routine_end_name(const LEX_CSTRING &end_name) const; bool check_package_routine_end_name(const LEX_CSTRING &end_name) const;
bool check_standalone_routine_end_name(const sp_name *end_name) const; bool check_standalone_routine_end_name(const sp_name *end_name) const;
bool check_group_aggregate_instructions_function() const;
bool check_group_aggregate_instructions_forbid() const;
bool check_group_aggregate_instructions_require() const;
private: private:
/** /**
Generate a code to set a single cursor parameter variable. Generate a code to set a single cursor parameter variable.
...@@ -730,11 +739,7 @@ class sp_head :private Query_arena, ...@@ -730,11 +739,7 @@ class sp_head :private Query_arena,
const LEX_CSTRING &db, const LEX_CSTRING &db,
const LEX_CSTRING &table); const LEX_CSTRING &table);
void set_chistics(const st_sp_chistics &chistics); void set_c_chistics(const st_sp_chistics &chistics);
inline void set_chistics_agg_type(enum enum_sp_aggregate_type type)
{
m_chistics.agg_type= type;
}
void set_info(longlong created, longlong modified, void set_info(longlong created, longlong modified,
const st_sp_chistics &chistics, sql_mode_t sql_mode); const st_sp_chistics &chistics, sql_mode_t sql_mode);
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include "sql_admin.h" // Sql_cmd_analyze/Check..._table #include "sql_admin.h" // Sql_cmd_analyze/Check..._table
#include "sql_partition.h" #include "sql_partition.h"
#include "sql_partition_admin.h" // Sql_cmd_alter_table_*_part #include "sql_partition_admin.h" // Sql_cmd_alter_table_*_part
#include "event_parse_data.h"
void LEX::parse_error(uint err_number) void LEX::parse_error(uint err_number)
{ {
...@@ -6469,13 +6470,14 @@ sp_name *LEX::make_sp_name(THD *thd, const LEX_CSTRING *name1, ...@@ -6469,13 +6470,14 @@ sp_name *LEX::make_sp_name(THD *thd, const LEX_CSTRING *name1,
sp_head *LEX::make_sp_head(THD *thd, const sp_name *name, sp_head *LEX::make_sp_head(THD *thd, const sp_name *name,
const Sp_handler *sph) const Sp_handler *sph,
enum_sp_aggregate_type agg_type)
{ {
sp_package *package= get_sp_package(); sp_package *package= get_sp_package();
sp_head *sp; sp_head *sp;
/* Order is important here: new - reset - init */ /* Order is important here: new - reset - init */
if (likely((sp= new sp_head(package, sph)))) if (likely((sp= new sp_head(package, sph, agg_type))))
{ {
sp->reset_thd_mem_root(thd); sp->reset_thd_mem_root(thd);
sp->init(this); sp->init(this);
...@@ -6498,7 +6500,8 @@ sp_head *LEX::make_sp_head(THD *thd, const sp_name *name, ...@@ -6498,7 +6500,8 @@ sp_head *LEX::make_sp_head(THD *thd, const sp_name *name,
sp_head *LEX::make_sp_head_no_recursive(THD *thd, const sp_name *name, sp_head *LEX::make_sp_head_no_recursive(THD *thd, const sp_name *name,
const Sp_handler *sph) const Sp_handler *sph,
enum_sp_aggregate_type agg_type)
{ {
sp_package *package= thd->lex->get_sp_package(); sp_package *package= thd->lex->get_sp_package();
/* /*
...@@ -6516,13 +6519,13 @@ sp_head *LEX::make_sp_head_no_recursive(THD *thd, const sp_name *name, ...@@ -6516,13 +6519,13 @@ sp_head *LEX::make_sp_head_no_recursive(THD *thd, const sp_name *name,
(package && (package &&
(sph == &sp_handler_package_procedure || (sph == &sp_handler_package_procedure ||
sph == &sp_handler_package_function))) sph == &sp_handler_package_function)))
return make_sp_head(thd, name, sph); return make_sp_head(thd, name, sph, agg_type);
my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), sph->type_str()); my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), sph->type_str());
return NULL; return NULL;
} }
bool LEX::sp_body_finalize_procedure(THD *thd) bool LEX::sp_body_finalize_routine(THD *thd)
{ {
if (sphead->check_unresolved_goto()) if (sphead->check_unresolved_goto())
return true; return true;
...@@ -6532,6 +6535,13 @@ bool LEX::sp_body_finalize_procedure(THD *thd) ...@@ -6532,6 +6535,13 @@ bool LEX::sp_body_finalize_procedure(THD *thd)
} }
bool LEX::sp_body_finalize_procedure(THD *thd)
{
return sphead->check_group_aggregate_instructions_forbid() ||
sp_body_finalize_routine(thd);
}
bool LEX::sp_body_finalize_procedure_standalone(THD *thd, bool LEX::sp_body_finalize_procedure_standalone(THD *thd,
const sp_name *end_name) const sp_name *end_name)
{ {
...@@ -6542,25 +6552,41 @@ bool LEX::sp_body_finalize_procedure_standalone(THD *thd, ...@@ -6542,25 +6552,41 @@ bool LEX::sp_body_finalize_procedure_standalone(THD *thd,
bool LEX::sp_body_finalize_function(THD *thd) bool LEX::sp_body_finalize_function(THD *thd)
{ {
if (sphead->is_not_allowed_in_function("function")) if (sphead->is_not_allowed_in_function("function") ||
sphead->check_group_aggregate_instructions_function())
return true; return true;
if (!(sphead->m_flags & sp_head::HAS_RETURN)) if (!(sphead->m_flags & sp_head::HAS_RETURN))
{ {
my_error(ER_SP_NORETURN, MYF(0), ErrConvDQName(sphead).ptr()); my_error(ER_SP_NORETURN, MYF(0), ErrConvDQName(sphead).ptr());
return true; return true;
} }
if (sp_body_finalize_procedure(thd)) if (sp_body_finalize_routine(thd))
return true; return true;
(void) is_native_function_with_warn(thd, &sphead->m_name); (void) is_native_function_with_warn(thd, &sphead->m_name);
return false; return false;
} }
bool LEX::sp_body_finalize_function_standalone(THD *thd, bool LEX::sp_body_finalize_trigger(THD *thd)
const sp_name *end_name)
{ {
return sp_body_finalize_function(thd) || return sphead->is_not_allowed_in_function("trigger") ||
sphead->check_standalone_routine_end_name(end_name); sp_body_finalize_procedure(thd);
}
bool LEX::sp_body_finalize_event(THD *thd)
{
event_parse_data->body_changed= true;
return sp_body_finalize_procedure(thd);
}
bool LEX::stmt_create_stored_function_finalize_standalone(const sp_name *end_name)
{
if (sphead->check_standalone_routine_end_name(end_name))
return true;
stmt_create_routine_finalize();
return false;
} }
...@@ -6855,7 +6881,7 @@ bool LEX::maybe_start_compound_statement(THD *thd) ...@@ -6855,7 +6881,7 @@ bool LEX::maybe_start_compound_statement(THD *thd)
{ {
if (!sphead) if (!sphead)
{ {
if (!make_sp_head(thd, NULL, &sp_handler_procedure)) if (!make_sp_head(thd, NULL, &sp_handler_procedure, DEFAULT_AGGREGATE))
return true; return true;
sphead->set_suid(SP_IS_NOT_SUID); sphead->set_suid(SP_IS_NOT_SUID);
sphead->set_body_start(thd, thd->m_parser_state->m_lip.get_cpp_ptr()); sphead->set_body_start(thd, thd->m_parser_state->m_lip.get_cpp_ptr());
...@@ -8376,6 +8402,7 @@ bool LEX::create_package_finalize(THD *thd, ...@@ -8376,6 +8402,7 @@ bool LEX::create_package_finalize(THD *thd,
exp ? ErrConvDQName(name).ptr() : name->m_name.str); exp ? ErrConvDQName(name).ptr() : name->m_name.str);
return true; return true;
} }
// TODO: reuse code in LEX::create_package_finalize and sp_head::set_stmt_end
sphead->m_body.length= body_end - body_start; sphead->m_body.length= body_end - body_start;
if (unlikely(!(sphead->m_body.str= thd->strmake(body_start, if (unlikely(!(sphead->m_body.str= thd->strmake(body_start,
sphead->m_body.length)))) sphead->m_body.length))))
...@@ -8390,7 +8417,8 @@ bool LEX::create_package_finalize(THD *thd, ...@@ -8390,7 +8417,8 @@ bool LEX::create_package_finalize(THD *thd,
sphead->restore_thd_mem_root(thd); sphead->restore_thd_mem_root(thd);
sp_package *pkg= sphead->get_package(); sp_package *pkg= sphead->get_package();
DBUG_ASSERT(pkg); DBUG_ASSERT(pkg);
return pkg->validate_after_parser(thd); return sphead->check_group_aggregate_instructions_forbid() ||
pkg->validate_after_parser(thd);
} }
...@@ -10326,3 +10354,40 @@ bool LEX::stmt_purge_before(Item *item) ...@@ -10326,3 +10354,40 @@ bool LEX::stmt_purge_before(Item *item)
value_list.push_front(item, thd->mem_root); value_list.push_front(item, thd->mem_root);
return check_main_unit_semantics(); return check_main_unit_semantics();
} }
bool LEX::stmt_create_udf_function(const DDL_options_st &options,
enum_sp_aggregate_type agg_type,
const Lex_ident_sys_st &name,
Item_result return_type,
const LEX_CSTRING &soname)
{
if (stmt_create_function_start(options))
return true;
if (unlikely(is_native_function(thd, &name)))
{
my_error(ER_NATIVE_FCT_NAME_COLLISION, MYF(0), name.str);
return true;
}
sql_command= SQLCOM_CREATE_FUNCTION;
udf.name= name;
udf.returns= return_type;
udf.dl= soname.str;
udf.type= agg_type == GROUP_AGGREGATE ? UDFTYPE_AGGREGATE :
UDFTYPE_FUNCTION;
stmt_create_routine_finalize();
return false;
}
bool LEX::stmt_create_stored_function_start(const DDL_options_st &options,
enum_sp_aggregate_type agg_type,
const sp_name *spname)
{
if (stmt_create_function_start(options) ||
unlikely(!make_sp_head_no_recursive(thd, spname,
&sp_handler_function, agg_type)))
return true;
return false;
}
...@@ -241,6 +241,14 @@ enum enum_sp_suid_behaviour ...@@ -241,6 +241,14 @@ enum enum_sp_suid_behaviour
}; };
enum enum_sp_aggregate_type
{
DEFAULT_AGGREGATE= 0,
NOT_AGGREGATE,
GROUP_AGGREGATE
};
/* These may not be declared yet */ /* These may not be declared yet */
class Table_ident; class Table_ident;
class sql_exchange; class sql_exchange;
...@@ -371,13 +379,6 @@ enum enum_sp_data_access ...@@ -371,13 +379,6 @@ enum enum_sp_data_access
SP_MODIFIES_SQL_DATA SP_MODIFIES_SQL_DATA
}; };
enum enum_sp_aggregate_type
{
DEFAULT_AGGREGATE= 0,
NOT_AGGREGATE,
GROUP_AGGREGATE
};
const LEX_CSTRING sp_data_access_name[]= const LEX_CSTRING sp_data_access_name[]=
{ {
{ STRING_WITH_LEN("") }, { STRING_WITH_LEN("") },
...@@ -3734,12 +3735,16 @@ struct LEX: public Query_tables_list ...@@ -3734,12 +3735,16 @@ struct LEX: public Query_tables_list
sp_name *make_sp_name(THD *thd, const LEX_CSTRING *name1, sp_name *make_sp_name(THD *thd, const LEX_CSTRING *name1,
const LEX_CSTRING *name2); const LEX_CSTRING *name2);
sp_name *make_sp_name_package_routine(THD *thd, const LEX_CSTRING *name); sp_name *make_sp_name_package_routine(THD *thd, const LEX_CSTRING *name);
sp_head *make_sp_head(THD *thd, const sp_name *name, const Sp_handler *sph); sp_head *make_sp_head(THD *thd, const sp_name *name, const Sp_handler *sph,
enum_sp_aggregate_type agg_type);
sp_head *make_sp_head_no_recursive(THD *thd, const sp_name *name, sp_head *make_sp_head_no_recursive(THD *thd, const sp_name *name,
const Sp_handler *sph); const Sp_handler *sph,
enum_sp_aggregate_type agg_type);
bool sp_body_finalize_routine(THD *);
bool sp_body_finalize_trigger(THD *);
bool sp_body_finalize_event(THD *);
bool sp_body_finalize_function(THD *); bool sp_body_finalize_function(THD *);
bool sp_body_finalize_procedure(THD *); bool sp_body_finalize_procedure(THD *);
bool sp_body_finalize_function_standalone(THD *, const sp_name *end_name);
bool sp_body_finalize_procedure_standalone(THD *, const sp_name *end_name); bool sp_body_finalize_procedure_standalone(THD *, const sp_name *end_name);
sp_package *create_package_start(THD *thd, sp_package *create_package_start(THD *thd,
enum_sql_command command, enum_sql_command command,
...@@ -4502,6 +4507,17 @@ struct LEX: public Query_tables_list ...@@ -4502,6 +4507,17 @@ struct LEX: public Query_tables_list
{ {
pop_select(); // main select pop_select(); // main select
} }
bool stmt_create_stored_function_start(const DDL_options_st &options,
enum_sp_aggregate_type,
const sp_name *name);
bool stmt_create_stored_function_finalize_standalone(const sp_name *end_name);
bool stmt_create_udf_function(const DDL_options_st &options,
enum_sp_aggregate_type agg_type,
const Lex_ident_sys_st &name,
Item_result return_type,
const LEX_CSTRING &soname);
}; };
......
This diff is collapsed.
This diff is collapsed.
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