Skip to content

Commit ec19e48

Browse files
author
Alexander Barkov
committed
MDEV-12314 Implicit cursor FOR LOOP for cursors with parameters
1 parent e045194 commit ec19e48

File tree

7 files changed

+167
-16
lines changed

7 files changed

+167
-16
lines changed

mysql-test/suite/compat/oracle/r/sp-cursor-rowtype.result

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1134,6 +1134,35 @@ DROP TABLE t3;
11341134
DROP TABLE t2;
11351135
DROP TABLE t1;
11361136
#
1137+
# MDEV-12314 Implicit cursor FOR LOOP for cursors with parameters
1138+
#
1139+
CREATE TABLE t1 (a INT, b VARCHAR(32));
1140+
INSERT INTO t1 VALUES (10,'b0');
1141+
INSERT INTO t1 VALUES (11,'b1');
1142+
INSERT INTO t1 VALUES (12,'b2');
1143+
CREATE PROCEDURE p1(pa INT, pb VARCHAR(32)) AS
1144+
CURSOR cur(va INT, vb VARCHAR(32)) IS
1145+
SELECT a, b FROM t1 WHERE a=va AND b=vb;
1146+
BEGIN
1147+
FOR rec IN cur(pa,pb)
1148+
LOOP
1149+
SELECT rec.a, rec.b;
1150+
END LOOP;
1151+
END;
1152+
$$
1153+
CALL p1(10,'B0');
1154+
rec.a rec.b
1155+
10 b0
1156+
CALL p1(11,'B1');
1157+
rec.a rec.b
1158+
11 b1
1159+
CALL p1(12,'B2');
1160+
rec.a rec.b
1161+
12 b2
1162+
CALL p1(12,'non-existing');
1163+
DROP TABLE t1;
1164+
DROP PROCEDURE p1;
1165+
#
11371166
# MDEV-12098 sql_mode=ORACLE: Implicit cursor FOR loop
11381167
#
11391168
# Parse error in the cursor SELECT statement

mysql-test/suite/compat/oracle/t/sp-cursor-rowtype.test

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1230,6 +1230,34 @@ DROP TABLE t2;
12301230
DROP TABLE t1;
12311231

12321232

1233+
--echo #
1234+
--echo # MDEV-12314 Implicit cursor FOR LOOP for cursors with parameters
1235+
--echo #
1236+
1237+
CREATE TABLE t1 (a INT, b VARCHAR(32));
1238+
INSERT INTO t1 VALUES (10,'b0');
1239+
INSERT INTO t1 VALUES (11,'b1');
1240+
INSERT INTO t1 VALUES (12,'b2');
1241+
DELIMITER $$;
1242+
CREATE PROCEDURE p1(pa INT, pb VARCHAR(32)) AS
1243+
CURSOR cur(va INT, vb VARCHAR(32)) IS
1244+
SELECT a, b FROM t1 WHERE a=va AND b=vb;
1245+
BEGIN
1246+
FOR rec IN cur(pa,pb)
1247+
LOOP
1248+
SELECT rec.a, rec.b;
1249+
END LOOP;
1250+
END;
1251+
$$
1252+
DELIMITER ;$$
1253+
CALL p1(10,'B0');
1254+
CALL p1(11,'B1');
1255+
CALL p1(12,'B2');
1256+
CALL p1(12,'non-existing');
1257+
DROP TABLE t1;
1258+
DROP PROCEDURE p1;
1259+
1260+
12331261
--echo #
12341262
--echo # MDEV-12098 sql_mode=ORACLE: Implicit cursor FOR loop
12351263
--echo #

sql/item_func.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2641,6 +2641,10 @@ class Item_func_sp :public Item_func
26412641
{
26422642
return sp_result_field;
26432643
}
2644+
const sp_name *get_sp_name() const
2645+
{
2646+
return m_name;
2647+
}
26442648

26452649
bool check_vcol_func_processor(void *arg);
26462650
bool limit_index_condition_pushdown_processor(void *opt_arg)

