diff options
author | Alexander Barkov <bar@mariadb.org> | 2016-08-24 07:39:04 +0400 |
---|---|---|
committer | Alexander Barkov <bar@mariadb.org> | 2017-04-05 15:02:46 +0400 |
commit | c570636ba278e935701ca917af966630d38556ab (patch) | |
tree | 856f9cccf65da0ffae4b70def8452fcf25f23182 /sql | |
parent | 71a0a12e61818d68201dfc985a64b58c19cfef44 (diff) | |
download | mariadb-git-c570636ba278e935701ca917af966630d38556ab.tar.gz |
MDEV-10580 sql_mode=ORACLE: FOR loop statement
Adding non-labeled FOR LOOP statement.
Diffstat (limited to 'sql')
-rw-r--r-- | sql/field.h | 13 | ||||
-rw-r--r-- | sql/sql_lex.cc | 124 | ||||
-rw-r--r-- | sql/sql_lex.h | 14 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 2 | ||||
-rw-r--r-- | sql/sql_yacc_ora.yy | 56 | ||||
-rw-r--r-- | sql/structs.h | 10 |
6 files changed, 216 insertions, 3 deletions
diff --git a/sql/field.h b/sql/field.h index caacb5f00b8..89b209379dd 100644 --- a/sql/field.h +++ b/sql/field.h @@ -3850,6 +3850,19 @@ public: interval_list.empty(); } + Column_definition(const char *name, enum_field_types type): + field_name(name), + comment(null_lex_str), + on_update(0), sql_type(type), length(0), decimals(0), + flags(0), pack_length(0), key_length(0), unireg_check(Field::NONE), + interval(0), charset(&my_charset_bin), + srid(0), geom_type(Field::GEOM_GEOMETRY), + option_list(NULL), + vcol_info(0), default_value(0), check_constraint(0) + { + interval_list.empty(); + } + Column_definition(THD *thd, Field *field, Field *orig_field); void set_attributes(const Lex_field_type_st &type, CHARSET_INFO *cs); void create_length_to_internal_length(void); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 093ed597b0a..a47a0fe0a46 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -2007,8 +2007,13 @@ static int lex_one_token(YYSTYPE *yylval, THD *thd) /* Actually real shouldn't start with . but allow them anyhow */ case MY_LEX_REAL_OR_POINT: - if (my_isdigit(cs,lip->yyPeek())) + if (my_isdigit(cs,(c= lip->yyPeek()))) state = MY_LEX_REAL; // Real + else if (c == '.') + { + lip->yySkip(); + return DOT_DOT_SYM; + } else { state= MY_LEX_IDENT_SEP; // return '.' @@ -5243,6 +5248,123 @@ bool LEX::sp_variable_declarations_finalize(THD *thd, int nvars, } +/********************************************************************** + The FOR LOOP statement + + This syntax: + FOR i IN lower_bound .. upper_bound + LOOP + statements; + END LOOP; + + is translated into: + + DECLARE + i INT := lower_bound; + j INT := upper_bound; + BEGIN + WHILE i <= j + LOOP + statements; + i:= i + 1; + END LOOP; + END; +*/ + + +sp_variable *LEX::sp_add_for_loop_variable(THD *thd, const LEX_STRING name, + Item *value) +{ + sp_variable *spvar= spcont->add_variable(thd, name); + spcont->declare_var_boundary(1); + spvar->field_def= Column_definition(spvar->name.str, MYSQL_TYPE_LONGLONG); + if (sp_variable_declarations_finalize(thd, 1, spvar->field_def, value)) + return NULL; + return spvar; +} + + +/** + Generate a code for a FOR loop condition: + - Make Item_splocal for the FOR loop index variable + - Make Item_splocal for the FOR loop upper bound variable + - Make a comparison function item on top of these two variables +*/ +bool LEX::sp_for_loop_condition(THD *thd, const Lex_for_loop_st &loop) +{ + Item_splocal *args[2]; + for (uint i= 0 ; i < 2; i++) + { + sp_variable *src= i == 0 ? loop.m_index : loop.m_upper_bound; + args[i]= new (thd->mem_root) + Item_splocal(thd, src->name, src->offset, src->sql_type()); + if (args[i] == NULL) + return true; +#ifndef DBUG_OFF + args[i]->m_sp= sphead; +#endif + } + + Item *expr= loop.m_direction > 0 ? + (Item *) new (thd->mem_root) Item_func_le(thd, args[0], args[1]) : + (Item *) new (thd->mem_root) Item_func_ge(thd, args[0], args[1]); + return !expr || sp_while_loop_expression(thd, expr); +} + + +/** + Generate the FOR LOOP condition code in its own lex +*/ +bool LEX::sp_for_loop_index_and_bounds(THD *thd, const Lex_for_loop_st &loop) +{ + sphead->reset_lex(thd); + if (thd->lex->sp_for_loop_condition(thd, loop)) + return true; + return thd->lex->sphead->restore_lex(thd); +} + + +/** + Generate a code for a FOR loop index increment +*/ +bool LEX::sp_for_loop_increment(THD *thd, const Lex_for_loop_st &loop) +{ + Item_splocal *splocal= new (thd->mem_root) + Item_splocal(thd, loop.m_index->name, loop.m_index->offset, + loop.m_index->sql_type()); + if (splocal == NULL) + return true; +#ifndef DBUG_OFF + splocal->m_sp= sphead; +#endif + Item_int *inc= new (thd->mem_root) Item_int(thd, loop.m_direction); + if (!inc) + return true; + Item *expr= new (thd->mem_root) Item_func_plus(thd, splocal, inc); + if (!expr || set_local_variable(loop.m_index, expr)) + return true; + return false; +} + + +bool LEX::sp_for_loop_finalize(THD *thd, const Lex_for_loop_st &loop) +{ + sphead->reset_lex(thd); + + // Generate FOR LOOP index increment in its own lex + DBUG_ASSERT(this != thd->lex); + if (thd->lex->sp_for_loop_increment(thd, loop) || + thd->lex->sphead->restore_lex(thd)) + return true; + + // Generate a jump to the beginning of the loop + DBUG_ASSERT(this == thd->lex); + return sp_while_loop_finalize(thd); +} + + +/***************************************************************************/ + bool LEX::sp_declare_cursor(THD *thd, const LEX_STRING name, LEX *cursor_stmt) { uint offp; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 041a0c8f130..0d834c2e1d3 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -2633,6 +2633,10 @@ private: bool sp_change_context(THD *thd, const sp_pcontext *ctx, bool exclusive); bool sp_exit_block(THD *thd, sp_label *lab); bool sp_exit_block(THD *thd, sp_label *lab, Item *when); + + bool sp_for_loop_condition(THD *thd, const Lex_for_loop_st &loop); + bool sp_for_loop_increment(THD *thd, const Lex_for_loop_st &loop); + public: inline bool is_arena_for_set_stmt() {return arena_for_set_stmt != 0;} bool set_arena_for_set_stmt(Query_arena *backup); @@ -3167,6 +3171,16 @@ public: bool sp_while_loop_expression(THD *thd, Item *expr); bool sp_while_loop_finalize(THD *thd); + sp_variable *sp_add_for_loop_variable(THD *thd, const LEX_STRING name, + Item *value); + sp_variable *sp_add_for_loop_upper_bound(THD *thd, Item *value) + { + LEX_STRING name= { C_STRING_WITH_LEN("[upper_bound]") }; + return sp_add_for_loop_variable(thd, name, value); + } + bool sp_for_loop_index_and_bounds(THD *thd, const Lex_for_loop_st &loop); + bool sp_for_loop_finalize(THD *thd, const Lex_for_loop_st &loop); + // Check if "KEY IF NOT EXISTS name" used outside of ALTER context bool check_add_key(DDL_options_st ddl) { diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 31fe5cf617e..f0e36bf3784 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -803,6 +803,7 @@ Virtual_column_info *add_virtual_expression(THD *thd, Item *expr) Lex_cast_type_st Lex_cast_type; Lex_field_type_st Lex_field_type; Lex_dyncol_type_st Lex_dyncol_type; + Lex_for_loop_st for_loop; /* pointers */ Create_field *create_field; @@ -1060,6 +1061,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token DOUBLE_SYM /* SQL-2003-R */ %token DO_DOMAIN_IDS_SYM %token DO_SYM +%token DOT_DOT_SYM %token DROP /* SQL-2003-R */ %token DUAL_SYM %token DUMPFILE diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy index 0d1977e59df..863066228aa 100644 --- a/sql/sql_yacc_ora.yy +++ b/sql/sql_yacc_ora.yy @@ -177,6 +177,7 @@ void ORAerror(THD *thd, const char *s) Lex_cast_type_st Lex_cast_type; Lex_field_type_st Lex_field_type; Lex_dyncol_type_st Lex_dyncol_type; + Lex_for_loop_st for_loop; /* pointers */ Create_field *create_field; @@ -260,10 +261,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %parse-param { THD *thd } %lex-param { THD *thd } /* - Currently there are 102 shift/reduce conflicts. + Currently there are 103 shift/reduce conflicts. We should not introduce new conflicts any more. */ -%expect 102 +%expect 103 /* Comments for TOKENS. @@ -434,6 +435,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token DOUBLE_SYM /* SQL-2003-R */ %token DO_DOMAIN_IDS_SYM %token DO_SYM +%token DOT_DOT_SYM %token DROP /* SQL-2003-R */ %token DUAL_SYM %token DUMPFILE @@ -1278,6 +1280,8 @@ END_OF_INPUT %type <lex> sp_cursor_stmt %type <spname> sp_name %type <spvar> sp_param_name sp_param_name_and_type +%type <for_loop> sp_for_loop_index_and_bounds +%type <num> opt_sp_for_loop_direction %type <spvar_mode> sp_opt_inout %type <index_hint> index_hint_type %type <num> index_hint_clause normal_join inner_join @@ -3471,6 +3475,32 @@ exception_handler: } ; +/* This adds one shift/reduce conflict */ +opt_sp_for_loop_direction: + /* Empty */ { $$= 1; } + | REVERSE_SYM { $$= -1; } + ; + +sp_for_loop_index_and_bounds: + ident_directly_assignable + { + Lex->sphead->reset_lex(thd); + } + IN_SYM opt_sp_for_loop_direction expr + { + if (!($<spvar>$= Lex->sp_add_for_loop_variable(thd, $1, $5))) + MYSQL_YYABORT; + Lex->sphead->reset_lex(thd); + } + DOT_DOT_SYM expr + { + $$.m_index= $<spvar>6; + if (!($$.m_upper_bound= Lex->sp_add_for_loop_upper_bound(thd, $8))) + MYSQL_YYABORT; + $$.m_direction= $4; + } + ; + loop_body: sp_proc_stmts1 END LOOP_SYM { @@ -3574,6 +3604,28 @@ sp_unlabeled_control: { Lex->sp_pop_loop_empty_label(thd); } + | FOR_SYM + { + // See "The FOR LOOP statement" comments in sql_lex.cc + Lex->sp_block_init(thd); // The outer DECLARE..BEGIN..END block + } + sp_for_loop_index_and_bounds + { + if (Lex->sp_push_loop_empty_label(thd)) // The inner WHILE block + MYSQL_YYABORT; + if (Lex->sp_for_loop_index_and_bounds(thd, $3)) + MYSQL_YYABORT; + } + LOOP_SYM + sp_proc_stmts1 + END LOOP_SYM + { + if (Lex->sp_for_loop_finalize(thd, $3)) + MYSQL_YYABORT; + Lex->sp_pop_loop_empty_label(thd); // The inner WHILE block + if (Lex->sp_block_finalize(thd)) // The outer DECLARE..BEGIN..END + MYSQL_YYABORT; + } | REPEAT_SYM { if (Lex->sp_push_loop_empty_label(thd)) diff --git a/sql/structs.h b/sql/structs.h index f0c3926e424..cf1b2ff4e94 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -668,4 +668,14 @@ public: } }; + +struct Lex_for_loop_st +{ +public: + class sp_variable *m_index; + class sp_variable *m_upper_bound; + int m_direction; +}; + + #endif /* STRUCTS_INCLUDED */ |