summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorAlexander Barkov <bar@mariadb.org>2017-03-08 23:20:39 +0400
committerAlexander Barkov <bar@mariadb.org>2017-04-05 15:02:59 +0400
commitf429b5a834439e4f0c76e893487e33027d76b74b (patch)
treea06ec92dad05a2820ecd8a9afe73696b103536a3 /sql
parent1b8a0c879d80733e3c684080b8c7719c35642e0d (diff)
downloadmariadb-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.h25
-rw-r--r--sql/sp_head.cc105
-rw-r--r--sql/sp_head.h65
-rw-r--r--sql/sp_pcontext.cc5
-rw-r--r--sql/sp_pcontext.h10
-rw-r--r--sql/sp_rcontext.cc35
-rw-r--r--sql/sp_rcontext.h10
-rw-r--r--sql/sql_cursor.cc4
-rw-r--r--sql/sql_cursor.h5
-rw-r--r--sql/sql_lex.cc67
-rw-r--r--sql/sql_lex.h9
-rw-r--r--sql/sql_select.cc2
-rw-r--r--sql/sql_yacc.yy32
-rw-r--r--sql/sql_yacc_ora.yy37
-rw-r--r--sql/table.cc22
-rw-r--r--sql/table.h1
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, &current_arena);
+ row->row_create_items(thd, &defs);
+ thd->restore_active_arena(thd->spcont->callers_arena, &current_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);
};