sql/sp_head.cc

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4612,14 +4612,16 @@ Item *sp_head::adjust_assignment_source(THD *thd, Item *val, Item *val2)
46124612

46134613
bool
46144614
sp_head::set_local_variable(THD *thd, sp_pcontext *spcont,
4615-
sp_variable *spv, Item *val, LEX *lex)
4615+
sp_variable *spv, Item *val, LEX *lex,
4616+
bool responsible_to_free_lex)
46164617
{
46174618
if (!(val= adjust_assignment_source(thd, val, spv->default_value)))
46184619
return true;
46194620

46204621
sp_instr_set *sp_set= new (thd->mem_root)
46214622
sp_instr_set(instructions(), spcont,
4622-
spv->offset, val, lex, true);
4623+
spv->offset, val, lex,
4624+
responsible_to_free_lex);
46234625

46244626
return sp_set == NULL || add_instr(sp_set);
46254627
}
@@ -4689,8 +4691,15 @@ bool sp_head::add_open_cursor(THD *thd, sp_pcontext *spcont, uint offset,
46894691

46904692
bool sp_head::add_for_loop_open_cursor(THD *thd, sp_pcontext *spcont,
46914693
sp_variable *index,
4692-
const sp_pcursor *pcursor, uint coffset)
4694+
const sp_pcursor *pcursor, uint coffset,
4695+
sp_assignment_lex *param_lex,
4696+
Item_args *parameters)
46934697
{
4698+
if (parameters &&
4699+
add_set_for_loop_cursor_param_variables(thd, pcursor->param_context(),
4700+
param_lex, parameters))
4701+
return true;
4702+
46944703
sp_instr *instr_copy_struct=
46954704
new (thd->mem_root) sp_instr_cursor_copy_struct(instructions(),
46964705
spcont, pcursor->lex(),
@@ -4711,3 +4720,28 @@ bool sp_head::add_for_loop_open_cursor(THD *thd, sp_pcontext *spcont,
47114720
instr_cfetch->add_to_varlist(index);
47124721
return false;
47134722
}
4723+
4724+
4725+
bool
4726+
sp_head::add_set_for_loop_cursor_param_variables(THD *thd,
4727+
sp_pcontext *param_spcont,
4728+
sp_assignment_lex *param_lex,
4729+
Item_args *parameters)
4730+
{
4731+
DBUG_ASSERT(param_spcont->context_var_count() == parameters->argument_count());
4732+
for (uint idx= 0; idx < parameters->argument_count(); idx ++)
4733+
{
4734+
/*
4735+
param_lex is shared between multiple items (cursor parameters).
4736+
Only the last sp_instr_set is responsible for freeing param_lex.
4737+
See more comments in LEX::sp_for_loop_cursor_declarations in sql_lex.cc.
4738+
*/
4739+
bool last= idx + 1 == parameters->argument_count();
4740+
sp_variable *spvar= param_spcont->find_context_variable(idx);
4741+
if (set_local_variable(thd, param_spcont,
4742+
spvar, parameters->arguments()[idx],
4743+
param_lex, last))
4744+
return true;
4745+
}
4746+
return false;
4747+
}

sql/sp_head.h

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -363,8 +363,20 @@ class sp_head :private Query_arena,
363363
}
364364

365365
Item *adjust_assignment_source(THD *thd, Item *val, Item *val2);
366+
/**
367+
@param thd - the current thd
368+
@param spcont - the current parse context
369+
@param spv - the SP variable
370+
@param val - the value to be assigned to the variable
371+
@param lex - the LEX that was used to create "val"
372+
@param responsible_to_free_lex - if the generated sp_instr_set should
373+
free "lex".
374+
@retval true - on error
375+
@retval false - on success
376+
*/
366377
bool set_local_variable(THD *thd, sp_pcontext *spcont,
367-
sp_variable *spv, Item *val, LEX *lex);
378+
sp_variable *spv, Item *val, LEX *lex,
379+
bool responsible_to_free_lex);
368380
bool set_local_variable_row_field(THD *thd, sp_pcontext *spcont,
369381
sp_variable *spv, uint field_idx,
370382
Item *val, LEX *lex);
@@ -394,7 +406,7 @@ class sp_head :private Query_arena,
394406
*/
395407
DBUG_ASSERT(m_thd->free_list == NULL);
396408
m_thd->free_list= prm->get_free_list();
397-
if (set_local_variable(thd, param_spcont, spvar, prm->get_item(), prm))
409+
if (set_local_variable(thd, param_spcont, spvar, prm->get_item(), prm, true))
398410
return true;
399411
/*
400412
Safety:
@@ -429,6 +441,15 @@ class sp_head :private Query_arena,
429441
return false;
430442
}
431443

444+
/**
445+
Generate a code to set all cursor parameter variables for a FOR LOOP, e.g.:
446+
FOR index IN cursor(1,2,3)
447+
@param
448+
*/
449+
bool add_set_for_loop_cursor_param_variables(THD *thd,
450+
sp_pcontext *param_spcont,
451+
sp_assignment_lex *param_lex,
452+
Item_args *parameters);
432453

