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/sp_head.cc | |
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/sp_head.cc')
-rw-r--r-- | sql/sp_head.cc | 105 |
1 files changed, 90 insertions, 15 deletions
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 */ |