MDEV-36705 Preparations for associative arrays (MDEV-34319)

- Moving the definition of "class Type_handler_row" into a new file
  sql_type_row.h. Also moving *some* of its methods into sql_type_row.cc.
  The rest of the methods will be moved in the patch for MDEV-34319.
  Moving the definition of my_var_sp_row_field into sql_type_row.cc.

- Fixing the grammar for function_call_generic to get the first
  production as "ident_cli_func" rather than "ident_func".
  The upcoming patch needs to know the position of the function name
  within the client query.

- Adding new data types to store data types defined by "TYPE" declarations:

  * sp_type_def
  * sp_type_def_list

  sp_pcontext now derives from sp_type_def_list

- A new virtual method in Field:

  virtual Item_field *make_item_field_spvar(THD *thd,
                                            const Spvar_definition &def);
  Using it in sp_rcontext::init_var_items().

- Fixing my_var_sp to get sp_rcontext_addr in the parameter
  instead of two separate parameters (rcontext_handler + offset).

- Adding new virtual methods in my_var:

  virtual bool set_row(THD *thd, List<Item> &select_list);

  It's used when a select_list record is assigned to a
  single composite variable, such as ROW, specified in the INTO clause.
  Using it in select_dumpvar::send_data().

  virtual bool check_assignability(THD *thd,
                                   const List<Item> &select_list,
                                   bool *assign_as_row) const;

  It's used to check if the select_list is compatible with
  a single INTO variable, in select_dumpvar::prepare().

- Fixing LEX methods create_outvar() to get identifiers
  a Lex_ident_sys_st values instead of generic LEX_CSTRING values.

- Adding virtual methods in Type_handler:

  // Used in Item_func_null_predicate::check_arguments()
  virtual bool has_null_predicate() const;

  // Used in LEX::sp_variable_declarations_finalize()
  virtual bool sp_variable_declarations_finalize(THD *thd,
                                                 LEX *lex, int nvars,
                                                 const Column_definition &def)
                                                                        const;
  // Handle SELECT 1 INTO spvar;
  virtual my_var *make_outvar(THD *thd,
                              const Lex_ident_sys_st &name,
                              const sp_rcontext_addr &addr,
                              sp_head *sphead,
                              bool validate_only) const;

  // Handle SELECT 1 INTO spvar.field;
  virtual my_var *make_outvar_field(THD *thd,
                                    const Lex_ident_sys_st &name,
                                    const sp_rcontext_addr &addr,
                                    const Lex_ident_sys_st &field,
                                    sp_head *sphead,
                                    bool validate_only) const;

  // create the value in: DECLARE var rec_t DEFAULT rec_t(1,'c');
  virtual Item *make_typedef_constructor_item(THD *thd,
                                              const sp_type_def &def,
                                              List<Item> *arg_list) const;

- A new helper method:
  Row_definition_list *Row_definition_list::deep_copy(THD *thd) const;
This commit is contained in:
Alexander Barkov 2025-04-29 14:51:57 +04:00 committed by Sergei Golubchik
parent c8f527a5dd
commit 9e13cf0862
19 changed files with 1058 additions and 621 deletions

View File