433454
public:
434455
/**
@@ -451,9 +472,11 @@ class sp_head :private Query_arena,
451472

452473
/**
453474
Generate an initiation code for a CURSOR FOR LOOP, e.g.:
454-
FOR index IN cursor
475+
FOR index IN cursor -- cursor without parameters
476+
FOR index IN cursor(1,2,3) -- cursor with parameters
455477
456478
The code generated by this method does the following during SP run-time:
479+
- Sets all cursor parameter vartiables from "parameters"
457480
- Initializes the index ROW-type variable from the cursor
458481
(the structure is copied from the cursor to the index variable)
459482
- The cursor gets opened
@@ -464,13 +487,16 @@ class sp_head :private Query_arena,
464487
@param index - the loop "index" ROW-type variable
465488
@param pcursor - the cursor
466489
@param coffset - the cursor offset
490+
@param param_lex - the LEX that owns Items in "parameters"
491+
@param parameters - the cursor parameters Item array
467492
@retval true - on error (EOM)
468493
@retval false - on success
469494
*/
470495
bool add_for_loop_open_cursor(THD *thd, sp_pcontext *spcont,
471496
sp_variable *index,
472-
const sp_pcursor *pcursor, uint coffset);
473-
497+
const sp_pcursor *pcursor, uint coffset,
498+
sp_assignment_lex *param_lex,
499+
Item_args *parameters);
474500
/**
475501
Returns true if any substatement in the routine directly
476502
(not through another routine) modifies data/changes table.

sql/sql_lex.cc

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5436,7 +5436,9 @@ sp_variable *
54365436
LEX::sp_add_for_loop_cursor_variable(THD *thd,
54375437
const LEX_STRING name,
54385438
const sp_pcursor *pcursor,
5439-
uint coffset)
5439+
uint coffset,
5440+
sp_assignment_lex *param_lex,
5441+
Item_args *parameters)
54405442
{
54415443
sp_variable *spvar= spcont->add_variable(thd, name);
54425444
spcont->declare_var_boundary(1);
@@ -5448,7 +5450,8 @@ LEX::sp_add_for_loop_cursor_variable(THD *thd,
54485450
return NULL;
54495451
spvar->field_def.set_cursor_rowtype_ref(ref);
54505452

5451-
if (sphead->add_for_loop_open_cursor(thd, spcont, spvar, pcursor, coffset))
5453+
if (sphead->add_for_loop_open_cursor(thd, spcont, spvar, pcursor, coffset,
5454+
param_lex, parameters))
54525455
return NULL;
54535456

54545457
spcont->declare_var_boundary(0);
@@ -5538,8 +5541,9 @@ bool LEX::sp_for_loop_cursor_declarations(THD *thd,
55385541
Item *item= bounds.m_index->get_item();
55395542
Item_splocal *item_splocal;
55405543
Item_field *item_field;
5544+
Item_func_sp *item_func_sp= NULL;
55415545
LEX_STRING name;
5542-
uint coffs;
5546+
uint coffs, param_count= 0;
55435547
const sp_pcursor *pcursor;
55445548

55455549
if ((item_splocal= item->get_item_splocal()))
@@ -5553,16 +5557,40 @@ bool LEX::sp_for_loop_cursor_declarations(THD *thd,
55535557
name.str= (char *) item_field->field_name;
55545558
name.length= strlen(item_field->field_name);
55555559
}
5560+
else if (item->type() == Item::FUNC_ITEM &&
5561+
static_cast<Item_func*>(item)->functype() == Item_func::FUNC_SP &&
5562+
!static_cast<Item_func_sp*>(item)->get_sp_name()->m_explicit_name)
5563+
{
5564+
/*
5565+
When a FOR LOOP for a cursor with parameters is parsed:
5566+
FOR index IN cursor(1,2,3) LOOP
5567+
statements;
5568+
END LOOP;
5569+
the parser scans "cursor(1,2,3)" using the "expr" rule,
5570+
so it thinks that cursor(1,2,3) is a stored function call.
5571+
It's not easy to implement this without using "expr" because
5572+
of grammar conflicts.
5573+
As a side effect, the Item_func_sp and its arguments in the parentheses
5574+
belong to the same LEX. This is different from an explicit
5575+
"OPEN cursor(1,2,3)" where every expression belongs to a separate LEX.
5576+
*/
5577+
item_func_sp= static_cast<Item_func_sp*>(item);
5578+
name= item_func_sp->get_sp_name()->m_name;
5579+
param_count= item_func_sp->argument_count();
5580+
}
55565581
else
55575582
{
55585583
thd->parse_error();
55595584
return true;
55605585
}
5561-
if (!(pcursor= spcont->find_cursor_with_error(name, &coffs, false)))
5586+
if (!(pcursor= spcont->find_cursor_with_error(name, &coffs, false)) ||
5587+
pcursor->check_param_count_with_error(param_count))
55625588
return true;
55635589

