mirror of
https://github.com/MariaDB/server.git
synced 2025-08-01 03:47:19 +03:00
A cleanup for MDEV-10914 ROW data type for stored routine variables
Addressing Monty's review suggestions
This commit is contained in:
@ -187,6 +187,7 @@ rec t1%ROWTYPE;
|
|||||||
BEGIN
|
BEGIN
|
||||||
rec.a:=100;
|
rec.a:=100;
|
||||||
rec.b:=200;
|
rec.b:=200;
|
||||||
|
SELECT rec=ROW(100,200) AS true1, ROW(100,200)=rec AS true2;
|
||||||
INSERT INTO t1 VALUES (rec.a,rec.b);
|
INSERT INTO t1 VALUES (rec.a,rec.b);
|
||||||
INSERT INTO t1 VALUES (10, rec=ROW(100,200));
|
INSERT INTO t1 VALUES (10, rec=ROW(100,200));
|
||||||
INSERT INTO t1 VALUES (10, ROW(100,200)=rec);
|
INSERT INTO t1 VALUES (10, ROW(100,200)=rec);
|
||||||
@ -207,6 +208,8 @@ INSERT INTO t1 SELECT 12, 21 FROM DUAL WHERE ROW(100,200)=rec;
|
|||||||
END;
|
END;
|
||||||
$$
|
$$
|
||||||
CALL p1();
|
CALL p1();
|
||||||
|
true1 true2
|
||||||
|
1 1
|
||||||
SELECT * FROM t1;
|
SELECT * FROM t1;
|
||||||
a b
|
a b
|
||||||
100 200
|
100 200
|
||||||
@ -261,6 +264,7 @@ rec t1%ROWTYPE;
|
|||||||
BEGIN
|
BEGIN
|
||||||
rec.a:=100;
|
rec.a:=100;
|
||||||
rec.b:=200;
|
rec.b:=200;
|
||||||
|
SELECT rec=ROW(100,200) AS true1, ROW(100,200)=rec AS true2;
|
||||||
INSERT INTO t1 VALUES (rec.a,rec.b);
|
INSERT INTO t1 VALUES (rec.a,rec.b);
|
||||||
INSERT INTO t1 VALUES (10, rec=ROW(100,200));
|
INSERT INTO t1 VALUES (10, rec=ROW(100,200));
|
||||||
INSERT INTO t1 VALUES (10, ROW(100,200)=rec);
|
INSERT INTO t1 VALUES (10, ROW(100,200)=rec);
|
||||||
|
@ -93,6 +93,7 @@ AS
|
|||||||
BEGIN
|
BEGIN
|
||||||
rec.a:=100;
|
rec.a:=100;
|
||||||
rec.b:=200;
|
rec.b:=200;
|
||||||
|
SELECT rec=ROW(100,200) AS true1, ROW(100,200)=rec AS true2;
|
||||||
INSERT INTO t1 VALUES (rec.a,rec.b);
|
INSERT INTO t1 VALUES (rec.a,rec.b);
|
||||||
INSERT INTO t1 VALUES (10, rec=ROW(100,200));
|
INSERT INTO t1 VALUES (10, rec=ROW(100,200));
|
||||||
INSERT INTO t1 VALUES (10, ROW(100,200)=rec);
|
INSERT INTO t1 VALUES (10, ROW(100,200)=rec);
|
||||||
|
24
sql/item.cc
24
sql/item.cc
@ -1789,14 +1789,14 @@ bool Item_splocal_row_field::set_value(THD *thd, sp_rcontext *ctx, Item **it)
|
|||||||
bool Item_splocal_row_field_by_name::fix_fields(THD *thd, Item **it)
|
bool Item_splocal_row_field_by_name::fix_fields(THD *thd, Item **it)
|
||||||
{
|
{
|
||||||
m_thd= thd;
|
m_thd= thd;
|
||||||
Item *row= m_thd->spcont->get_item(m_var_idx);
|
Item *item, *row= m_thd->spcont->get_item(m_var_idx);
|
||||||
if (row->element_index_by_name(&m_field_idx, m_field_name))
|
if (row->element_index_by_name(&m_field_idx, m_field_name))
|
||||||
{
|
{
|
||||||
my_error(ER_ROW_VARIABLE_DOES_NOT_HAVE_FIELD, MYF(0),
|
my_error(ER_ROW_VARIABLE_DOES_NOT_HAVE_FIELD, MYF(0),
|
||||||
m_name.str, m_field_name.str);
|
m_name.str, m_field_name.str);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
Item *item= row->element_index(m_field_idx);
|
item= row->element_index(m_field_idx);
|
||||||
set_handler(item->type_handler());
|
set_handler(item->type_handler());
|
||||||
return fix_fields_from_item(thd, it, item);
|
return fix_fields_from_item(thd, it, item);
|
||||||
}
|
}
|
||||||
@ -1804,15 +1804,17 @@ bool Item_splocal_row_field_by_name::fix_fields(THD *thd, Item **it)
|
|||||||
|
|
||||||
void Item_splocal_row_field_by_name::print(String *str, enum_query_type)
|
void Item_splocal_row_field_by_name::print(String *str, enum_query_type)
|
||||||
{
|
{
|
||||||
str->reserve(m_name.length + 2 * m_field_name.length + 8);
|
// +16 should be enough for .NNN@[""]
|
||||||
str->append(m_name.str, m_name.length);
|
if (str->reserve(m_name.length + 2 * m_field_name.length + 16))
|
||||||
str->append('.');
|
return;
|
||||||
str->append(m_field_name.str, m_field_name.length);
|
str->qs_append(m_name.str, m_name.length);
|
||||||
str->append('@');
|
str->qs_append('.');
|
||||||
str->append_ulonglong(m_var_idx);
|
str->qs_append(m_field_name.str, m_field_name.length);
|
||||||
str->append("[\"", 2);
|
str->qs_append('@');
|
||||||
str->append(m_field_name.str, m_field_name.length);
|
str->qs_append(m_var_idx);
|
||||||
str->append("\"]", 2);
|
str->qs_append("[\"", 2);
|
||||||
|
str->qs_append(m_field_name.str, m_field_name.length);
|
||||||
|
str->qs_append("\"]", 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1624,7 +1624,7 @@ public:
|
|||||||
virtual Item* element_index(uint i) { return this; }
|
virtual Item* element_index(uint i) { return this; }
|
||||||
virtual bool element_index_by_name(uint *idx, const LEX_STRING &name) const
|
virtual bool element_index_by_name(uint *idx, const LEX_STRING &name) const
|
||||||
{
|
{
|
||||||
return true;
|
return true; // Error
|
||||||
}
|
}
|
||||||
virtual Item** addr(uint i) { return 0; }
|
virtual Item** addr(uint i) { return 0; }
|
||||||
virtual bool check_cols(uint c);
|
virtual bool check_cols(uint c);
|
||||||
|
@ -3501,11 +3501,11 @@ sp_instr_set_row_field_by_name::print(String *str)
|
|||||||
str->qs_append(STRING_WITH_LEN("set "));
|
str->qs_append(STRING_WITH_LEN("set "));
|
||||||
str->qs_append(var->name.str, var->name.length);
|
str->qs_append(var->name.str, var->name.length);
|
||||||
str->qs_append('.');
|
str->qs_append('.');
|
||||||
str->qs_append(m_field_name.str);
|
str->qs_append(m_field_name.str, m_field_name.length);
|
||||||
str->qs_append('@');
|
str->qs_append('@');
|
||||||
str->qs_append(m_offset);
|
str->qs_append(m_offset);
|
||||||
str->qs_append("[\"",2);
|
str->qs_append("[\"",2);
|
||||||
str->qs_append(m_field_name.str);
|
str->qs_append(m_field_name.str, m_field_name.length);
|
||||||
str->qs_append("\"]",2);
|
str->qs_append("\"]",2);
|
||||||
str->qs_append(' ');
|
str->qs_append(' ');
|
||||||
m_value->print(str, enum_query_type(QT_ORDINARY |
|
m_value->print(str, enum_query_type(QT_ORDINARY |
|
||||||
|
@ -1176,6 +1176,22 @@ public:
|
|||||||
}; // class sp_instr_set_field : public sp_instr_set
|
}; // class sp_instr_set_field : public sp_instr_set
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
This class handles assignment instructions like this:
|
||||||
|
DECLARE
|
||||||
|
CURSOR cur IS SELECT * FROM t1;
|
||||||
|
rec cur%ROWTYPE;
|
||||||
|
BEGIN
|
||||||
|
rec.column1:= 10; -- This instruction
|
||||||
|
END;
|
||||||
|
|
||||||
|
The idea is that during sp_rcontext::create() we do not know the extact
|
||||||
|
structure of "rec". It gets resolved at run time, during the corresponding
|
||||||
|
sp_instr_cursor_copy_struct::exec_core().
|
||||||
|
|
||||||
|
So sp_instr_set_row_field_by_name searches for ROW fields by name,
|
||||||
|
while sp_instr_set_row_field (see above) searches for ROW fields by index.
|
||||||
|
*/
|
||||||
class sp_instr_set_row_field_by_name : public sp_instr_set
|
class sp_instr_set_row_field_by_name : public sp_instr_set
|
||||||
{
|
{
|
||||||
// Prevent use of this
|
// Prevent use of this
|
||||||
|
@ -195,6 +195,11 @@ bool sp_rcontext::resolve_type_ref(THD *thd, Column_definition *def,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
This method resolves the structure of a variable declared as:
|
||||||
|
rec t1%ROWTYPE;
|
||||||
|
It opens the table "t1" and copies its structure to %ROWTYPE variable.
|
||||||
|
*/
|
||||||
bool sp_rcontext::resolve_table_rowtype_ref(THD *thd,
|
bool sp_rcontext::resolve_table_rowtype_ref(THD *thd,
|
||||||
Row_definition_list &defs,
|
Row_definition_list &defs,
|
||||||
Table_ident *ref)
|
Table_ident *ref)
|
||||||
@ -206,6 +211,11 @@ bool sp_rcontext::resolve_table_rowtype_ref(THD *thd,
|
|||||||
LEX *save_lex= thd->lex;
|
LEX *save_lex= thd->lex;
|
||||||
bool rc= true;
|
bool rc= true;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Create a temporary LEX on stack and switch to it.
|
||||||
|
In case of VIEW, open_tables_only_view_structure() will open more
|
||||||
|
tables/views recursively. We want to avoid them to stick to the current LEX.
|
||||||
|
*/
|
||||||
sp_lex_local lex(thd, thd->lex);
|
sp_lex_local lex(thd, thd->lex);
|
||||||
thd->lex= &lex;
|
thd->lex= &lex;
|
||||||
|
|
||||||
@ -228,11 +238,12 @@ bool sp_rcontext::resolve_table_rowtype_ref(THD *thd,
|
|||||||
in the end of this method.
|
in the end of this method.
|
||||||
*/
|
*/
|
||||||
LEX_CSTRING tmp= {src[0]->field_name, strlen(src[0]->field_name)};
|
LEX_CSTRING tmp= {src[0]->field_name, strlen(src[0]->field_name)};
|
||||||
|
Spvar_definition *def;
|
||||||
if ((rc= check_column_grant_for_type_ref(thd, table_list,
|
if ((rc= check_column_grant_for_type_ref(thd, table_list,
|
||||||
tmp.str, tmp.length)) ||
|
tmp.str, tmp.length)) ||
|
||||||
(rc= !(src[0]->field_name= thd->strmake(tmp.str, tmp.length))))
|
(rc= !(src[0]->field_name= thd->strmake(tmp.str, tmp.length))) ||
|
||||||
|
(rc= !(def= new (thd->mem_root) Spvar_definition(thd, *src))))
|
||||||
break;
|
break;
|
||||||
Spvar_definition *def= new (thd->mem_root) Spvar_definition(thd, *src);
|
|
||||||
src[0]->field_name= tmp.str; // Restore field name, just in case.
|
src[0]->field_name= tmp.str; // Restore field name, just in case.
|
||||||
def->flags&= (uint) ~NOT_NULL_FLAG;
|
def->flags&= (uint) ~NOT_NULL_FLAG;
|
||||||
if ((rc= def->sp_prepare_create_field(thd, thd->mem_root)))
|
if ((rc= def->sp_prepare_create_field(thd, thd->mem_root)))
|
||||||
@ -884,6 +895,16 @@ bool sp_cursor::Select_fetch_into_spvars::
|
|||||||
int sp_cursor::Select_fetch_into_spvars::send_data(List<Item> &items)
|
int sp_cursor::Select_fetch_into_spvars::send_data(List<Item> &items)
|
||||||
{
|
{
|
||||||
Item *item;
|
Item *item;
|
||||||
|
/*
|
||||||
|
If we have only one variable in spvar_list, and this is a ROW variable,
|
||||||
|
and the number of fields in the ROW variable matches the number of
|
||||||
|
fields in the query result, we fetch to this ROW variable.
|
||||||
|
|
||||||
|
If there is one variable, and it is a ROW variable, but its number
|
||||||
|
of fields does not match the number of fields in the query result,
|
||||||
|
we go through send_data_to_variable_list(). It will report an error
|
||||||
|
on attempt to assign a scalar value to a ROW variable.
|
||||||
|
*/
|
||||||
return spvar_list->elements == 1 &&
|
return spvar_list->elements == 1 &&
|
||||||
(item= thd->spcont->get_item(spvar_list->head()->offset)) &&
|
(item= thd->spcont->get_item(spvar_list->head()->offset)) &&
|
||||||
item->type_handler() == &type_handler_row &&
|
item->type_handler() == &type_handler_row &&
|
||||||
|
@ -5282,6 +5282,15 @@ bool LEX::sp_variable_declarations_finalize(THD *thd, int nvars,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Finalize a %ROWTYPE declaration, e.g.:
|
||||||
|
DECLARE a,b,c,d t1%ROWTYPE := ROW(1,2,3);
|
||||||
|
|
||||||
|
@param thd - the current thd
|
||||||
|
@param nvars - the number of variables in the declaration
|
||||||
|
@param ref - the table or cursor name (see comments below)
|
||||||
|
@param def - the default value, e.g., ROW(1,2,3), or NULL (no default).
|
||||||
|
*/
|
||||||
bool
|
bool
|
||||||
LEX::sp_variable_declarations_rowtype_finalize(THD *thd, int nvars,
|
LEX::sp_variable_declarations_rowtype_finalize(THD *thd, int nvars,
|
||||||
Qualified_column_ident *ref,
|
Qualified_column_ident *ref,
|
||||||
@ -5295,6 +5304,7 @@ LEX::sp_variable_declarations_rowtype_finalize(THD *thd, int nvars,
|
|||||||
if (!def && !(def= new (thd->mem_root) Item_null(thd)))
|
if (!def && !(def= new (thd->mem_root) Item_null(thd)))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
// Loop through all variables in the same declaration
|
||||||
for (uint i= num_vars - nvars; i < num_vars; i++)
|
for (uint i= num_vars - nvars; i < num_vars; i++)
|
||||||
{
|
{
|
||||||
bool last= i == num_vars - 1;
|
bool last= i == num_vars - 1;
|
||||||
|
Reference in New Issue
Block a user