@ -136,6 +136,7 @@ SET(SQL_EMBEDDED_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc
../sql/sql_schema.cc
../sql/lex_charset.cc ../sql/charset_collations.cc
../sql/sql_type.cc ../sql/sql_type.h
../sql/sql_type_row.cc
../sql/sql_mode.cc
../sql/sql_type_string.cc
../sql/sql_type_json.cc

View File

@ -195,6 +195,7 @@ SET (SQL_SOURCE
opt_vcol_substitution.cc
opt_hints_parser.cc opt_hints_parser.h scan_char.h
opt_hints.cc opt_hints.h
sql_type_row.cc
${CMAKE_CURRENT_BINARY_DIR}/lex_hash.h
${CMAKE_CURRENT_BINARY_DIR}/lex_token.h
${GEN_SOURCES}

View File

@ -1077,6 +1077,12 @@ void Field::make_sort_key_part(uchar *buff,uint length)
}
Item_field *Field::make_item_field_spvar(THD *thd, const Spvar_definition &def)
{
return new (thd->mem_root) Item_field(thd, this);
}
/*
@brief
Create a packed sort key part

View File

@ -2035,6 +2035,8 @@ public:
{
return const_item;
}
virtual Item_field *make_item_field_spvar(THD *thd,
const Spvar_definition &def);
virtual Data_type_compatibility can_optimize_keypart_ref(
const Item_bool_func *cond,
const Item *item) const;
@ -5301,6 +5303,8 @@ public:
return m_table;
}
Virtual_tmp_table **virtual_tmp_table_addr() override { return &m_table; }
Item_field *make_item_field_spvar(THD *thd,
const Spvar_definition &def) override;
bool row_create_fields(THD *thd, List<Spvar_definition> *list);
bool row_create_fields(THD *thd, const Spvar_definition &def);
bool sp_prepare_and_store_item(THD *thd, Item **value) override;
@ -5708,6 +5712,7 @@ public:
bool adjust_formal_params_to_actual_params(THD *thd,
Item **args, uint arg_count);
bool resolve_type_refs(THD *);
Row_definition_list *deep_copy(THD *thd) const;
};
/**
@ -5826,7 +5831,6 @@ public:
m_row_field_definitions= list;
}
class Item_field_row *make_item_field_row(THD *thd, Field_row *field);
};

View File

@ -5834,6 +5834,17 @@ bool Item_func_null_predicate::count_sargable_conds(void *arg)
}
bool Item_func_null_predicate::check_arguments() const
{
DBUG_ASSERT(arg_count == 1);
if (args[0]->type_handler()->has_null_predicate())
return false;
my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0),
args[0]->type_handler()->name().ptr(), func_name());
return true;
}
bool Item_func_isnull::val_bool()
{
DBUG_ASSERT(fixed());

View File

@ -2824,6 +2824,7 @@ protected:
Item_func::Functype type, Item *value) override;
public:
Item_func_null_predicate(THD *thd, Item *a): Item_bool_func(thd, a) { }
bool check_arguments() const override;
void add_key_fields(JOIN *join, KEY_FIELD **key_fields, uint *and_level,
table_map usable_tables, SARGABLE_PARAM **sargables)
override;

View File

@ -75,6 +75,11 @@ bool sp_condition_value::equals(const sp_condition_value *cv) const
}
sp_type_def_list::sp_type_def_list()
:m_type_defs(PSI_INSTRUMENT_MEM)
{ }
void sp_pcontext::init(uint var_offset,
uint cursor_offset,
int num_case_expressions)
@ -94,7 +99,7 @@ sp_pcontext::sp_pcontext()
m_parent(NULL), m_pboundary(0),
m_vars(PSI_INSTRUMENT_MEM), m_case_expr_ids(PSI_INSTRUMENT_MEM),
m_conditions(PSI_INSTRUMENT_MEM), m_cursors(PSI_INSTRUMENT_MEM),
m_handlers(PSI_INSTRUMENT_MEM), m_records(PSI_INSTRUMENT_MEM),
m_handlers(PSI_INSTRUMENT_MEM),
m_children(PSI_INSTRUMENT_MEM), m_scope(REGULAR_SCOPE)
{
init(0, 0, 0);
@ -107,7 +112,7 @@ sp_pcontext::sp_pcontext(sp_pcontext *prev, sp_pcontext::enum_scope scope)
m_parent(prev), m_pboundary(0),
m_vars(PSI_INSTRUMENT_MEM), m_case_expr_ids(PSI_INSTRUMENT_MEM),
m_conditions(PSI_INSTRUMENT_MEM), m_cursors(PSI_INSTRUMENT_MEM),
m_handlers(PSI_INSTRUMENT_MEM), m_records(PSI_INSTRUMENT_MEM),
m_handlers(PSI_INSTRUMENT_MEM),
m_children(PSI_INSTRUMENT_MEM), m_scope(scope)
{
init(prev->m_var_offset + prev->m_max_var_index,
@ -427,35 +432,28 @@ sp_condition_value *sp_pcontext::find_condition(const LEX_CSTRING *name,
}
bool sp_pcontext::add_record(THD *thd, const Lex_ident_column &name,
Row_definition_list *field)
bool sp_type_def_list::type_defs_add_record(THD *thd,
const Lex_ident_column &name,
Row_definition_list *field)
{
sp_record *p= new (thd->mem_root) sp_record(name, field);
auto p= new (thd->mem_root) sp_type_def_record(name, field);
if (p == NULL)
if (p == nullptr)
return true;
return m_records.append(p);
return m_type_defs.append(p);
}
sp_record *sp_pcontext::find_record(const LEX_CSTRING *name,
bool current_scope_only) const
sp_type_def *sp_pcontext::find_type_def(const LEX_CSTRING &name,
bool current_scope_only) const
{
size_t i= m_records.elements();
while (i--)
{
sp_record *p= m_records.at(i);
if (p->eq_name(name))
{
return p;
}
}
auto p= sp_type_def_list::find_type_def(name);
if (p)
return p;
return (!current_scope_only && m_parent) ?
m_parent->find_record(name, false) :
m_parent->find_type_def(name, false) :
NULL;
}

View File

@ -21,6 +21,7 @@
#include "sql_string.h" // LEX_STRING
#include "field.h" // Create_field
#include "sql_array.h" // Dynamic_array
#include "sp_type_def.h"
/// This class represents a stored program variable or a parameter
@ -391,7 +392,8 @@ public:
/// - for error checking (e.g. to check correct number of parameters);
/// - to resolve SQL-handlers.
class sp_pcontext : public Sql_alloc
class sp_pcontext : public Sql_alloc,
public sp_type_def_list
{
public:
enum enum_scope
@ -752,27 +754,23 @@ public:
return m_scope;
}
sp_type_def *find_type_def(const LEX_CSTRING &name,
bool current_scope_only) const;
/////////////////////////////////////////////////////////////////////////
// Record.
/////////////////////////////////////////////////////////////////////////
bool add_record(THD *thd,
const Lex_ident_column &name,
Row_definition_list *field);
sp_record *find_record(const LEX_CSTRING *name,
bool current_scope_only) const;
bool declare_record(THD *thd,
const Lex_ident_column &name,
Row_definition_list *field)
bool type_defs_declare_record(THD *thd,
const Lex_ident_column &name,
Row_definition_list *field)
{
if (find_record(&name, true))
if (unlikely(find_type_def(name, true)))
{
my_error(ER_SP_DUP_DECL, MYF(0), name.str);
return true;
}
return add_record(thd, name, field);
return type_defs_add_record(thd, name, field);
}
private:
@ -839,9 +837,6 @@ private:
/// Stack of SQL-handlers.
Dynamic_array<sp_handler *> m_handlers;
/// Stack of records.
Dynamic_array<sp_record *> m_records;
/*
In the below example the label <<lab>> has two meanings:
- GOTO lab : must go before the beginning of the loop

View File

@ -170,6 +170,29 @@ sp_rcontext *sp_rcontext::create(THD *thd,
}
/*
Create a deep copy.
Used e.g. for "TYPE IS RECORD" variables.
*/
Row_definition_list *Row_definition_list::deep_copy(THD *thd) const
{
Row_definition_list *row= new (thd->mem_root) Row_definition_list();
if (unlikely(row == NULL))
return nullptr;
// Create a deep copy of the elements
List_iterator<Spvar_definition> it(*const_cast<Row_definition_list*>(this));
for (Spvar_definition *def= it++; def; def= it++)
{
Spvar_definition *new_def= new (thd->mem_root) Spvar_definition(*def);
if (unlikely(new_def == NULL) ||
row->push_back(new_def, thd->mem_root))
return nullptr;
}
return row;
}
bool Row_definition_list::append_uniq(MEM_ROOT *mem_root, Spvar_definition *var)
{
DBUG_ASSERT(elements);
@ -402,25 +425,6 @@ bool Row_definition_list::resolve_type_refs(THD *thd)
};
Item_field_row *Spvar_definition::make_item_field_row(THD *thd,
Field_row *field)
{
Item_field_row *item= new (thd->mem_root) Item_field_row(thd, field);
if (!item)
return nullptr;
if (field->row_create_fields(thd, *this))
return nullptr;
// field->virtual_tmp_table() returns nullptr in case of ROW TYPE OF cursor
if (field->virtual_tmp_table() &&
item->add_array_of_item_field(thd, *field->virtual_tmp_table()))
return nullptr;
return item;
}
bool sp_rcontext::init_var_items(THD *thd,
List<Spvar_definition> &field_def_lst)
{
@ -438,10 +442,7 @@ bool sp_rcontext::init_var_items(THD *thd,
for (uint idx= 0; idx < num_vars; ++idx, def= it++)
{
Field *field= m_var_table->field[idx];
Field_row *field_row= dynamic_cast<Field_row*>(field);
if (!(m_var_items[idx]= field_row ?
def->make_item_field_row(thd, field_row) :
new (thd->mem_root) Item_field(thd, field)))
if (!(m_var_items[idx]= field->make_item_field_spvar(thd, *def)))
return true;
}
return false;

98
sql/sp_type_def.h Normal file
View File

@ -0,0 +1,98 @@
/*
Copyright (c) 2025, Rakuten Securities
Copyright (c) 2025, MariaDB plc
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; version 2 of
the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA
*/
#ifndef SQL_TYPE_DEF_H
#define SQL_TYPE_DEF_H
#include "sql_type.h"
/*
This class represents a type definition in a stored program.
*/
class sp_type_def : public Sql_alloc,
public Type_handler_hybrid_field_type
{
protected:
/// Name of the type.
Lex_ident_column m_name;
public:
sp_type_def(const Lex_ident_column &name_arg, const Type_handler *th)
:Sql_alloc(),
Type_handler_hybrid_field_type(th),
m_name(name_arg)
{ }
bool eq_name(const LEX_CSTRING &name) const
{
return m_name.streq(name);
}
const Lex_ident_column &get_name() const
{
return m_name;
}
Item *make_constructor_item(THD *thd, List<Item> *args) const
{
return type_handler()->make_typedef_constructor_item(thd, *this, args);
}
};
/*
This class represents 'DECLARE RECORD' statement.
*/
class sp_type_def_record : public sp_type_def
{
public:
Row_definition_list *field;
public:
sp_type_def_record(const Lex_ident_column &name_arg,
Row_definition_list *prmfield)
:sp_type_def(name_arg, &type_handler_row),
field(prmfield)
{ }
};
class sp_type_def_list
{
protected:
/// Stack of type definitions.
Dynamic_array<sp_type_def *> m_type_defs;
public:
sp_type_def_list();
sp_type_def *find_type_def(const LEX_CSTRING &name) const
{
for (uint i= 0; i < m_type_defs.elements(); i++)
{
sp_type_def *p= m_type_defs.at(i);
if (p->eq_name(name))
return p;
}
return nullptr;
}
bool type_defs_add_record(THD *thd, const Lex_ident_column &name,
Row_definition_list *field);
};
#endif // SQL_TYPE_DEF_H

View File

@ -4110,14 +4110,13 @@ int select_dumpvar::prepare(List<Item> &list, SELECT_LEX_UNIT *u)
m_var_sp_row= NULL;
if (var_list.elements == 1 &&
(mvsp= var_list.head()->get_my_var_sp()) &&
mvsp->type_handler() == &type_handler_row)
(mvsp= var_list.head()->get_my_var_sp()))
{
// SELECT INTO row_type_sp_variable
if (mvsp->get_rcontext(thd->spcont)->get_variable(mvsp->offset)->cols() !=
list.elements)
bool assign_as_row= false;
if (mvsp->check_assignability(thd, list, &assign_as_row))
goto error;
m_var_sp_row= mvsp;
if (assign_as_row)
m_var_sp_row= mvsp;
return 0;
}
@ -4596,13 +4595,7 @@ sp_rcontext *my_var_sp::get_rcontext(sp_rcontext *local_ctx) const
bool my_var_sp::set(THD *thd, Item *item)
{
return get_rcontext(thd->spcont)->set_variable(thd, offset, &item);
}
bool my_var_sp_row_field::set(THD *thd, Item *item)
{
return get_rcontext(thd->spcont)->
set_variable_row_field(thd, offset, m_field_offset, &item);
return get_rcontext(thd->spcont)->set_variable(thd, offset(), &item);
}
@ -4643,10 +4636,12 @@ int select_dumpvar::send_data(List<Item> &items)
my_message(ER_TOO_MANY_ROWS, ER_THD(thd, ER_TOO_MANY_ROWS), MYF(0));
DBUG_RETURN(1);
}
if (m_var_sp_row ?
m_var_sp_row->get_rcontext(thd->spcont)->
set_variable_row(thd, m_var_sp_row->offset, items) :
send_data_to_var_list(items))
if (m_var_sp_row)
{
if (m_var_sp_row->set_row(thd, items))
DBUG_RETURN(1);
}
else if (send_data_to_var_list(items))
DBUG_RETURN(1);
DBUG_RETURN(thd->is_error());

View File

@ -7538,47 +7538,53 @@ public:
virtual my_var_sp *get_my_var_sp() { return NULL; }
};
class my_var_sp: public my_var {
const Sp_rcontext_handler *m_rcontext_handler;
class my_var_sp: public my_var,
public sp_rcontext_addr
{
const Type_handler *m_type_handler;
public:
uint offset;
/*
Routine to which this Item_splocal belongs. Used for checking if correct
runtime context is used for variable handling.
*/
sp_head *sp;
my_var_sp(const Sp_rcontext_handler *rcontext_handler,
const LEX_CSTRING *j, uint o, const Type_handler *type_handler,
sp_head *s)
: my_var(j, LOCAL_VAR),
m_rcontext_handler(rcontext_handler),
m_type_handler(type_handler), offset(o), sp(s) { }
my_var_sp(const Lex_ident_sys_st &name, const sp_rcontext_addr &addr,
const Type_handler *type_handler, sp_head *s)
: my_var(&name, LOCAL_VAR),
sp_rcontext_addr(addr), m_type_handler(type_handler), sp(s) { }
~my_var_sp() = default;
bool set(THD *thd, Item *val) override;
virtual bool set_row(THD *thd, List<Item> &select_list)
{
DBUG_ASSERT(0);
return set(thd, select_list.head());
}
my_var_sp *get_my_var_sp() override { return this; }
const Type_handler *type_handler() const
{ return m_type_handler; }
sp_rcontext *get_rcontext(sp_rcontext *local_ctx) const;
/*
Check if the value list is compatible with the INTO variable.
This method is called if there is only one variable in the INTO list, e.g.:
SELECT <select list> INTO spvar_varchar; -- scalar variable
*/
virtual bool check_assignability(THD *thd,
const List<Item> &select_list,
bool *assign_as_row) const
{
// The ROW data type has its own my_var_sp. See sql_type_row.cc.
DBUG_ASSERT(type_handler() != &type_handler_row);
/*
If the variable is not scalar (and it's also known not to be ROW),
then it's not compatible with the select list, because Items
in the select list can only be scalar.
*/
*assign_as_row= false;
return select_list.elements != 1 ||
!type_handler()->is_scalar_type() /*e.g. assoc array */;
}
};
/*
This class handles fields of a ROW SP variable when it's used as a OUT
parameter in a stored procedure.
*/
class my_var_sp_row_field: public my_var_sp
{
uint m_field_offset;
public:
my_var_sp_row_field(const Sp_rcontext_handler *rcontext_handler,
const LEX_CSTRING *varname, const LEX_CSTRING *fieldname,
uint var_idx, uint field_idx, sp_head *s)
:my_var_sp(rcontext_handler, varname, var_idx,
&type_handler_double/*Not really used*/, s),
m_field_offset(field_idx)
{ }
bool set(THD *thd, Item *val) override;
};
class my_var_user: public my_var {
public:

View File

@ -6903,11 +6903,9 @@ bool LEX::sp_variable_declarations_set_default(THD *thd, int nvars,
bool
LEX::sp_variable_declarations_copy_type_finalize(THD *thd, int nvars,
LEX::sp_variable_declarations_copy_type_finalize_internal(THD *thd, int nvars,
const Column_definition &ref,
Row_definition_list *fields,
Item *default_value,
const LEX_CSTRING &expr_str)
Row_definition_list *fields)
{
for (uint i= 0 ; i < (uint) nvars; i++)
{
@ -6920,7 +6918,21 @@ LEX::sp_variable_declarations_copy_type_finalize(THD *thd, int nvars,
}
spvar->field_def.field_name= spvar->name;
}
if (unlikely(sp_variable_declarations_set_default(thd, nvars,
return false;
}
bool
LEX::sp_variable_declarations_copy_type_finalize(THD *thd, int nvars,
const Column_definition &ref,
Row_definition_list *fields,
Item *default_value,
const LEX_CSTRING &expr_str)
{
if (unlikely(sp_variable_declarations_copy_type_finalize_internal(thd, nvars,
ref,
fields)) ||
unlikely(sp_variable_declarations_set_default(thd, nvars,
default_value, expr_str)))
return true;
spcont->declare_var_boundary(0);
@ -6934,83 +6946,28 @@ bool LEX::sp_variable_declarations_finalize(THD *thd, int nvars,
const LEX_CSTRING &expr_str)
{
DBUG_ASSERT(cdef);
if (cdef->type_handler() == &type_handler_row)
{
if (sp_record *sprec=
(sp_record *)cdef->get_attr_const_void_ptr(0)) {
return sp_variable_declarations_rec_finalize(thd, nvars,
sprec->field,
dflt_value_item, expr_str);
}
}
Column_definition tmp(*cdef);
if (sphead->fill_spvar_definition(thd, &tmp))
if (unlikely(cdef->type_handler()->sp_variable_declarations_finalize(thd,
this,
nvars,
*cdef)))
return true;
return sp_variable_declarations_copy_type_finalize(thd, nvars, tmp, NULL,
dflt_value_item, expr_str);
if (unlikely(sp_variable_declarations_set_default(thd, nvars,
dflt_value_item, expr_str)))
return true;
spcont->declare_var_boundary(0);
return sphead->restore_lex(thd);
}
bool LEX::sp_variable_declarations_rec_finalize(THD *thd, int nvars,
Row_definition_list *src_row,
Item *dflt_value_item,
const LEX_CSTRING &expr_str)
{
DBUG_ASSERT(src_row);
// Create a copy of the row definition list to fill
// definitions
Row_definition_list *row= new (thd->mem_root) Row_definition_list();
if (unlikely(row == NULL))
return true;
// Create a deep copy of the elements
List_iterator<Spvar_definition> it(*src_row);
for (Spvar_definition *def= it++; def; def= it++)
{
Spvar_definition *new_def= new (thd->mem_root) Spvar_definition(*def);
if (unlikely(new_def == NULL))
return true;
row->push_back(new_def, thd->mem_root);
}
return sp_variable_declarations_row_finalize(thd, nvars, row,
dflt_value_item, expr_str);
}
bool LEX::sp_variable_declarations_row_finalize(THD *thd, int nvars,
Row_definition_list *row,
Item *dflt_value_item,
const LEX_CSTRING &expr_str)
{
DBUG_ASSERT(row);
/*
Prepare all row fields.
Note, we do it only one time outside of the below loop.
The converted list in "row" is further reused by all variable
declarations processed by the current call.
Example:
DECLARE
a, b, c ROW(x VARCHAR(10) CHARACTER SET utf8);
BEGIN
...
END;
*/
if (sphead->row_fill_field_definitions(thd, row))
return true;
for (uint i= 0 ; i < (uint) nvars ; i++)
{
sp_variable *spvar= spcont->get_last_context_variable((uint) nvars - 1 - i);
spvar->field_def.set_row_field_definitions(row);
if (sphead->fill_spvar_definition(thd, &spvar->field_def, &spvar->name))
return true;
}
if (sp_variable_declarations_set_default(thd, nvars, dflt_value_item,
if (Type_handler_row::sp_variable_declarations_row_finalize(thd, this,
nvars,
row) ||
sp_variable_declarations_set_default(thd, nvars, dflt_value_item,
expr_str))
return true;
spcont->declare_var_boundary(0);
@ -8760,38 +8717,39 @@ Item_splocal *LEX::create_item_spvar_row_field(THD *thd,
}
my_var *LEX::create_outvar(THD *thd, const LEX_CSTRING *name)
my_var *LEX::create_outvar(THD *thd,const Lex_ident_sys_st &name)
{
const Sp_rcontext_handler *rh;
sp_variable *spv;
if (likely((spv= find_variable(name, &rh))))
return result ? new (thd->mem_root)
my_var_sp(rh, name, spv->offset,
spv->type_handler(), sphead) :
NULL /* EXPLAIN */;
my_error(ER_SP_UNDECLARED_VAR, MYF(0), name->str);
return NULL;
if (unlikely(!(spv= find_variable(&name, &rh))))
{
my_error(ER_SP_UNDECLARED_VAR, MYF(0), name.str);
return NULL;
}
const sp_rcontext_addr addr(rh, spv->offset);
my_var *var= spv->type_handler()->make_outvar(thd, name, addr,
sphead, !result);
DBUG_ASSERT(var || thd->is_error() || !result);
return var;
}
my_var *LEX::create_outvar(THD *thd,
const LEX_CSTRING *a,
const LEX_CSTRING *b)
const Lex_ident_sys_st &a,
const Lex_ident_sys_st &b)
{
const Sp_rcontext_handler *rh;
sp_variable *t;
if (unlikely(!(t= find_variable(a, &rh))))
if (unlikely(!(t= find_variable(&a, &rh))))
{
my_error(ER_SP_UNDECLARED_VAR, MYF(0), a->str);
my_error(ER_SP_UNDECLARED_VAR, MYF(0), a.str);
return NULL;
}
uint row_field_offset;
if (!t->find_row_field(a, b, &row_field_offset))
return NULL;
return result ?
new (thd->mem_root) my_var_sp_row_field(rh, a, b, t->offset,
row_field_offset, sphead) :
NULL /* EXPLAIN */;
const sp_rcontext_addr addr(rh, t->offset);
my_var *var= t->type_handler()->make_outvar_field(thd, a, addr, b,
sphead, !result);
DBUG_ASSERT(var || thd->is_error() || !result);
return var;
}

View File

@ -3977,6 +3977,11 @@ public:
const LEX_CSTRING &name,
Item *def,
const LEX_CSTRING &expr_str);
protected:
bool sp_variable_declarations_copy_type_finalize_internal(THD *thd, int nvars,
const Column_definition &ref,
Row_definition_list *fields);
public:
bool sp_variable_declarations_copy_type_finalize(THD *thd, int nvars,
const Column_definition &ref,
Row_definition_list *fields,
@ -4269,7 +4274,7 @@ public:
Item *make_item_func_call_native_or_parse_error(THD *thd,
Lex_ident_cli_st &name,
List<Item> *args);
my_var *create_outvar(THD *thd, const LEX_CSTRING *name);
my_var *create_outvar(THD *thd, const Lex_ident_sys_st &name);
/*
Create a my_var instance for a ROW field variable that was used
@ -4279,8 +4284,8 @@ public:
@param field_name - the variable field name
*/
my_var *create_outvar(THD *thd,
const LEX_CSTRING *var_name,
const LEX_CSTRING *field_name);
const Lex_ident_sys_st &var_name,
const Lex_ident_sys_st &field_name);
bool is_trigger_new_or_old_reference(const LEX_CSTRING *name) const;

View File

@ -26,6 +26,8 @@
#include "log.h"
#include "tztime.h"
#include <mysql/plugin_data_type.h>
#include "sp_type_def.h"
#include "sp_head.h"
const DTCollation &DTCollation_numeric::singleton()
@ -2773,6 +2775,27 @@ Field *Type_handler_enum::make_schema_field(MEM_ROOT *root, TABLE *table,
/*************************************************************************/
bool
Type_handler::sp_variable_declarations_finalize(THD *thd,
LEX *lex, int nvars,
const Column_definition &cdef)
const
{
Column_definition tmp(cdef);
if (lex->sphead->fill_spvar_definition(thd, &tmp))
return true;
for (uint i= 0 ; i < (uint) nvars; i++)
{
uint offset= (uint) nvars - 1 - i;
sp_variable *spvar= lex->spcont->get_last_context_variable(offset);
spvar->field_def.set_type(tmp);
spvar->field_def.field_name= spvar->name;
}
return false;
}
bool Type_handler::
Column_definition_validate_check_constraint(THD *thd,
Column_definition * c) const
@ -3647,6 +3670,35 @@ uint Type_handler_blob_common::calc_key_length(const Column_definition &def) con
return 0;
}
/*************************************************************************/
// SELECT 1 INTO spvar;
my_var *Type_handler::make_outvar(THD *thd,
const Lex_ident_sys_st &name,
const sp_rcontext_addr &addr,
sp_head *sphead,
bool validate_only) const
{
if (validate_only) // e.g. EXPLAIN SELECT
return nullptr;
return new (thd->mem_root) my_var_sp(name, addr, this, sphead);
}
// SELECT 1 INTO spvar.field;
my_var *Type_handler::make_outvar_field(THD *thd,
const Lex_ident_sys_st &name,
const sp_rcontext_addr &addr,
const Lex_ident_sys_st &field,
sp_head *sphead,
bool validate_only) const
{
my_printf_error(ER_UNKNOWN_ERROR,
"'%s' is not a row variable", MYF(0), name.str);
return nullptr;
}
/*************************************************************************/
Field *Type_handler::make_and_init_table_field(MEM_ROOT *root,
const LEX_CSTRING *name,
@ -4907,19 +4959,6 @@ bool Type_handler_timestamp_common::
}
bool Type_handler_row::
Item_hybrid_func_fix_attributes(THD *thd,
const LEX_CSTRING &opname,
Type_handler_hybrid_field_type *,
Type_all_attributes *atrr,
Item **items, uint nitems)
const
{
my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0),
name().ptr(), opname.str);
return true;
}
/*************************************************************************/
bool Type_handler::
@ -9874,3 +9913,12 @@ int initialize_data_type_plugin(void *plugin_)
}
return 0;
}
Item *Type_handler::make_typedef_constructor_item(THD *thd,
const sp_type_def &def,
List<Item> *arg_list) const
{
my_error(ER_WRONG_ARGUMENTS, MYF(0), def.get_name().str);
return nullptr;
}

View File

@ -19,6 +19,7 @@
#include "mysqld.h"
#include "lex_string.h"
#include "lex_ident_sys.h"
#include "sql_type_timeofday.h"
#include "sql_array.h"
#include "sql_const.h"
@ -97,6 +98,9 @@ class Conv_source;
class ST_FIELD_INFO;
class Type_collection;
class Create_func;
class sp_type_def;
class sp_head;
class my_var;
#define my_charset_numeric my_charset_latin1
@ -4181,6 +4185,7 @@ public:
virtual bool can_return_extract_source(interval_type type) const;
virtual bool is_bool_type() const { return false; }
virtual bool is_general_purpose_string_type() const { return false; }
virtual bool has_null_predicate() const { return true; }
virtual decimal_digits_t Item_time_precision(THD *thd, Item *item) const;
virtual decimal_digits_t Item_datetime_precision(THD *thd, Item *item) const;
virtual decimal_digits_t Item_decimal_scale(const Item *item) const;
@ -4322,6 +4327,10 @@ public:
virtual bool Column_definition_prepare_stage2(Column_definition *c,
handler *file,
ulonglong table_flags) const= 0;
virtual bool sp_variable_declarations_finalize(THD *thd,
LEX *lex, int nvars,
const Column_definition &def)
const;
virtual bool Key_part_spec_init_primary(Key_part_spec *part,
const Column_definition &def,
const handler *file) const;
@ -4373,6 +4382,33 @@ public:
const Bit_addr &bit,
const Column_definition_attributes *attr,
uint32 flags) const= 0;
/*
Make a my_var to handle:
SELECT 1 INTO spvar;
@param thd - Current thd
@param name - The variable name
@param addr - The variable run-time address
@param sphead - The sphead containing the variable
@param validate_only - Do not make my_var, only raise an SQL error
if the variable is not used correctly.
This is needed for EXPLAIN SELECT statements.
@returns - A pointer to a new my_var instance.
nullptr if "validate_only" was passed.
nullptr if the variable is not used correcly
(an SQL error is raised in this case).
*/
virtual my_var *make_outvar(THD *thd,
const Lex_ident_sys_st &name,
const sp_rcontext_addr &addr,
sp_head *sphead,
bool validate_only) const;
// SELECT 1 INTO spvar.field;
virtual my_var *make_outvar_field(THD *thd,
const Lex_ident_sys_st &name,
const sp_rcontext_addr &addr,
const Lex_ident_sys_st &field,
sp_head *sphead,
bool validate_only) const;
virtual void
Column_definition_attributes_frm_pack(const Column_definition_attributes *at,
uchar *buff) const;
@ -4543,6 +4579,9 @@ public:
{
return NULL;
}
virtual Item *make_typedef_constructor_item(THD *thd,
const sp_type_def &def,
List<Item> *arg_list) const;
/**
normalize_cond() replaces
@ -4742,389 +4781,7 @@ public:
};
/*
Special handler for ROW
*/
class Type_handler_row: public Type_handler
{
public:
virtual ~Type_handler_row() = default;
const Name &default_value() const override;
bool validate_implicit_default_value(THD *, const Column_definition &)
const override
{
MY_ASSERT_UNREACHABLE();
return true;
}
const Type_collection *type_collection() const override;
bool is_scalar_type() const override { return false; }
bool can_return_int() const override { return false; }
bool can_return_decimal() const override { return false; }
bool can_return_real() const override { return false; }
bool can_return_str() const override { return false; }
bool can_return_text() const override { return false; }
bool can_return_date() const override { return false; }
bool can_return_time() const override { return false; }
enum_field_types field_type() const override
{
MY_ASSERT_UNREACHABLE();
return MYSQL_TYPE_NULL;
};
protocol_send_type_t protocol_send_type() const override
{
MY_ASSERT_UNREACHABLE();
return PROTOCOL_SEND_STRING;
}
Item_result result_type() const override
{
return ROW_RESULT;
}
Item_result cmp_type() const override
{
return ROW_RESULT;
}
enum_dynamic_column_type dyncol_type(const Type_all_attributes *)
const override
{
MY_ASSERT_UNREACHABLE();
return DYN_COL_NULL;
}
const Type_handler *type_handler_for_comparison() const override;
int stored_field_cmp_to_item(THD *, Field *, Item *) const override
{
MY_ASSERT_UNREACHABLE();
return 0;
}
bool subquery_type_allows_materialization(const Item *, const Item *, bool)
const override
{
MY_ASSERT_UNREACHABLE();
return false;
}
Field *make_num_distinct_aggregator_field(MEM_ROOT *, const Item *) const
override
{
MY_ASSERT_UNREACHABLE();
return nullptr;
}
Field *make_conversion_table_field(MEM_ROOT *, TABLE *, uint, const Field *)
const override
{
MY_ASSERT_UNREACHABLE();
return nullptr;
}
bool Column_definition_fix_attributes(Column_definition *) const override
{
return false;
}
void Column_definition_reuse_fix_attributes(THD *, Column_definition *,
const Field *) const override
{
MY_ASSERT_UNREACHABLE();
}
bool Column_definition_prepare_stage1(THD *thd,
MEM_ROOT *mem_root,
Column_definition *c,
column_definition_type_t type,
const Column_derived_attributes
*derived_attr)
const override;
bool Column_definition_redefine_stage1(Column_definition *,
const Column_definition *,
const handler *)
const override
{
MY_ASSERT_UNREACHABLE();
return true;
}
bool Column_definition_prepare_stage2(Column_definition *, handler *,
ulonglong) const override
{
return false;
}
bool Spvar_definition_with_complex_data_types(Spvar_definition *def)
const override;
Field *make_table_field(MEM_ROOT *, const LEX_CSTRING *, const Record_addr &,
const Type_all_attributes &, TABLE_SHARE *)
const override
{
MY_ASSERT_UNREACHABLE();
return nullptr;
}
Field *make_table_field_from_def(TABLE_SHARE *share,
MEM_ROOT *mem_root,
const LEX_CSTRING *name,
const Record_addr &addr,
const Bit_addr &bit,
const Column_definition_attributes *attr,
uint32 flags) const override;
void make_sort_key_part(uchar *to, Item *item,
const SORT_FIELD_ATTR *sort_field,
String *tmp) const override
{
MY_ASSERT_UNREACHABLE();
}
uint make_packed_sort_key_part(uchar *, Item *, const SORT_FIELD_ATTR *,
String *) const override
{
MY_ASSERT_UNREACHABLE();
return 0;
}
void sort_length(THD *, const Type_std_attributes *, SORT_FIELD_ATTR *)
const override
{
MY_ASSERT_UNREACHABLE();
}
uint32 max_display_length(const Item *) const override
{
MY_ASSERT_UNREACHABLE();
return 0;
}
uint32 max_display_length_for_field(const Conv_source &) const override
{
MY_ASSERT_UNREACHABLE();
return 0;
}
uint32 calc_pack_length(uint32) const override
{
MY_ASSERT_UNREACHABLE();
return 0;
}
bool Item_eq_value(THD *thd, const Type_cmp_attributes *attr,
Item *a, Item *b) const override;
decimal_digits_t Item_decimal_precision(const Item *) const override
{
MY_ASSERT_UNREACHABLE();
return DECIMAL_MAX_PRECISION;
}
bool Item_save_in_value(THD *thd, Item *item, st_value *value) const
override;
bool Item_param_set_from_value(THD *thd,
Item_param *param,
const Type_all_attributes *attr,
const st_value *value) const override;
bool Item_send(Item *, Protocol *, st_value *) const override
{
MY_ASSERT_UNREACHABLE();
return true;
}
void Item_update_null_value(Item *item) const override;
int Item_save_in_field(Item *, Field *, bool) const override
{
MY_ASSERT_UNREACHABLE();
return 1;
}
String *print_item_value(THD *thd, Item *item, String *str) const override;
bool can_change_cond_ref_to_const(Item_bool_func2 *, Item *, Item *,
Item_bool_func2 *, Item *, Item *)
const override
{
MY_ASSERT_UNREACHABLE();
return false;
}
Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const
override;
Item_cache *Item_get_cache(THD *thd, const Item *item) const override;
Item_copy *create_item_copy(THD *, Item *) const override
{
MY_ASSERT_UNREACHABLE();
return nullptr;
}
bool set_comparator_func(THD *thd, Arg_comparator *cmp) const override;
bool Item_hybrid_func_fix_attributes(THD *thd,
const LEX_CSTRING &name,
Type_handler_hybrid_field_type *,
Type_all_attributes *atrr,
Item **items, uint nitems)
const override;
bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *) const override
{
MY_ASSERT_UNREACHABLE();
return true;
}
bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *) const override
{
MY_ASSERT_UNREACHABLE();
return true;
}
bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const override
{
MY_ASSERT_UNREACHABLE();
return true;
}
bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *) const override
{
MY_ASSERT_UNREACHABLE();
return true;
}
bool Item_val_bool(Item *item) const override
{
MY_ASSERT_UNREACHABLE();
return false;
}
void Item_get_date(THD *, Item *, Temporal::Warn *, MYSQL_TIME *ltime,
date_mode_t) const override
{
MY_ASSERT_UNREACHABLE();
set_zero_time(ltime, MYSQL_TIMESTAMP_NONE);
}
longlong Item_val_int_signed_typecast(Item *) const override
{
MY_ASSERT_UNREACHABLE();
return 0;
}
longlong Item_val_int_unsigned_typecast(Item *) const override
{
MY_ASSERT_UNREACHABLE();
return 0;
}
String *Item_func_hex_val_str_ascii(Item_func_hex *, String *) const override
{
MY_ASSERT_UNREACHABLE();
return nullptr;
}
String *Item_func_hybrid_field_type_val_str(Item_func_hybrid_field_type *,
String *) const override
{
MY_ASSERT_UNREACHABLE();
return nullptr;
}
double Item_func_hybrid_field_type_val_real(Item_func_hybrid_field_type *)
const override
{
MY_ASSERT_UNREACHABLE();
return 0.0;
}
longlong Item_func_hybrid_field_type_val_int(Item_func_hybrid_field_type *)
const override
{
MY_ASSERT_UNREACHABLE();
return 0;
}
my_decimal *Item_func_hybrid_field_type_val_decimal(
Item_func_hybrid_field_type *,
my_decimal *) const override
{
MY_ASSERT_UNREACHABLE();
return nullptr;
}
void Item_func_hybrid_field_type_get_date(THD *,
Item_func_hybrid_field_type *,
Temporal::Warn *,
MYSQL_TIME *ltime,
date_mode_t) const override
{
MY_ASSERT_UNREACHABLE();
set_zero_time(ltime, MYSQL_TIMESTAMP_NONE);
}
String *Item_func_min_max_val_str(Item_func_min_max *, String *) const
override
{
MY_ASSERT_UNREACHABLE();
return nullptr;
}
double Item_func_min_max_val_real(Item_func_min_max *) const override
{
MY_ASSERT_UNREACHABLE();
return 0;
}
longlong Item_func_min_max_val_int(Item_func_min_max *) const override
{
MY_ASSERT_UNREACHABLE();
return 0;
}
my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *,
my_decimal *) const override
{
MY_ASSERT_UNREACHABLE();
return nullptr;
}
bool Item_func_min_max_get_date(THD *, Item_func_min_max*, MYSQL_TIME *,
date_mode_t) const override
{
MY_ASSERT_UNREACHABLE();
return true;
}
bool Item_func_between_fix_length_and_dec(Item_func_between *) const override
{
MY_ASSERT_UNREACHABLE();
return true;
}
longlong Item_func_between_val_int(Item_func_between *func) const override;
cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const override;
in_vector *make_in_vector(THD *thd, const Item_func_in *f, uint nargs) const
override;
bool Item_func_in_fix_comparator_compatible_types(THD *thd,
Item_func_in *) const
override;
bool Item_func_round_fix_length_and_dec(Item_func_round *) const override;
bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const
override;
bool Item_func_abs_fix_length_and_dec(Item_func_abs *) const override;
bool Item_func_neg_fix_length_and_dec(Item_func_neg *) const override;
bool Item_func_signed_fix_length_and_dec(Item_func_signed *) const override
{
MY_ASSERT_UNREACHABLE();
return true;
}
bool Item_func_unsigned_fix_length_and_dec(Item_func_unsigned *) const
override
{
MY_ASSERT_UNREACHABLE();
return true;
}
bool Item_double_typecast_fix_length_and_dec(Item_double_typecast *) const
override
{
MY_ASSERT_UNREACHABLE();
return true;
}
bool Item_float_typecast_fix_length_and_dec(Item_float_typecast *) const
override
{
MY_ASSERT_UNREACHABLE();
return true;
}
bool Item_decimal_typecast_fix_length_and_dec(Item_decimal_typecast *) const
override
{
MY_ASSERT_UNREACHABLE();
return true;
}
bool Item_char_typecast_fix_length_and_dec(Item_char_typecast *) const
override
{
MY_ASSERT_UNREACHABLE();
return true;
}
bool Item_time_typecast_fix_length_and_dec(Item_time_typecast *) const
override
{
MY_ASSERT_UNREACHABLE();
return true;
}
bool Item_date_typecast_fix_length_and_dec(Item_date_typecast *) const
override
{
MY_ASSERT_UNREACHABLE();
return true;
}
bool Item_datetime_typecast_fix_length_and_dec(Item_datetime_typecast *)
const override
{
MY_ASSERT_UNREACHABLE();
return true;
}
bool Item_func_plus_fix_length_and_dec(Item_func_plus *) const override;
bool Item_func_minus_fix_length_and_dec(Item_func_minus *) const override;
bool Item_func_mul_fix_length_and_dec(Item_func_mul *) const override;
bool Item_func_div_fix_length_and_dec(Item_func_div *) const override;
bool Item_func_mod_fix_length_and_dec(Item_func_mod *) const override;
};
#include "sql_type_row.h"
/*

223
sql/sql_type_row.cc Normal file
View File

@ -0,0 +1,223 @@
/*
Copyright (c) 2025, Rakuten Securities
Copyright (c) 2015, 2025, MariaDB plc
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; version 2 of
the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA
*/
#include "sql_type.h"
#include "sql_type_row.h"
#include "item.h"
#include "sql_select.h"
#include "field.h"
#include "sp_rcontext.h"
#include "sp_type_def.h"
#include "sp_head.h"
bool Type_handler_row::
sp_variable_declarations_row_finalize(THD *thd, LEX *lex, int nvars,
Row_definition_list *row)
{
DBUG_ASSERT(row);
/*
Prepare all row fields.
Note, we do it only one time outside of the below loop.
The converted list in "row" is further reused by all variable
declarations processed by the current call.
Example:
DECLARE
a, b, c ROW(x VARCHAR(10) CHARACTER SET utf8);
BEGIN
...
END;
*/
if (lex->sphead->row_fill_field_definitions(thd, row))
return true;
for (uint i= 0 ; i < (uint) nvars ; i++)
{
uint offset= (uint) nvars - 1 - i;
sp_variable *spvar= lex->spcont->get_last_context_variable(offset);
spvar->field_def.set_row_field_definitions(row);
if (lex->sphead->fill_spvar_definition(thd, &spvar->field_def,
&spvar->name))
return true;
}
return false;
}
class my_var_sp_row: public my_var_sp
{
public:
my_var_sp_row(const Lex_ident_sys_st &name, const sp_rcontext_addr &addr,
sp_head *s)
:my_var_sp(name, addr, &type_handler_row, s)
{ }
bool check_assignability(THD *thd, const List<Item> &select_list,
bool *assign_as_row) const override
{
Item_field *item= get_rcontext(thd->spcont)->get_variable(offset());
const Field_row *field= dynamic_cast<const Field_row*>(item->field);
DBUG_ASSERT(field);
*assign_as_row= true;
return !field ||
select_list.elements != field->virtual_tmp_table()->s->fields;
}
bool set_row(THD *thd, List<Item> &select_list) override
{
return get_rcontext(thd->spcont)->set_variable_row(thd, offset(),
select_list);
}
};
/*
This class handles fields of a ROW SP variable when it's used as a OUT
parameter in a stored procedure.
*/
class my_var_sp_row_field: public my_var_sp
{
uint m_field_offset;
public:
my_var_sp_row_field(const Lex_ident_sys_st &varname,
const sp_rcontext_addr &varaddr,
uint field_idx, sp_head *s)
:my_var_sp(varname, varaddr,
&type_handler_double/*Not really used*/, s),
m_field_offset(field_idx)
{ }
bool check_assignability(THD *thd, const List<Item> &select_list,
bool *assign_as_row) const override
{
*assign_as_row= false;
return select_list.elements == 1;
}
bool set(THD *thd, Item *item) override
{
return get_rcontext(thd->spcont)->
set_variable_row_field(thd, offset(), m_field_offset, &item);
}
};
my_var *Type_handler_row::make_outvar(THD *thd,
const Lex_ident_sys_st &name,
const sp_rcontext_addr &addr,
sp_head *sphead,
bool validate_only) const
{
if (validate_only) // e.g. EXPLAIN SELECT .. INTO spvar_row;
return nullptr;
return new (thd->mem_root) my_var_sp_row(name, addr, sphead);
}
my_var *Type_handler_row::make_outvar_field(THD *thd,
const Lex_ident_sys_st &name,
const sp_rcontext_addr &addr,
const Lex_ident_sys_st &field,
sp_head *sphead,
bool validate_only) const
{
const Sp_rcontext_handler *rh;
sp_variable *t= thd->lex->find_variable(&name, &rh);
DBUG_ASSERT(t);
DBUG_ASSERT(t->type_handler() == this);
uint row_field_offset;
if (!t->find_row_field(&name, &field, &row_field_offset))
{
DBUG_ASSERT(0);
my_error(ER_ROW_VARIABLE_DOES_NOT_HAVE_FIELD, MYF(0), name.str, field.str);
return NULL;
}
if (validate_only) // e.g. EXPLAIN SELECT .. INTO spvar_row.field;
return nullptr;
return new (thd->mem_root) my_var_sp_row_field(name, addr, row_field_offset,
sphead);
}
Item_field *Field_row::make_item_field_spvar(THD *thd,
const Spvar_definition &def)
{
Item_field_row *item= new (thd->mem_root) Item_field_row(thd, this);
if (!item)
return nullptr;
if (row_create_fields(thd, def))
return nullptr;
// virtual_tmp_table() returns nullptr in case of ROW TYPE OF cursor
if (virtual_tmp_table() &&
item->add_array_of_item_field(thd, *virtual_tmp_table()))
return nullptr;
return item;
}
bool
Type_handler_row::
sp_variable_declarations_finalize(THD *thd, LEX *lex, int nvars,
const Column_definition &cdef) const
{
const sp_type_def_record *rec= static_cast<const sp_type_def_record*>
(cdef.get_attr_const_void_ptr(0));
DBUG_ASSERT(!rec || rec->field);
if (!rec || !rec->field)
{
// A variable with an explicit ROW data type
return Type_handler::sp_variable_declarations_finalize(thd, lex,
nvars, cdef);
}
// TYPE row_t IS RECORD
Row_definition_list *row= rec->field->deep_copy(thd);
return row == nullptr ||
Type_handler_row::sp_variable_declarations_row_finalize(thd,
lex,
nvars,
row);
}
Item *Type_handler_row::make_typedef_constructor_item(THD *thd,
const sp_type_def &def,
List<Item> *args) const
{
if (unlikely(args == nullptr))
{
my_error(ER_WRONG_ARGUMENTS, MYF(0), def.get_name().str);
return nullptr;
}
return new (thd->mem_root) Item_row(thd, *args);
}
bool Type_handler_row::
Item_hybrid_func_fix_attributes(THD *thd,
const LEX_CSTRING &opname,
Type_handler_hybrid_field_type *,
Type_all_attributes *atrr,
Item **items, uint nitems)
const
{
my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0),
name().ptr(), opname.str);
return true;
}

