Skip to content

Commit

Permalink
MDEV-10697 GOTO statement
Browse files Browse the repository at this point in the history
  • Loading branch information
halfspawn authored and Alexander Barkov committed Apr 5, 2017
1 parent d836f52 commit af7f287
Show file tree
Hide file tree
Showing 11 changed files with 2,092 additions and 41 deletions.
834 changes: 834 additions & 0 deletions mysql-test/suite/compat/oracle/r/sp-goto.result

Large diffs are not rendered by default.

872 changes: 872 additions & 0 deletions mysql-test/suite/compat/oracle/t/sp-goto.test

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions sql/lex.h
Expand Up @@ -261,6 +261,7 @@ static SYMBOL symbols[] = {
{ "GET_FORMAT", SYM(GET_FORMAT)},
{ "GET", SYM(GET_SYM)},
{ "GLOBAL", SYM(GLOBAL_SYM)},
{ "GOTO", SYM(GOTO_SYM)},
{ "GRANT", SYM(GRANT)},
{ "GRANTS", SYM(GRANTS)},
{ "GROUP", SYM(GROUP_SYM)},
Expand Down
135 changes: 133 additions & 2 deletions sql/sp_head.cc
Expand Up @@ -556,6 +556,7 @@ sp_head::sp_head()
DBUG_ENTER("sp_head::sp_head");

m_backpatch.empty();
m_backpatch_goto.empty();
m_cont_backpatch.empty();
m_lex.empty();
my_hash_init(&m_sptabs, system_charset_info, 0, 0, 0, sp_table_key, 0, 0);
Expand Down Expand Up @@ -2212,15 +2213,54 @@ sp_head::merge_lex(THD *thd, LEX *oldlex, LEX *sublex)
Put the instruction on the backpatch list, associated with the label.
*/
int
sp_head::push_backpatch(THD *thd, sp_instr *i, sp_label *lab)
sp_head::push_backpatch(THD *thd, sp_instr *i, sp_label *lab,
List<bp_t> *list, backpatch_instr_type itype)
{
bp_t *bp= (bp_t *) thd->alloc(sizeof(bp_t));

if (!bp)
return 1;
bp->lab= lab;
bp->instr= i;
return m_backpatch.push_front(bp);
bp->instr_type= itype;
return list->push_front(bp);
}

int
sp_head::push_backpatch(THD *thd, sp_instr *i, sp_label *lab)
{
return push_backpatch(thd, i, lab, &m_backpatch, GOTO);
}

int
sp_head::push_backpatch_goto(THD *thd, sp_pcontext *ctx, sp_label *lab)
{
uint ip= instructions();

/*
Add cpop/hpop : they will be removed or updated later if target is in
the same block or not
*/
sp_instr_hpop *hpop= new (thd->mem_root) sp_instr_hpop(ip++, ctx, 0);
if (hpop == NULL || add_instr(hpop))
return true;
if (push_backpatch(thd, hpop, lab, &m_backpatch_goto, HPOP))
return true;

sp_instr_cpop *cpop= new (thd->mem_root) sp_instr_cpop(ip++, ctx, 0);
if (cpop == NULL || add_instr(cpop))
return true;
if (push_backpatch(thd, cpop, lab, &m_backpatch_goto, CPOP))
return true;

// Add jump with ip=0. IP will be updated when label is found.
sp_instr_jump *i= new (thd->mem_root) sp_instr_jump(ip, ctx);
if (i == NULL || add_instr(i))
return true;
if (push_backpatch(thd, i, lab, &m_backpatch_goto, GOTO))
return true;

return false;
}

/**
Expand All @@ -2247,6 +2287,97 @@ sp_head::backpatch(sp_label *lab)
DBUG_VOID_RETURN;
}

void
sp_head::backpatch_goto(THD *thd, sp_label *lab,sp_label *lab_begin_block)
{
bp_t *bp;
uint dest= instructions();
List_iterator<bp_t> li(m_backpatch_goto);

DBUG_ENTER("sp_head::backpatch_goto");
while ((bp= li++))
{
if (bp->instr->m_ip < lab_begin_block->ip || bp->instr->m_ip > lab->ip)
{
/*
Update only jump target from the beginning of the block where the
label is defined.
*/
continue;
}
if (my_strcasecmp(system_charset_info,
bp->lab->name.str,
lab->name.str) == 0)
{
if (bp->instr_type == GOTO)
{
DBUG_PRINT("info",
("backpatch_goto: (m_ip %d, label 0x%lx <%s>) to dest %d",
bp->instr->m_ip, (ulong) lab, lab->name.str, dest));
bp->instr->backpatch(dest, lab->ctx);
// Jump resolved, remove from the list
li.remove();
continue;
}
if (bp->instr_type == CPOP)
{
int n= lab->ctx->diff_cursors(lab_begin_block->ctx, true);
if (n == 0)
{
// Remove cpop instr
replace_instr_to_nop(thd,bp->instr->m_ip);
}
else
{
// update count of cpop
static_cast<sp_instr_cpop*>(bp->instr)->update_count(n);
n= 1;
}
li.remove();
continue;
}
if (bp->instr_type == HPOP)
{
int n= lab->ctx->diff_handlers(lab_begin_block->ctx, true);
if (n == 0)
{
// Remove hpop instr
replace_instr_to_nop(thd,bp->instr->m_ip);
}
else
{
// update count of cpop
static_cast<sp_instr_hpop*>(bp->instr)->update_count(n);
n= 1;
}
li.remove();
continue;
}
}
}
DBUG_VOID_RETURN;
}

