From 3b47587f419f70687d840c522f4a80e7762b9183 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Fri, 1 Mar 2019 23:55:55 +0400 Subject: [PATCH] MDEV-18789 Port "MDEV-7773 Aggregate stored functions" to sql_yacc_ora.yy --- .../r/custom_aggregate_functions.result | 58 ++++++++++++++++ .../oracle/t/custom_aggregate_functions.test | 66 ++++++++++++++++++ sql/sql_lex.cc | 9 +++ sql/sql_lex.h | 1 + sql/sql_yacc.yy | 19 ++---- sql/sql_yacc_ora.yy | 68 +++++++++++++++---- 6 files changed, 195 insertions(+), 26 deletions(-) create mode 100644 mysql-test/suite/compat/oracle/r/custom_aggregate_functions.result create mode 100644 mysql-test/suite/compat/oracle/t/custom_aggregate_functions.test diff --git a/mysql-test/suite/compat/oracle/r/custom_aggregate_functions.result b/mysql-test/suite/compat/oracle/r/custom_aggregate_functions.result new file mode 100644 index 00000000000..413eca1b02f --- /dev/null +++ b/mysql-test/suite/compat/oracle/r/custom_aggregate_functions.result @@ -0,0 +1,58 @@ +SET sql_mode=ORACLE; +create aggregate function f1(x INT) return INT AS +begin +insert into t1(sal) values (x); +return x; +end| +ERROR HY000: Aggregate specific instruction(FETCH GROUP NEXT ROW) missing from the aggregate function +create function f1(x INT) return INT AS +begin +set x=5; +fetch group next row; +return x+1; +end | +ERROR HY000: Non-aggregate function contains aggregate specific instructions: (FETCH GROUP NEXT ROW) +CREATE TABLE marks(stud_id INT, grade_count INT); +INSERT INTO marks VALUES (1,6), (2,4), (3,7), (4,5), (5,8); +SELECT * FROM marks; +stud_id grade_count +1 6 +2 4 +3 7 +4 5 +5 8 +# Using PL/SQL syntax: EXCEPTION WHEN NO_DATA_FOUND +CREATE AGGREGATE FUNCTION IF NOT EXISTS aggregate_count(x INT) RETURN INT AS +count_students INT DEFAULT 0; +BEGIN +LOOP +FETCH GROUP NEXT ROW; +IF x THEN +count_students:= count_students + 1; +END IF; +END LOOP; +EXCEPTION +WHEN NO_DATA_FOUND THEN +RETURN count_students; +END aggregate_count // +SELECT aggregate_count(stud_id) FROM marks; +aggregate_count(stud_id) +5 +DROP FUNCTION IF EXISTS aggregate_count; +# Using SQL/PSM systax: CONTINUE HANDLER +CREATE AGGREGATE FUNCTION IF NOT EXISTS aggregate_count(x INT) RETURN INT AS +count_students INT DEFAULT 0; +CONTINUE HANDLER FOR NOT FOUND RETURN count_students; +BEGIN +LOOP +FETCH GROUP NEXT ROW; +IF x THEN +SET count_students= count_students + 1; +END IF; +END LOOP; +END // +SELECT aggregate_count(stud_id) FROM marks; +aggregate_count(stud_id) +5 +DROP FUNCTION IF EXISTS aggregate_count; +DROP TABLE marks; diff --git a/mysql-test/suite/compat/oracle/t/custom_aggregate_functions.test b/mysql-test/suite/compat/oracle/t/custom_aggregate_functions.test new file mode 100644 index 00000000000..2d42722a1d9 --- /dev/null +++ b/mysql-test/suite/compat/oracle/t/custom_aggregate_functions.test @@ -0,0 +1,66 @@ +SET sql_mode=ORACLE; + +delimiter |; +--error ER_INVALID_AGGREGATE_FUNCTION +create aggregate function f1(x INT) return INT AS +begin + insert into t1(sal) values (x); + return x; +end| + +--error ER_NOT_AGGREGATE_FUNCTION +create function f1(x INT) return INT AS +begin + set x=5; + fetch group next row; +return x+1; +end | + +DELIMITER ;| + + +CREATE TABLE marks(stud_id INT, grade_count INT); +INSERT INTO marks VALUES (1,6), (2,4), (3,7), (4,5), (5,8); +SELECT * FROM marks; + +--echo # Using PL/SQL syntax: EXCEPTION WHEN NO_DATA_FOUND + +DELIMITER //; +CREATE AGGREGATE FUNCTION IF NOT EXISTS aggregate_count(x INT) RETURN INT AS + count_students INT DEFAULT 0; +BEGIN + LOOP + FETCH GROUP NEXT ROW; + IF x THEN + count_students:= count_students + 1; + END IF; + END LOOP; +EXCEPTION + WHEN NO_DATA_FOUND THEN + RETURN count_students; +END aggregate_count // +DELIMITER ;// +SELECT aggregate_count(stud_id) FROM marks; +DROP FUNCTION IF EXISTS aggregate_count; + + +--echo # Using SQL/PSM systax: CONTINUE HANDLER + +DELIMITER //; +CREATE AGGREGATE FUNCTION IF NOT EXISTS aggregate_count(x INT) RETURN INT AS + count_students INT DEFAULT 0; + CONTINUE HANDLER FOR NOT FOUND RETURN count_students; +BEGIN + LOOP + FETCH GROUP NEXT ROW; + IF x THEN + SET count_students= count_students + 1; + END IF; + END LOOP; +END // +DELIMITER ;// +SELECT aggregate_count(stud_id) FROM marks; +DROP FUNCTION IF EXISTS aggregate_count; + + +DROP TABLE marks; diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index e0a38c7521e..53b40aabbc8 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -8186,6 +8186,15 @@ bool LEX::sp_add_cfetch(THD *thd, const LEX_CSTRING *name) } +bool LEX::sp_add_agg_cfetch() +{ + sphead->m_flags|= sp_head::HAS_AGGREGATE_INSTR; + sp_instr_agg_cfetch *i= + new (thd->mem_root) sp_instr_agg_cfetch(sphead->instructions(), spcont); + return i == NULL || sphead->add_instr(i); +} + + bool LEX::create_or_alter_view_finalize(THD *thd, Table_ident *table_ident) { sql_command= SQLCOM_CREATE_VIEW; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 88d6310c2df..35b13b37b26 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -4244,6 +4244,7 @@ public: return check_create_options(create_info); } bool sp_add_cfetch(THD *thd, const LEX_CSTRING *name); + bool sp_add_agg_cfetch(); bool set_command_with_check(enum_sql_command command, uint scope, diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index f225bb11763..811d464dba9 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -2060,7 +2060,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); opt_field_or_var_spec fields_or_vars opt_load_data_set_spec view_list_opt view_list view_select trigger_tail sp_tail sf_tail event_tail - udf_tail create_function_tail create_aggregate_function_tail + udf_tail + create_function_tail + create_aggregate_function_tail install uninstall partition_entry binlog_base64_event normal_key_options normal_key_opts all_key_opt spatial_key_options fulltext_key_options normal_key_opt @@ -2975,10 +2977,10 @@ create_function_tail: ; create_aggregate_function_tail: - sf_tail_aggregate - { } + sf_tail_aggregate { } | udf_tail { Lex->udf.type= UDFTYPE_AGGREGATE; } ; + opt_sequence: /* empty */ { } | sequence_defs @@ -4421,15 +4423,8 @@ sp_proc_stmt_fetch: sp_proc_stmt_fetch_head sp_fetch_list { } | FETCH_SYM GROUP_SYM NEXT_SYM ROW_SYM { - LEX *lex= Lex; - sp_head *sp= lex->sphead; - lex->sphead->m_flags|= sp_head::HAS_AGGREGATE_INSTR; - sp_instr_agg_cfetch *i= - new (thd->mem_root) sp_instr_agg_cfetch(sp->instructions(), - lex->spcont); - if (unlikely(i == NULL) || - unlikely(sp->add_instr(i))) - MYSQL_YYABORT; + if (unlikely(Lex->sp_add_agg_cfetch())) + MYSQL_YYABORT; } ; diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy index 2ebabf67720..333e1f76343 100644 --- a/sql/sql_yacc_ora.yy +++ b/sql/sql_yacc_ora.yy @@ -1563,7 +1563,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); opt_field_or_var_spec fields_or_vars opt_load_data_set_spec view_list_opt view_list view_select trigger_tail sp_tail sf_tail event_tail - udf_tail create_function_tail + udf_tail + create_function_tail_standalone + create_aggregate_function_tail_standalone install uninstall partition_entry binlog_base64_event normal_key_options normal_key_opts all_key_opt spatial_key_options fulltext_key_options normal_key_opt @@ -2403,13 +2405,23 @@ create: { Lex->pop_select(); //main select } + | create_or_replace definer AGGREGATE_SYM FUNCTION_SYM + { + if (Lex->main_select_push()) + MYSQL_YYABORT; + Lex->create_info.set($1); + } + sf_tail_aggregate_standalone + { + Lex->pop_select(); //main select + } | create_or_replace no_definer FUNCTION_SYM { if (Lex->main_select_push()) MYSQL_YYABORT; Lex->create_info.set($1); } - create_function_tail + create_function_tail_standalone { Lex->pop_select(); //main select } @@ -2418,9 +2430,8 @@ create: if (Lex->main_select_push()) MYSQL_YYABORT; Lex->create_info.set($1); - Lex->udf.type= UDFTYPE_AGGREGATE; } - udf_tail + create_aggregate_function_tail_standalone { Lex->pop_select(); //main select } @@ -2502,6 +2513,39 @@ create: } ; +sf_tail_not_aggregate_standalone: + sf_tail_standalone + { + if (unlikely(Lex->sphead->m_flags & sp_head::HAS_AGGREGATE_INSTR)) + { + my_yyabort_error((ER_NOT_AGGREGATE_FUNCTION, MYF(0))); + } + Lex->sphead->set_chistics_agg_type(NOT_AGGREGATE); + } + ; + +sf_tail_aggregate_standalone: + sf_tail_standalone + { + if (unlikely(!(Lex->sphead->m_flags & sp_head::HAS_AGGREGATE_INSTR))) + { + my_yyabort_error((ER_INVALID_AGGREGATE_FUNCTION, MYF(0))); + } + Lex->sphead->set_chistics_agg_type(GROUP_AGGREGATE); + } + ; + +create_function_tail_standalone: + sf_tail_not_aggregate_standalone { } + | udf_tail { Lex->udf.type= UDFTYPE_FUNCTION; } + ; + + +create_aggregate_function_tail_standalone: + sf_tail_aggregate_standalone { } + | udf_tail { Lex->udf.type= UDFTYPE_AGGREGATE; } + ; + package_implementation_executable_section: END { @@ -2696,10 +2740,6 @@ package_specification_element: } ; -create_function_tail: - sf_tail_standalone { } - | udf_tail { Lex->udf.type= UDFTYPE_FUNCTION; } - ; opt_sequence: /* empty */ { } @@ -4281,7 +4321,12 @@ sp_proc_stmt_fetch_head: ; sp_proc_stmt_fetch: - sp_proc_stmt_fetch_head sp_fetch_list { } + sp_proc_stmt_fetch_head sp_fetch_list { } + | FETCH_SYM GROUP_SYM NEXT_SYM ROW_SYM + { + if (unlikely(Lex->sp_add_agg_cfetch())) + MYSQL_YYABORT; + } ; sp_proc_stmt_close: @@ -18008,11 +18053,6 @@ sf_tail: { if (unlikely(Lex->sp_body_finalize_function(thd))) MYSQL_YYABORT; - if (unlikely(Lex->sphead->m_flags & sp_head::HAS_AGGREGATE_INSTR)) - { - my_yyabort_error((ER_NOT_AGGREGATE_FUNCTION, MYF(0))); - } - Lex->sphead->set_chistics_agg_type(NOT_AGGREGATE); } ;