434
sql/sql_type_row.h Normal file
View File

@ -0,0 +1,434 @@
/*
Copyright (c) 2025, Rakuten Securities
Copyright (c) 2025, MariaDB plc
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; version 2 of
the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA
*/
#ifndef SQL_TYPE_ROW_INCLUDED
#define SQL_TYPE_ROW_INCLUDED
class Row_definition_list;
/*
Special handler for ROW
*/
class Type_handler_row: public Type_handler
{
public:
static bool sp_variable_declarations_row_finalize(THD *thd, LEX *lex,
int nvars,
Row_definition_list *row);
public:
virtual ~Type_handler_row() = default;
const Name &default_value() const override;
bool validate_implicit_default_value(THD *, const Column_definition &)
const override
{
MY_ASSERT_UNREACHABLE();
return true;
}
const Type_collection *type_collection() const override;
bool is_scalar_type() const override { return false; }
bool can_return_int() const override { return false; }
bool can_return_decimal() const override { return false; }
bool can_return_real() const override { return false; }
bool can_return_str() const override { return false; }
bool can_return_text() const override { return false; }
bool can_return_date() const override { return false; }
bool can_return_time() const override { return false; }
enum_field_types field_type() const override
{
MY_ASSERT_UNREACHABLE();
return MYSQL_TYPE_NULL;
};
protocol_send_type_t protocol_send_type() const override
{
MY_ASSERT_UNREACHABLE();
return PROTOCOL_SEND_STRING;
}
Item_result result_type() const override
{
return ROW_RESULT;
}
Item_result cmp_type() const override
{
return ROW_RESULT;
}
enum_dynamic_column_type dyncol_type(const Type_all_attributes *)
const override
{
MY_ASSERT_UNREACHABLE();
return DYN_COL_NULL;
}
const Type_handler *type_handler_for_comparison() const override;
bool has_null_predicate() const override { return false; }
int stored_field_cmp_to_item(THD *, Field *, Item *) const override
{
MY_ASSERT_UNREACHABLE();
return 0;
}
bool subquery_type_allows_materialization(const Item *, const Item *, bool)
const override
{
MY_ASSERT_UNREACHABLE();
return false;
}
Field *make_num_distinct_aggregator_field(MEM_ROOT *, const Item *) const
override
{
MY_ASSERT_UNREACHABLE();
return nullptr;
}
Field *make_conversion_table_field(MEM_ROOT *, TABLE *, uint, const Field *)
const override
{
MY_ASSERT_UNREACHABLE();
return nullptr;
}
bool Column_definition_fix_attributes(Column_definition *) const override
{
return false;
}
void Column_definition_reuse_fix_attributes(THD *, Column_definition *,
const Field *) const override
{
MY_ASSERT_UNREACHABLE();
}
bool Column_definition_prepare_stage1(THD *thd,
MEM_ROOT *mem_root,
Column_definition *c,
column_definition_type_t type,
const Column_derived_attributes
*derived_attr)
const override;
bool Column_definition_redefine_stage1(Column_definition *,
const Column_definition *,
const handler *)
const override
{
MY_ASSERT_UNREACHABLE();
return true;
}
bool Column_definition_prepare_stage2(Column_definition *, handler *,
ulonglong) const override
{
return false;
}
bool Spvar_definition_with_complex_data_types(Spvar_definition *def)
const override;
bool sp_variable_declarations_finalize(THD *thd,
LEX *lex, int nvars,
const Column_definition &def)
const override;
Field *make_table_field(MEM_ROOT *, const LEX_CSTRING *, const Record_addr &,
const Type_all_attributes &, TABLE_SHARE *)
const override
{
MY_ASSERT_UNREACHABLE();
return nullptr;
}
Field *make_table_field_from_def(TABLE_SHARE *share,
MEM_ROOT *mem_root,
const LEX_CSTRING *name,
const Record_addr &addr,
const Bit_addr &bit,
const Column_definition_attributes *attr,
uint32 flags) const override;
void make_sort_key_part(uchar *to, Item *item,
const SORT_FIELD_ATTR *sort_field,
String *tmp) const override
{
MY_ASSERT_UNREACHABLE();
}
uint make_packed_sort_key_part(uchar *, Item *, const SORT_FIELD_ATTR *,
String *) const override
{
MY_ASSERT_UNREACHABLE();
return 0;
}
void sort_length(THD *, const Type_std_attributes *, SORT_FIELD_ATTR *)
const override
{
MY_ASSERT_UNREACHABLE();
}
uint32 max_display_length(const Item *) const override
{
MY_ASSERT_UNREACHABLE();
return 0;
}
uint32 max_display_length_for_field(const Conv_source &) const override
{
MY_ASSERT_UNREACHABLE();
return 0;
}
uint32 calc_pack_length(uint32) const override
{
MY_ASSERT_UNREACHABLE();
return 0;
}
bool Item_eq_value(THD *thd, const Type_cmp_attributes *attr,
Item *a, Item *b) const override;
decimal_digits_t Item_decimal_precision(const Item *) const override
{
MY_ASSERT_UNREACHABLE();
return DECIMAL_MAX_PRECISION;
}
bool Item_save_in_value(THD *thd, Item *item, st_value *value) const
override;
bool Item_param_set_from_value(THD *thd,
Item_param *param,
const Type_all_attributes *attr,
const st_value *value) const override;
bool Item_send(Item *, Protocol *, st_value *) const override
{
MY_ASSERT_UNREACHABLE();
return true;
}
void Item_update_null_value(Item *item) const override;
int Item_save_in_field(Item *, Field *, bool) const override
{
MY_ASSERT_UNREACHABLE();
return 1;
}
// SELECT 1,2,3 INTO spvar_row;
my_var *make_outvar(THD *thd,
const Lex_ident_sys_st &name,
const sp_rcontext_addr &addr,
sp_head *sphead,
bool validate_only) const override;
// SELECT 1 INTO spvar_row.field;
virtual my_var *make_outvar_field(THD *thd,
const Lex_ident_sys_st &name,
const sp_rcontext_addr &addr,
const Lex_ident_sys_st &field,
sp_head *sphead,
bool validate_only) const override;
String *print_item_value(THD *thd, Item *item, String *str) const override;
bool can_change_cond_ref_to_const(Item_bool_func2 *, Item *, Item *,
Item_bool_func2 *, Item *, Item *)
const override
{
MY_ASSERT_UNREACHABLE();
return false;
}
Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const
override;
Item *make_typedef_constructor_item(THD *thd, const sp_type_def &def,
List<Item> *arg_list) const override;
Item_cache *Item_get_cache(THD *thd, const Item *item) const override;
Item_copy *create_item_copy(THD *, Item *) const override
{
MY_ASSERT_UNREACHABLE();
return nullptr;
}
bool set_comparator_func(THD *thd, Arg_comparator *cmp) const override;
bool Item_hybrid_func_fix_attributes(THD *thd,
const LEX_CSTRING &name,
Type_handler_hybrid_field_type *,
Type_all_attributes *atrr,
Item **items, uint nitems)
const override;
bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *) const override
{
MY_ASSERT_UNREACHABLE();
return true;
}
bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *) const override
{
MY_ASSERT_UNREACHABLE();
return true;
}
bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const override
{
MY_ASSERT_UNREACHABLE();
return true;
}
bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *) const override
{
MY_ASSERT_UNREACHABLE();
return true;
}
bool Item_val_bool(Item *item) const override
{
MY_ASSERT_UNREACHABLE();
return false;
}
void Item_get_date(THD *, Item *, Temporal::Warn *, MYSQL_TIME *ltime,
date_mode_t) const override
{
MY_ASSERT_UNREACHABLE();
set_zero_time(ltime, MYSQL_TIMESTAMP_NONE);
}
longlong Item_val_int_signed_typecast(Item *) const override
{
MY_ASSERT_UNREACHABLE();
return 0;
}
longlong Item_val_int_unsigned_typecast(Item *) const override
{
MY_ASSERT_UNREACHABLE();
return 0;
}
String *Item_func_hex_val_str_ascii(Item_func_hex *, String *) const override
{
MY_ASSERT_UNREACHABLE();
return nullptr;
}
String *Item_func_hybrid_field_type_val_str(Item_func_hybrid_field_type *,
String *) const override
{
MY_ASSERT_UNREACHABLE();
return nullptr;
}
double Item_func_hybrid_field_type_val_real(Item_func_hybrid_field_type *)
const override
{
MY_ASSERT_UNREACHABLE();
return 0.0;
}
longlong Item_func_hybrid_field_type_val_int(Item_func_hybrid_field_type *)
const override
{
MY_ASSERT_UNREACHABLE();
return 0;
}
my_decimal *Item_func_hybrid_field_type_val_decimal(
Item_func_hybrid_field_type *,
my_decimal *) const override
{
MY_ASSERT_UNREACHABLE();
return nullptr;
}
void Item_func_hybrid_field_type_get_date(THD *,
Item_func_hybrid_field_type *,
Temporal::Warn *,
MYSQL_TIME *ltime,
date_mode_t) const override
{
MY_ASSERT_UNREACHABLE();
set_zero_time(ltime, MYSQL_TIMESTAMP_NONE);
}
String *Item_func_min_max_val_str(Item_func_min_max *, String *) const
override
{
MY_ASSERT_UNREACHABLE();
return nullptr;
}
double Item_func_min_max_val_real(Item_func_min_max *) const override
{
MY_ASSERT_UNREACHABLE();
return 0;
}
longlong Item_func_min_max_val_int(Item_func_min_max *) const override
{
MY_ASSERT_UNREACHABLE();
return 0;
}
my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *,
my_decimal *) const override
{
MY_ASSERT_UNREACHABLE();
return nullptr;
}
bool Item_func_min_max_get_date(THD *, Item_func_min_max*, MYSQL_TIME *,
date_mode_t) const override
{
MY_ASSERT_UNREACHABLE();
return true;
}
bool Item_func_between_fix_length_and_dec(Item_func_between *) const override
{
MY_ASSERT_UNREACHABLE();
return true;
}
longlong Item_func_between_val_int(Item_func_between *func) const override;
cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const override;
in_vector *make_in_vector(THD *thd, const Item_func_in *f, uint nargs) const
override;
bool Item_func_in_fix_comparator_compatible_types(THD *thd,
Item_func_in *) const
override;
bool Item_func_round_fix_length_and_dec(Item_func_round *) const override;
bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const
override;
bool Item_func_abs_fix_length_and_dec(Item_func_abs *) const override;
bool Item_func_neg_fix_length_and_dec(Item_func_neg *) const override;
bool Item_func_signed_fix_length_and_dec(Item_func_signed *) const override
{
MY_ASSERT_UNREACHABLE();
return true;
}
bool Item_func_unsigned_fix_length_and_dec(Item_func_unsigned *) const
override
{
MY_ASSERT_UNREACHABLE();
return true;
}
bool Item_double_typecast_fix_length_and_dec(Item_double_typecast *) const
override
{
MY_ASSERT_UNREACHABLE();
return true;
}
bool Item_float_typecast_fix_length_and_dec(Item_float_typecast *) const
override
{
MY_ASSERT_UNREACHABLE();
return true;
}
bool Item_decimal_typecast_fix_length_and_dec(Item_decimal_typecast *) const
override
{
MY_ASSERT_UNREACHABLE();
return true;
}
bool Item_char_typecast_fix_length_and_dec(Item_char_typecast *) const
override
{
MY_ASSERT_UNREACHABLE();
return true;
}
bool Item_time_typecast_fix_length_and_dec(Item_time_typecast *) const
override
{
MY_ASSERT_UNREACHABLE();
return true;
}
bool Item_date_typecast_fix_length_and_dec(Item_date_typecast *) const
override
{
MY_ASSERT_UNREACHABLE();
return true;
}
bool Item_datetime_typecast_fix_length_and_dec(Item_datetime_typecast *)
const override
{
MY_ASSERT_UNREACHABLE();
return true;
}
bool Item_func_plus_fix_length_and_dec(Item_func_plus *) const override;
bool Item_func_minus_fix_length_and_dec(Item_func_minus *) const override;
bool Item_func_mul_fix_length_and_dec(Item_func_mul *) const override;
bool Item_func_div_fix_length_and_dec(Item_func_div *) const override;
bool Item_func_mod_fix_length_and_dec(Item_func_mod *) const override;
};
#endif /* SQL_TYPE_ROW_INCLUDED */

