diff options
author | Alexander Barkov <bar@mariadb.org> | 2017-03-08 23:20:39 +0400 |
---|---|---|
committer | Alexander Barkov <bar@mariadb.org> | 2017-04-05 15:02:59 +0400 |
commit | f429b5a834439e4f0c76e893487e33027d76b74b (patch) | |
tree | a06ec92dad05a2820ecd8a9afe73696b103536a3 /sql | |
parent | 1b8a0c879d80733e3c684080b8c7719c35642e0d (diff) | |
download | mariadb-git-f429b5a834439e4f0c76e893487e33027d76b74b.tar.gz |
MDEV-12011 sql_mode=ORACLE: cursor%ROWTYPE in variable declarations
Implementing cursor%ROWTYPE variables, according to the task description.
This patch includes a refactoring in how sp_instr_cpush and sp_instr_copen
work. This is needed to implement MDEV-10598 later easier, to allow variable
declarations go after cursor declarations (which is currently not allowed).
Before this patch, sp_instr_cpush worked as a Query_arena associated with
the cursor. sp_instr_copen::execute() switched to the sp_instr_cpush's
Query_arena when executing the cursor SELECT statement.
Now the Query_arena associated with the cursor is stored inside an instance
of a new class sp_lex_cursor (a LEX descendand) that contains the cursor SELECT
statement.
This simplifies the implementation, because:
- It's easier to follow the code when everything related to execution
of the cursor SELECT statement is stored inside the same sp_lex_cursor
object (rather than distributed between LEX and sp_instr_cpush).
- It's easier to link an sp_instr_cursor_copy_struct to
sp_lex_cursor rather than to sp_instr_cpush.
- Also, it allows to perform sp_instr_cursor_copy_struct::exec_core()
without having a pointer to sp_instr_cpush, using a pointer to sp_lex_cursor
instead. This will be important for MDEV-10598, because sp_instr_cpush will
happen *after* sp_instr_cursor_copy_struct.
After MDEV-10598 is done, this declaration:
DECLARE
CURSOR cur IS SELECT * FROM t1;
rec cur%ROWTYPE;
BEGIN
OPEN cur;
FETCH cur INTO rec;
CLOSE cur;
END;
will generate about this code:
+-----+--------------------------+
| Pos | Instruction |
+-----+--------------------------+
| 0 | cursor_copy_struct rec@0 | Points to sp_cursor_lex through m_lex_keeper
| 1 | set rec@0 NULL |
| 2 | cpush cur@0 | Points to sp_cursor_lex through m_lex_keeper
| 3 | copen cur@0 | Points to sp_cursor_lex through m_cursor
| 4 | cfetch cur@0 rec@0 |
| 5 | cclose cur@0 |
| 6 | cpop 1 |
+-----+--------------------------+
Notice, "cursor_copy_struct" and "set" will go before "cpush".
Instructions at positions 0, 2, 3 point to the same sp_cursor_lex instance.
Diffstat (limited to 'sql')
-rw-r--r-- | sql/field.h | 25 | ||||
-rw-r--r-- | sql/sp_head.cc | 105 | ||||
-rw-r--r-- | sql/sp_head.h | 65 | ||||
-rw-r--r-- | sql/sp_pcontext.cc | 5 | ||||
-rw-r--r-- | sql/sp_pcontext.h | 10 | ||||
-rw-r--r-- | sql/sp_rcontext.cc | 35 | ||||
-rw-r--r-- | sql/sp_rcontext.h | 10 | ||||
-rw-r--r-- | sql/sql_cursor.cc | 4 | ||||
-rw-r--r-- | sql/sql_cursor.h | 5 | ||||
-rw-r--r-- | sql/sql_lex.cc | 67 | ||||
-rw-r--r-- | sql/sql_lex.h | 9 | ||||
-rw-r--r-- | sql/sql_select.cc | 2 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 32 | ||||
-rw-r--r-- | sql/sql_yacc_ora.yy | 37 | ||||
-rw-r--r-- | sql/table.cc | 22 | ||||
-rw-r--r-- | sql/table.h | 1 |
16 files changed, 351 insertions, 83 deletions
diff --git a/sql/field.h b/sql/field.h index e110e1f8146..3b96dd546cb 100644 --- a/sql/field.h +++ b/sql/field.h @@ -3987,6 +3987,16 @@ public: }; +class Cursor_rowtype: public Sql_alloc +{ +public: + uint m_cursor; + Cursor_rowtype(uint cursor) + :m_cursor(cursor) + { } +}; + + /** This class is used during a stored routine or a trigger execution, at sp_rcontext::create() time. @@ -4010,20 +4020,25 @@ class Spvar_definition: public Column_definition { class Qualified_column_ident *m_column_type_ref; // for %TYPE class Table_ident *m_table_rowtype_ref; // for table%ROWTYPE + class Cursor_rowtype *m_cursor_rowtype_ref; // for cursor%ROWTYPE Row_definition_list *m_row_field_definitions; // for ROW public: Spvar_definition() :m_column_type_ref(NULL), m_table_rowtype_ref(NULL), - m_row_field_definitions(NULL) { } + m_cursor_rowtype_ref(NULL), + m_row_field_definitions(NULL) + { } Spvar_definition(THD *thd, Field *field) :Column_definition(thd, field, NULL), m_column_type_ref(NULL), m_table_rowtype_ref(NULL), + m_cursor_rowtype_ref(NULL), m_row_field_definitions(NULL) { } bool is_column_type_ref() const { return m_column_type_ref != 0; } bool is_table_rowtype_ref() const { return m_table_rowtype_ref != 0; } + bool is_cursor_rowtype_ref() const { return m_cursor_rowtype_ref != NULL; } class Qualified_column_ident *column_type_ref() const { return m_column_type_ref; @@ -4041,6 +4056,14 @@ public: { m_table_rowtype_ref= ref; } + class Cursor_rowtype *cursor_rowtype_ref() const + { + return m_cursor_rowtype_ref; + } + void set_cursor_rowtype_ref(class Cursor_rowtype *ref) + { + m_cursor_rowtype_ref= ref; + } /* Find a ROW field by name. diff --git a/sql/sp_head.cc b/sql/sp_head.cc index a40ad52676c..2b5b1db5ddf 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -3152,6 +3152,26 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp, } +int sp_lex_keeper::cursor_reset_lex_and_exec_core(THD *thd, uint *nextp, + bool open_tables, + sp_instr *instr) +{ + Query_arena *old_arena= thd->stmt_arena; + /* + Get the Query_arena from the cursor statement LEX, which contains + the free_list of the query, so new items (if any) are stored in + the right free_list, and we can cleanup after each cursor operation, + e.g. open or cursor_copy_struct (for cursor%ROWTYPE variables). + */ + thd->stmt_arena= m_lex->query_arena(); + int res= reset_lex_and_exec_core(thd, nextp, open_tables, instr); + if (thd->stmt_arena->free_list) + cleanup_items(thd->stmt_arena->free_list); + thd->stmt_arena= old_arena; + return res; +} + + /* sp_instr class functions */ @@ -3455,7 +3475,8 @@ sp_instr_set_row_field_by_name::print(String *str) int rsrv= SP_INSTR_UINT_MAXLEN + 6 + 6 + 3 + 2; sp_variable *var= m_ctx->find_variable(m_offset); DBUG_ASSERT(var); - DBUG_ASSERT(var->field_def.is_table_rowtype_ref()); + DBUG_ASSERT(var->field_def.is_table_rowtype_ref() || + var->field_def.is_cursor_rowtype_ref()); rsrv+= var->name.length + 2 * m_field_name.length; if (str->reserve(rsrv)) @@ -3915,7 +3936,7 @@ sp_instr_cpush::execute(THD *thd, uint *nextp) { DBUG_ENTER("sp_instr_cpush::execute"); - int ret= thd->spcont->push_cursor(thd, &m_lex_keeper, this); + int ret= thd->spcont->push_cursor(thd, &m_lex_keeper); *nextp= m_ip+1; @@ -3995,19 +4016,7 @@ sp_instr_copen::execute(THD *thd, uint *nextp) else { sp_lex_keeper *lex_keeper= c->get_lex_keeper(); - Query_arena *old_arena= thd->stmt_arena; - - /* - Get the Query_arena from the cpush instruction, which contains - the free_list of the query, so new items (if any) are stored in - the right free_list, and we can cleanup after each open. - */ - thd->stmt_arena= c->get_instr(); - res= lex_keeper->reset_lex_and_exec_core(thd, nextp, FALSE, this); - /* Cleanup the query's items */ - if (thd->stmt_arena->free_list) - cleanup_items(thd->stmt_arena->free_list); - thd->stmt_arena= old_arena; + res= lex_keeper->cursor_reset_lex_and_exec_core(thd, nextp, FALSE, this); /* TODO: Assert here that we either have an error or a cursor */ } DBUG_RETURN(res); @@ -4140,6 +4149,72 @@ sp_instr_cfetch::print(String *str) /* + sp_instr_cursor_copy_struct class functions +*/ + +int +sp_instr_cursor_copy_struct::exec_core(THD *thd, uint *nextp) +{ + DBUG_ENTER("sp_instr_cusrot_copy_struct::exec_core"); + int ret= 0; + Item_field_row *row= (Item_field_row*) thd->spcont->get_item(m_var); + DBUG_ASSERT(row->type_handler() == &type_handler_row); + + /* + Copy structure only once. If the cursor%ROWTYPE variable is declared + inside a LOOP block, it gets its structure on the first loop interation + and remembers the structure for all consequent loop iterations. + It we recreated the structure on every iteration, we would get + potential memory leaks, and it would be less efficient. + */ + if (!row->arguments()) + { + sp_cursor tmp(thd, &m_lex_keeper); + if (!(ret= tmp.open_view_structure_only(thd))) + { + Row_definition_list defs; + if (!(ret= tmp.export_structure(thd, &defs))) + { + /* + Create row elements on the caller arena. + It's the same arena that was used during sp_rcontext::create(). + This puts cursor%ROWTYPE elements on the same mem_root + where explicit ROW elements and table%ROWTYPE reside. + */ + Query_arena current_arena; + thd->set_n_backup_active_arena(thd->spcont->callers_arena, ¤t_arena); + row->row_create_items(thd, &defs); + thd->restore_active_arena(thd->spcont->callers_arena, ¤t_arena); + } + tmp.close(thd); + } + } + *nextp= m_ip + 1; + DBUG_RETURN(ret); +} + + +int +sp_instr_cursor_copy_struct::execute(THD *thd, uint *nextp) +{ + DBUG_ENTER("sp_instr_cursor_copy_struct::execute"); + int ret= m_lex_keeper.cursor_reset_lex_and_exec_core(thd, nextp, FALSE, this); + DBUG_RETURN(ret); +} + + +void +sp_instr_cursor_copy_struct::print(String *str) +{ + sp_variable *var= m_ctx->find_variable(m_var); + str->append(STRING_WITH_LEN("cursor_copy_struct ")); + str->append(var->name.str, var->name.length); + str->append('@'); + str->append_ulonglong(m_var); +} + + +/* sp_instr_error class functions */ diff --git a/sql/sp_head.h b/sql/sp_head.h index 92f17884285..37ef4b6825f 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -341,6 +341,8 @@ public: bool show_create_routine(THD *thd, int type); + MEM_ROOT *get_main_mem_root() { return &main_mem_root; } + int add_instr(sp_instr *instr); @@ -767,6 +769,42 @@ private: }; // class sp_head : public Sql_alloc +class sp_lex_cursor: public sp_lex_local, public Query_arena +{ +public: + sp_lex_cursor(THD *thd, const LEX *oldlex, MEM_ROOT *mem_root_arg) + :sp_lex_local(thd, oldlex), + Query_arena(mem_root_arg, STMT_INITIALIZED_FOR_SP) + { } + sp_lex_cursor(THD *thd, const LEX *oldlex) + :sp_lex_local(thd, oldlex), + Query_arena(thd->lex->sphead->get_main_mem_root(), STMT_INITIALIZED_FOR_SP) + { } + ~sp_lex_cursor() { free_items(); } + void cleanup_stmt() { } + Query_arena *query_arena() { return this; } + bool validate() + { + DBUG_ASSERT(sql_command == SQLCOM_SELECT); + if (result) + { + my_error(ER_SP_BAD_CURSOR_SELECT, MYF(0)); + return true; + } + return false; + } + bool stmt_finalize(THD *thd) + { + if (validate()) + return true; + sp_lex_in_use= true; + free_list= thd->free_list; + thd->free_list= NULL; + return false; + } +}; + + // // "Instructions"... // @@ -924,6 +962,9 @@ public: int reset_lex_and_exec_core(THD *thd, uint *nextp, bool open_tables, sp_instr* instr); + int cursor_reset_lex_and_exec_core(THD *thd, uint *nextp, bool open_tables, + sp_instr *instr); + inline uint sql_command() const { return (uint)m_lex->sql_command; @@ -1550,6 +1591,30 @@ private: }; // class sp_instr_copen : public sp_instr_stmt +/** + Initialize the structure of a cursor%ROWTYPE variable + from the LEX containing the cursor SELECT statement. +*/ +class sp_instr_cursor_copy_struct: public sp_instr +{ + /**< Prevent use of these */ + sp_instr_cursor_copy_struct(const sp_instr_cursor_copy_struct &); + void operator=(sp_instr_cursor_copy_struct &); + sp_lex_keeper m_lex_keeper; + uint m_var; +public: + sp_instr_cursor_copy_struct(uint ip, sp_pcontext *ctx, + sp_lex_cursor *lex, uint voffs) + : sp_instr(ip, ctx), m_lex_keeper(lex, FALSE), m_var(voffs) + {} + virtual ~sp_instr_cursor_copy_struct() + {} + virtual int execute(THD *thd, uint *nextp); + virtual int exec_core(THD *thd, uint *nextp); + virtual void print(String *str); +}; + + class sp_instr_cclose : public sp_instr { sp_instr_cclose(const sp_instr_cclose &); /**< Prevent use of these */ diff --git a/sql/sp_pcontext.cc b/sql/sp_pcontext.cc index 332f43db846..3a3ad0713b5 100644 --- a/sql/sp_pcontext.cc +++ b/sql/sp_pcontext.cc @@ -553,12 +553,13 @@ sp_pcontext::find_handler(const Sql_condition_identity &value) const } -bool sp_pcontext::add_cursor(const LEX_STRING name, sp_pcontext *param_ctx) +bool sp_pcontext::add_cursor(const LEX_STRING name, sp_pcontext *param_ctx, + sp_lex_cursor *lex) { if (m_cursors.elements() == m_max_cursor_index) ++m_max_cursor_index; - return m_cursors.append(sp_pcursor(name, param_ctx)); + return m_cursors.append(sp_pcursor(name, param_ctx, lex)); } diff --git a/sql/sp_pcontext.h b/sql/sp_pcontext.h index 85d0afa8feb..6d8f5e1baf8 100644 --- a/sql/sp_pcontext.h +++ b/sql/sp_pcontext.h @@ -288,11 +288,14 @@ public: class sp_pcursor: public LEX_STRING { class sp_pcontext *m_param_context; // Formal parameters + class sp_lex_cursor *m_lex; // The cursor statement LEX public: - sp_pcursor(const LEX_STRING &name, class sp_pcontext *param_ctx) - :LEX_STRING(name), m_param_context(param_ctx) + sp_pcursor(const LEX_STRING &name, class sp_pcontext *param_ctx, + class sp_lex_cursor *lex) + :LEX_STRING(name), m_param_context(param_ctx), m_lex(lex) { } class sp_pcontext *param_context() const { return m_param_context; } + class sp_lex_cursor *lex() const { return m_lex; } }; @@ -633,7 +636,8 @@ public: // Cursors. ///////////////////////////////////////////////////////////////////////// - bool add_cursor(const LEX_STRING name, sp_pcontext *param_ctx); + bool add_cursor(const LEX_STRING name, sp_pcontext *param_ctx, + class sp_lex_cursor *lex); /// See comment for find_variable() above. const sp_pcursor *find_cursor(const LEX_STRING name, diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc index b631ad71e49..d60d7f5118f 100644 --- a/sql/sp_rcontext.cc +++ b/sql/sp_rcontext.cc @@ -286,6 +286,13 @@ bool sp_rcontext::init_var_items(THD *thd, item->row_create_items(thd, &defs)) return true; } + else if (def->is_cursor_rowtype_ref()) + { + Row_definition_list defs; + Item_field_row *item= new (thd->mem_root) Item_field_row(thd, field); + if (!(m_var_items[idx]= item)) + return true; + } else if (def->is_row()) { Item_field_row *item= new (thd->mem_root) Item_field_row(thd, field); @@ -341,14 +348,13 @@ bool sp_rcontext::set_return_value(THD *thd, Item **return_value_item) } -bool sp_rcontext::push_cursor(THD *thd, sp_lex_keeper *lex_keeper, - sp_instr_cpush *i) +bool sp_rcontext::push_cursor(THD *thd, sp_lex_keeper *lex_keeper) { /* We should create cursors in the callers arena, as it could be (and usually is) used in several instructions. */ - sp_cursor *c= new (callers_arena->mem_root) sp_cursor(thd, lex_keeper, i); + sp_cursor *c= new (callers_arena->mem_root) sp_cursor(thd, lex_keeper); if (c == NULL) return true; @@ -687,11 +693,10 @@ bool sp_rcontext::set_case_expr(THD *thd, int case_expr_id, /////////////////////////////////////////////////////////////////////////// -sp_cursor::sp_cursor(THD *thd_arg, sp_lex_keeper *lex_keeper, sp_instr_cpush *i): +sp_cursor::sp_cursor(THD *thd_arg, sp_lex_keeper *lex_keeper): result(thd_arg), m_lex_keeper(lex_keeper), server_side_cursor(NULL), - m_i(i), m_fetch_count(0), m_row_count(0), m_found(false) @@ -731,6 +736,21 @@ int sp_cursor::open(THD *thd) } +int sp_cursor::open_view_structure_only(THD *thd) +{ + int res; + int thd_no_errors_save= thd->no_errors; + Item *limit_rows_examined= thd->lex->limit_rows_examined; + if (!(thd->lex->limit_rows_examined= new (thd->mem_root) Item_uint(thd, 0))) + return -1; + thd->no_errors= true; // Suppress ER_QUERY_EXCEEDED_ROWS_EXAMINED_LIMIT + res= open(thd); + thd->no_errors= thd_no_errors_save; + thd->lex->limit_rows_examined= limit_rows_examined; + return res; +} + + int sp_cursor::close(THD *thd) { if (! server_side_cursor) @@ -802,6 +822,11 @@ int sp_cursor::fetch(THD *thd, List<sp_variable> *vars) } +bool sp_cursor::export_structure(THD *thd, Row_definition_list *list) +{ + return server_side_cursor->export_structure(thd, list); +} + /////////////////////////////////////////////////////////////////////////// // sp_cursor::Select_fetch_into_spvars implementation. /////////////////////////////////////////////////////////////////////////// diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h index 1c47b1caff0..98464518787 100644 --- a/sql/sp_rcontext.h +++ b/sql/sp_rcontext.h @@ -272,7 +272,7 @@ public: /// @return error flag. /// @retval false on success. /// @retval true on error. - bool push_cursor(THD *thd, sp_lex_keeper *lex_keeper, sp_instr_cpush *i); + bool push_cursor(THD *thd, sp_lex_keeper *lex_keeper); /// Pop and delete given number of sp_cursor instance from the cursor stack. /// @@ -434,7 +434,7 @@ private: }; public: - sp_cursor(THD *thd_arg, sp_lex_keeper *lex_keeper, sp_instr_cpush *i); + sp_cursor(THD *thd_arg, sp_lex_keeper *lex_keeper); virtual ~sp_cursor() { destroy(); } @@ -443,6 +443,8 @@ public: int open(THD *thd); + int open_view_structure_only(THD *thd); + int close(THD *thd); my_bool is_open() @@ -459,14 +461,12 @@ public: int fetch(THD *, List<sp_variable> *vars); - sp_instr_cpush *get_instr() - { return m_i; } + bool export_structure(THD *thd, Row_definition_list *list); private: Select_fetch_into_spvars result; sp_lex_keeper *m_lex_keeper; Server_side_cursor *server_side_cursor; - sp_instr_cpush *m_i; // My push instruction ulonglong m_fetch_count; // Number of FETCH commands since last OPEN ulonglong m_row_count; // Number of successful FETCH since last OPEN bool m_found; // If last FETCH fetched a row diff --git a/sql/sql_cursor.cc b/sql/sql_cursor.cc index 60d80387a10..40eb7046108 100644 --- a/sql/sql_cursor.cc +++ b/sql/sql_cursor.cc @@ -53,6 +53,10 @@ public: virtual int open(JOIN *join __attribute__((unused))); virtual void fetch(ulong num_rows); virtual void close(); + bool export_structure(THD *thd, Row_definition_list *defs) + { + return table->export_structure(thd, defs); + } virtual ~Materialized_cursor(); void on_table_fill_finished(); diff --git a/sql/sql_cursor.h b/sql/sql_cursor.h index bff47d654b3..6fa72a2005d 100644 --- a/sql/sql_cursor.h +++ b/sql/sql_cursor.h @@ -54,6 +54,11 @@ public: virtual int open(JOIN *top_level_join)= 0; virtual void fetch(ulong num_rows)= 0; virtual void close()= 0; + virtual bool export_structure(THD *thd, Row_definition_list *defs) + { + DBUG_ASSERT(0); + return true; + } virtual ~Server_side_cursor(); static void operator delete(void *ptr, size_t size); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 8f258f50902..ca35535973e 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -5297,6 +5297,9 @@ LEX::sp_variable_declarations_rowtype_finalize(THD *thd, int nvars, Qualified_column_ident *ref, Item *def) { + uint coffp; + const sp_pcursor *pcursor= ref->table.str && ref->db.str ? NULL : + spcont->find_cursor(ref->m_column, &coffp, false); uint num_vars= spcont->context_var_count(); if (!def && !(def= new (thd->mem_root) Item_null(thd))) @@ -5307,24 +5310,41 @@ LEX::sp_variable_declarations_rowtype_finalize(THD *thd, int nvars, bool last= i == num_vars - 1; uint var_idx= spcont->var_context2runtime(i); sp_variable *spvar= spcont->find_context_variable(i); - /* - When parsing a qualified identifier chain, the parser does not know yet - if it's going to be a qualified column name (for %TYPE), - or a qualified table name (for %ROWTYPE). So it collects the chain - into Qualified_column_ident. - Now we know that it was actually a qualified table name (%ROWTYPE). - Create a new Table_ident from Qualified_column_ident, - shifting fields as follows: - - ref->m_column becomes table_ref->table - - ref->table becomes table_ref->db - */ - Table_ident *table_ref; - if (!(table_ref= new (thd->mem_root) Table_ident(thd, - ref->table, - ref->m_column, - false))) - return true; - spvar->field_def.set_table_rowtype_ref(table_ref); + + if (pcursor) + { + Cursor_rowtype *ref; + if (!(ref= new (thd->mem_root) Cursor_rowtype(coffp))) + return true; + spvar->field_def.set_cursor_rowtype_ref(ref); + sp_instr_cursor_copy_struct *instr= + new (thd->mem_root) sp_instr_cursor_copy_struct(sphead->instructions(), + spcont, pcursor->lex(), + spvar->offset); + if (instr == NULL || sphead->add_instr(instr)) + return true; + } + else + { + /* + When parsing a qualified identifier chain, the parser does not know yet + if it's going to be a qualified column name (for %TYPE), + or a qualified table name (for %ROWTYPE). So it collects the chain + into Qualified_column_ident. + Now we know that it was actually a qualified table name (%ROWTYPE). + Create a new Table_ident from Qualified_column_ident, + shifting fields as follows: + - ref->m_column becomes table_ref->table + - ref->table becomes table_ref->db + */ + Table_ident *table_ref; + if (!(table_ref= new (thd->mem_root) Table_ident(thd, + ref->table, + ref->m_column, + false))) + return true; + spvar->field_def.set_table_rowtype_ref(table_ref); + } spvar->field_def.field_name= spvar->name.str; spvar->default_value= def; /* The last instruction is responsible for freeing LEX. */ @@ -5485,7 +5505,8 @@ bool LEX::sp_for_loop_finalize(THD *thd, const Lex_for_loop_st &loop) /***************************************************************************/ -bool LEX::sp_declare_cursor(THD *thd, const LEX_STRING name, LEX *cursor_stmt, +bool LEX::sp_declare_cursor(THD *thd, const LEX_STRING name, + sp_lex_cursor *cursor_stmt, sp_pcontext *param_ctx) { uint offp; @@ -5501,7 +5522,7 @@ bool LEX::sp_declare_cursor(THD *thd, const LEX_STRING name, LEX *cursor_stmt, spcont->current_cursor_count()); return i == NULL || sphead->add_instr(i) || - spcont->add_cursor(name, param_ctx); + spcont->add_cursor(name, param_ctx, cursor_stmt); } @@ -6178,7 +6199,8 @@ Item_splocal *LEX::create_item_spvar_row_field(THD *thd, } Item_splocal *item; - if (spv->field_def.is_table_rowtype_ref()) + if (spv->field_def.is_table_rowtype_ref() || + spv->field_def.is_cursor_rowtype_ref()) { if (!(item= new (thd->mem_root) Item_splocal_row_field_by_name(thd, a, b, spv->offset, @@ -6406,7 +6428,8 @@ bool LEX::set_variable(const LEX_STRING &name1, sp_variable *spv; if (spcont && (spv= spcont->find_variable(name1, false))) { - if (spv->field_def.is_table_rowtype_ref()) + if (spv->field_def.is_table_rowtype_ref() || + spv->field_def.is_cursor_rowtype_ref()) return sphead->set_local_variable_row_field_by_name(thd, spcont, spv, name2, item, this); diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 8edebaf6028..3cdf05377dd 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -2936,6 +2936,12 @@ public: delete_dynamic(&plugins); } + virtual class Query_arena *query_arena() + { + DBUG_ASSERT(0); + return NULL; + } + void start(THD *thd); const char *substatement_query(THD *thd) const; @@ -3155,7 +3161,8 @@ public: bool sp_handler_declaration_init(THD *thd, int type); bool sp_handler_declaration_finalize(THD *thd, int type); - bool sp_declare_cursor(THD *thd, const LEX_STRING name, LEX *cursor_stmt, + bool sp_declare_cursor(THD *thd, const LEX_STRING name, + class sp_lex_cursor *cursor_stmt, sp_pcontext *param_ctx); bool sp_open_cursor(THD *thd, const LEX_STRING name, List<sp_assignment_lex> *parameters); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index d3f2a674664..5ca19e34bac 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -377,7 +377,7 @@ bool handle_select(THD *thd, LEX *lex, select_result *result, res|= thd->is_error(); if (unlikely(res)) result->abort_result_set(); - if (thd->killed == ABORT_QUERY) + if (thd->killed == ABORT_QUERY && !thd->no_errors) { /* If LIMIT ROWS EXAMINED interrupted query execution, issue a warning, diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 93fdff57d38..45af801272a 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -783,6 +783,7 @@ Virtual_column_info *add_virtual_expression(THD *thd, Item *expr) Item_param *item_param; Key_part_spec *key_part; LEX *lex; + class sp_lex_cursor *sp_cursor_stmt; LEX_STRING *lex_str_ptr; LEX_USER *lex_user; List<Condition_information_item> *cond_info_list; @@ -1710,6 +1711,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); expr_list opt_udf_expr_list udf_expr_list when_list ident_list ident_list_arg opt_expr_list +%type <sp_cursor_stmt> + sp_cursor_stmt_lex + sp_cursor_stmt + %type <var_type> option_type opt_var_type opt_var_ident_type @@ -1871,7 +1876,6 @@ END_OF_INPUT %type <num> sp_decl_idents sp_handler_type sp_hcond_list %type <spcondvalue> sp_cond sp_hcond sqlstate signal_value opt_signal_value %type <spblock> sp_decls sp_decl sp_decl_body -%type <lex> sp_cursor_stmt %type <spname> sp_name %type <spvar> sp_param_name sp_param_name_and_type %type <spvar_mode> sp_opt_inout @@ -3024,22 +3028,28 @@ sp_decl_body: } ; + +sp_cursor_stmt_lex: + { + DBUG_ASSERT(thd->lex->sphead); + if (!($$= new (thd->mem_root) sp_lex_cursor(thd, thd->lex))) + MYSQL_YYABORT; + } + ; + sp_cursor_stmt: + sp_cursor_stmt_lex { - Lex->sphead->reset_lex(thd); + DBUG_ASSERT(thd->free_list == NULL); + Lex->sphead->reset_lex(thd, $1); } select { - LEX *lex= Lex; - - DBUG_ASSERT(lex->sql_command == SQLCOM_SELECT); - - if (lex->result) - my_yyabort_error((ER_SP_BAD_CURSOR_SELECT, MYF(0))); - lex->sp_lex_in_use= TRUE; - $$= lex; - if (lex->sphead->restore_lex(thd)) + DBUG_ASSERT(Lex == $1); + if ($1->stmt_finalize(thd) || + $1->sphead->restore_lex(thd)) MYSQL_YYABORT; + $$= $1; } ; diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy index c72fa8ca0e5..2360b735674 100644 --- a/sql/sql_yacc_ora.yy +++ b/sql/sql_yacc_ora.yy @@ -199,6 +199,7 @@ void ORAerror(THD *thd, const char *s) Key_part_spec *key_part; LEX *lex; sp_assignment_lex *assignment_lex; + class sp_lex_cursor *sp_cursor_stmt; LEX_STRING *lex_str_ptr; LEX_USER *lex_user; List<Condition_information_item> *cond_info_list; @@ -1150,6 +1151,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); ident_list ident_list_arg opt_expr_list decode_when_list +%type <sp_cursor_stmt> + sp_cursor_stmt_lex + sp_cursor_stmt + %type <assignment_lex> assignment_source_lex assignment_source_expr @@ -1333,7 +1338,6 @@ END_OF_INPUT %type <sp_instr_addr> sp_instr_addr %type <sp_cursor_name_and_offset> sp_cursor_name_and_offset %type <num> opt_exception_clause exception_handlers -%type <lex> sp_cursor_stmt remember_lex %type <spname> sp_name %type <spvar> sp_param_name sp_param_name_and_type %type <for_loop> sp_for_loop_index_and_bounds @@ -2609,22 +2613,27 @@ opt_parenthesized_cursor_formal_parameters: ; +sp_cursor_stmt_lex: + { + DBUG_ASSERT(thd->lex->sphead); + if (!($$= new (thd->mem_root) sp_lex_cursor(thd, thd->lex))) + MYSQL_YYABORT; + } + ; + sp_cursor_stmt: + sp_cursor_stmt_lex { - Lex->sphead->reset_lex(thd); + DBUG_ASSERT(thd->free_list == NULL); + Lex->sphead->reset_lex(thd, $1); } select { - LEX *lex= Lex; - - DBUG_ASSERT(lex->sql_command == SQLCOM_SELECT); - - if (lex->result) - my_yyabort_error((ER_SP_BAD_CURSOR_SELECT, MYF(0))); - lex->sp_lex_in_use= TRUE; - $$= lex; - if (lex->sphead->restore_lex(thd)) + DBUG_ASSERT(Lex == $1); + if ($1->stmt_finalize(thd) || + $1->sphead->restore_lex(thd)) MYSQL_YYABORT; + $$= $1; } ; @@ -3303,12 +3312,6 @@ sp_proc_stmt_goto: ; -remember_lex: - { - $$= thd->lex; - } - ; - assignment_source_lex: { DBUG_ASSERT(Lex->sphead); diff --git a/sql/table.cc b/sql/table.cc index 38cda5cc209..ee592f120d3 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -8213,3 +8213,25 @@ Field *TABLE::find_field_by_name(const char *str) const } return NULL; } + + +bool TABLE::export_structure(THD *thd, Row_definition_list *defs) +{ + for (Field **src= field; *src; src++) + { + uint offs; + if (defs->find_row_field_by_name(src[0]->field_name, &offs)) + { + my_error(ER_DUP_FIELDNAME, MYF(0), src[0]->field_name); + return true; + } + Spvar_definition *def= new (thd->mem_root) Spvar_definition(thd, *src); + if (!def) + return true; + def->flags&= (uint) ~NOT_NULL_FLAG; + if ((def->sp_prepare_create_field(thd, thd->mem_root)) || + (defs->push_back(def, thd->mem_root))) + return true; + } + return false; +} diff --git a/sql/table.h b/sql/table.h index 22dfeaaa596..4885196a817 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1441,6 +1441,7 @@ public: TMP_TABLE_PARAM *tmp_table_param, bool with_cleanup); Field *find_field_by_name(const char *str) const; + bool export_structure(THD *thd, class Row_definition_list *defs); }; |