55645590
if (!(loop->m_index= sp_add_for_loop_cursor_variable(thd, index,
5565-
pcursor, coffs)))
5591+
pcursor, coffs,
5592+
bounds.m_index,
5593+
item_func_sp)))
55665594
return true;
55675595
loop->m_upper_bound= NULL;
55685596
loop->m_direction= bounds.m_direction;
@@ -5590,7 +5618,7 @@ bool LEX::sp_for_loop_increment(THD *thd, const Lex_for_loop_st &loop)
55905618
return true;
55915619
Item *expr= new (thd->mem_root) Item_func_plus(thd, splocal, inc);
55925620
if (!expr ||
5593-
sphead->set_local_variable(thd, spcont, loop.m_index, expr, this))
5621+
sphead->set_local_variable(thd, spcont, loop.m_index, expr, this, true))
55945622
return true;
55955623
return false;
55965624
}
@@ -6464,7 +6492,7 @@ bool LEX::set_variable(struct sys_var_with_base *variable, Item *item)
64646492
sp_variable *spv= spcont->find_variable(variable->base_name, false);
64656493
DBUG_ASSERT(spv);
64666494
/* It is a local variable. */
6467-
return sphead->set_local_variable(thd, spcont, spv, item, this);
6495+
return sphead->set_local_variable(thd, spcont, spv, item, this, true);
64686496
}
64696497

64706498

sql/sql_lex.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3376,7 +3376,9 @@ struct LEX: public Query_tables_list
33763376
sp_variable *sp_add_for_loop_cursor_variable(THD *thd,
33773377
const LEX_STRING name,
33783378
const class sp_pcursor *cur,
3379-
uint coffset);
3379+
uint coffset,
3380+
sp_assignment_lex *param_lex,
3381+
Item_args *parameters);
33803382
bool sp_for_loop_cursor_condition_test(THD *thd, const Lex_for_loop_st &loop);
33813383
bool sp_for_loop_cursor_finalize(THD *thd, const Lex_for_loop_st &);
33823384

0 commit comments

Comments
 (0)