View File

@ -290,7 +290,7 @@ void _CONCAT_UNDERSCORED(turn_parser_debug_on,yyparse)()
class sp_head *sphead;
class sp_name *spname;
class sp_variable *spvar;
class sp_record *sprec;
class sp_type_def_record *sprec;
class With_element_head *with_element_head;
class With_clause *with_clause;
class Virtual_column_info *virtual_column;
@ -1340,7 +1340,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%type <ident_sys>
IDENT_sys
ident_func
ident
label_ident
sp_decl_ident
@ -6483,9 +6482,9 @@ field_type_all_with_record:
}
| udt_name float_options srid_option
{
sp_record *sprec = NULL;
sp_type_def *sprec = NULL;
if (Lex->spcont)
sprec = Lex->spcont->find_record(&$1, false);
sprec = Lex->spcont->find_type_def($1, false);
if (sprec == NULL)
{
@ -10933,13 +10932,15 @@ function_call_conflict:
in sql/item_create.cc
*/
function_call_generic:
ident_func '('
ident_cli_func '('
{
#ifdef HAVE_DLOPEN
udf_func *udf= 0;
LEX *lex= Lex;
Lex_ident_sys ident(thd, &$1);
if (using_udf_functions &&
(udf= find_udf($1.str, $1.length)) &&
(udf= find_udf(ident.str, ident.length)) &&
udf->type == UDFTYPE_AGGREGATE)
{
if (unlikely(lex->current_select->inc_in_sum_expr()))
@ -10957,9 +10958,11 @@ function_call_generic:
const Type_handler *h;
Create_func *builder;
Item *item= NULL;
sp_record* rec= NULL;
const sp_type_def *tdef= NULL;
const Lex_ident_sys ident(thd, &$1);
if (unlikely(Lex_ident_routine::check_name_with_error($1)))
if (unlikely(ident.is_null() ||
Lex_ident_routine::check_name_with_error(ident)))
MYSQL_YYABORT;
/*
@ -10973,20 +10976,20 @@ function_call_generic:
This will be revised with WL#2128 (SQL PATH)
*/
builder= Schema::find_implied(thd)->
find_native_function_builder(thd, $1);
find_native_function_builder(thd, ident);
if (builder)
{
item= builder->create_func(thd, &$1, $4);
item= builder->create_func(thd, &ident, $4);
}
else if ((h= Type_handler::handler_by_name(thd, $1)) &&
else if ((h= Type_handler::handler_by_name(thd, ident)) &&
(item= h->make_constructor_item(thd, $4)))
{
// Found a constructor with a proper argument count
}
else if (Lex->spcont &&
(rec = Lex->spcont->find_record(&$1, false)))
(tdef= Lex->spcont->find_type_def(ident, false)))
{
item= new (thd->mem_root) Item_row(thd, *$4);
item= tdef->make_constructor_item(thd, $4);
}
else
{
@ -11008,7 +11011,7 @@ function_call_generic:
{
builder= find_qualified_function_builder(thd);
DBUG_ASSERT(builder);
item= builder->create_func(thd, &$1, $4);
item= builder->create_func(thd, &ident, $4);
}
}
@ -13346,12 +13349,13 @@ select_outvar:
}
| ident_or_text
{
if (unlikely(!($$= Lex->create_outvar(thd, &$1)) && Lex->result))
const Lex_ident_sys name(thd, &$1);
if (unlikely(!($$= Lex->create_outvar(thd, name)) && Lex->result))
MYSQL_YYABORT;
}
| ident '.' ident
{
if (unlikely(!($$= Lex->create_outvar(thd, &$1, &$3)) && Lex->result))
if (unlikely(!($$= Lex->create_outvar(thd, $1, $3)) && Lex->result))
MYSQL_YYABORT;
}
;
@ -16027,15 +16031,6 @@ ident_cli_func:
| keyword_func_sp_var_not_label { $$= $1; }
;
ident_func:
ident_cli_func
{
if (unlikely(thd->to_ident_sys_alloc(&$$, &$1)))
MYSQL_YYABORT;
}
;
TEXT_STRING_sys:
TEXT_STRING
{
@ -20200,7 +20195,7 @@ sp_decl_non_handler:
| TYPE_SYM ident_directly_assignable IS RECORD_SYM rec_type_body
{
if (unlikely(Lex->spcont->
declare_record(thd, Lex_ident_column($2), $5)))
type_defs_declare_record(thd, Lex_ident_column($2), $5)))
MYSQL_YYABORT;
$$.vars= $$.conds= $$.hndlrs= $$.curs= 0;