bool
sp_head::check_unresolved_goto()
{
DBUG_ENTER("sp_head::check_unresolved_goto");
bool has_unresolved_label=false;
if (m_backpatch_goto.elements > 0)
{
List_iterator_fast<bp_t> li(m_backpatch_goto);
bp_t *bp;
while ((bp= li++))
{
if ((bp->instr_type == GOTO))
{
my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "GOTO", bp->lab->name);
has_unresolved_label=true;
}
}
}
DBUG_RETURN(has_unresolved_label);
}

int
sp_head::new_cont_backpatch(sp_instr_opt_meta *i)
Expand Down
29 changes: 29 additions & 0 deletions sql/sp_head.h
Expand Up @@ -516,11 +516,19 @@ class sp_head :private Query_arena,
/// Put the instruction on the backpatch list, associated with the label.
int
push_backpatch(THD *thd, sp_instr *, sp_label *);
int
push_backpatch_goto(THD *thd, sp_pcontext *ctx, sp_label *lab);

/// Update all instruction with this label in the backpatch list to
/// the current position.
void
backpatch(sp_label *);
void
backpatch_goto(THD *thd, sp_label *, sp_label *);

/// Check for unresolved goto label
bool
check_unresolved_goto();

/// Start a new cont. backpatch level. If 'i' is NULL, the level is just incr.
int
Expand Down Expand Up @@ -695,12 +703,17 @@ class sp_head :private Query_arena,
sp_pcontext *m_pcont; ///< Parse context
List<LEX> m_lex; ///< Temp. store for the other lex
DYNAMIC_ARRAY m_instr; ///< The "instructions"

enum backpatch_instr_type { GOTO, CPOP, HPOP };
typedef struct
{
sp_label *lab;
sp_instr *instr;
backpatch_instr_type instr_type;
} bp_t;
List<bp_t> m_backpatch; ///< Instructions needing backpatching
List<bp_t> m_backpatch_goto; // Instructions needing backpatching (for goto)

/**
We need a special list for backpatching of instructions with a continue
destination (in the case of a continue handler catching an error in
Expand Down Expand Up @@ -738,6 +751,12 @@ class sp_head :private Query_arena,
by routine.
*/
bool merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check);

/// Put the instruction on the a backpatch list, associated with the label.
int
push_backpatch(THD *thd, sp_instr *, sp_label *, List<bp_t> *list,
backpatch_instr_type itype);

}; // class sp_head : public Sql_alloc


Expand Down Expand Up @@ -1359,6 +1378,11 @@ class sp_instr_hpop : public sp_instr
virtual ~sp_instr_hpop()
{}

void update_count(uint count)
{
m_count= count;
}

virtual int execute(THD *thd, uint *nextp);

virtual void print(String *str);
Expand Down Expand Up @@ -1451,6 +1475,11 @@ class sp_instr_cpop : public sp_instr
virtual ~sp_instr_cpop()
{}

void update_count(uint count)
{
m_count= count;
}

virtual int execute(THD *thd, uint *nextp);

virtual void print(String *str);
Expand Down
61 changes: 58 additions & 3 deletions sql/sp_pcontext.cc
Expand Up @@ -87,6 +87,7 @@ void sp_pcontext::init(uint var_offset,
m_num_case_exprs= num_case_expressions;

m_labels.empty();
m_goto_labels.empty();
}


Expand Down Expand Up @@ -129,6 +130,12 @@ sp_pcontext *sp_pcontext::push_context(THD *thd, sp_pcontext::enum_scope scope)
}


bool cmp_labels(sp_label *a, sp_label *b)
{
return (my_strcasecmp(system_charset_info, a->name.str, b->name.str) == 0
&& a->type == b->type);
}

sp_pcontext *sp_pcontext::pop_context()
{
m_parent->m_max_var_index+= m_max_var_index;
Expand All @@ -140,6 +147,18 @@ sp_pcontext *sp_pcontext::pop_context()
if (m_num_case_exprs > m_parent->m_num_case_exprs)
m_parent->m_num_case_exprs= m_num_case_exprs;

/*
** Push unresolved goto label to parent context
*/
sp_label *label;
List_iterator_fast<sp_label> li(m_goto_labels);
while ((label= li++))
{
if (label->ip == 0)
{
m_parent->m_goto_labels.add_unique(label, &cmp_labels);
}
}
return m_parent;
}

Expand Down Expand Up @@ -227,21 +246,57 @@ sp_variable *sp_pcontext::add_variable(THD *thd, LEX_STRING name)
return m_vars.append(p) ? NULL : p;
}


sp_label *sp_pcontext::push_label(THD *thd, LEX_STRING name, uint ip,
sp_label::enum_type type)
sp_label::enum_type type,
List<sp_label> *list)
{
sp_label *label=
new (thd->mem_root) sp_label(name, ip, type, this);

if (!label)
return NULL;

m_labels.push_front(label, thd->mem_root);
list->push_front(label, thd->mem_root);

return label;
}

sp_label *sp_pcontext::find_goto_label(const LEX_STRING name, bool recusive)
{
List_iterator_fast<sp_label> li(m_goto_labels);
sp_label *lab;

while ((lab= li++))
{
if (my_strcasecmp(system_charset_info, name.str, lab->name.str) == 0)
return lab;
}

if (!recusive)
return NULL;

/*
Note about exception handlers.
See SQL:2003 SQL/PSM (ISO/IEC 9075-4:2003),
section 13.1 <compound statement>,
syntax rule 4.
In short, a DECLARE HANDLER block can not refer
to labels from the parent context, as they are out of scope.
*/
if (m_scope == HANDLER_SCOPE && m_parent)
{
if (m_parent->m_parent)
{
// Skip the parent context
return m_parent->m_parent->find_goto_label(name);
}
}

return m_parent && (m_scope == REGULAR_SCOPE) ?
m_parent->find_goto_label(name) :
NULL;
}


sp_label *sp_pcontext::find_label(const LEX_STRING name)
{
Expand Down

0 comments on commit af7f287

Please sign in to comment.