From 9e13cf0862d2fffd905ac5296b92365de3fe09c7 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Tue, 29 Apr 2025 14:51:57 +0400 Subject: [PATCH] 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 &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 &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 *arg_list) const; - A new helper method: Row_definition_list *Row_definition_list::deep_copy(THD *thd) const; --- libmysqld/CMakeLists.txt | 1 + sql/CMakeLists.txt | 1 + sql/field.cc | 6 + sql/field.h | 6 +- sql/item_cmpfunc.cc | 11 + sql/item_cmpfunc.h | 1 + sql/sp_pcontext.cc | 40 ++-- sql/sp_pcontext.h | 27 +-- sql/sp_rcontext.cc | 47 ++--- sql/sp_type_def.h | 98 +++++++++ sql/sql_class.cc | 29 ++- sql/sql_class.h | 58 +++--- sql/sql_lex.cc | 142 +++++-------- sql/sql_lex.h | 11 +- sql/sql_type.cc | 74 +++++-- sql/sql_type.h | 423 ++++---------------------------------- sql/sql_type_row.cc | 223 ++++++++++++++++++++ sql/sql_type_row.h | 434 +++++++++++++++++++++++++++++++++++++++ sql/sql_yacc.yy | 47 ++--- 19 files changed, 1058 insertions(+), 621 deletions(-) create mode 100644 sql/sp_type_def.h create mode 100644 sql/sql_type_row.cc create mode 100644 sql/sql_type_row.h diff --git a/libmysqld/CMakeLists.txt b/libmysqld/CMakeLists.txt index be6dd1af678..14b06dd5559 100644 --- a/libmysqld/CMakeLists.txt +++ b/libmysqld/CMakeLists.txt @@ -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 diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 685fcdad220..86906fcfe75 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -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} diff --git a/sql/field.cc b/sql/field.cc index 0ff82ac1a97..ba758d51171 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -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 diff --git a/sql/field.h b/sql/field.h index 6418730051c..ea90b5fa37c 100644 --- a/sql/field.h +++ b/sql/field.h @@ -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 *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); }; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index f6d3d67dabc..25c99d9065a 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -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()); diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 7f18e2d8140..799a7050608 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -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; diff --git a/sql/sp_pcontext.cc b/sql/sp_pcontext.cc index ac6c134514a..f0f41f2cb4b 100644 --- a/sql/sp_pcontext.cc +++ b/sql/sp_pcontext.cc @@ -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; } diff --git a/sql/sp_pcontext.h b/sql/sp_pcontext.h index 69f50b99954..de0c2268a52 100644 --- a/sql/sp_pcontext.h +++ b/sql/sp_pcontext.h @@ -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 m_handlers; - /// Stack of records. - Dynamic_array m_records; - /* In the below example the label <> has two meanings: - GOTO lab : must go before the beginning of the loop diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc index 3f432514e40..0ad0ff6a13a 100644 --- a/sql/sp_rcontext.cc +++ b/sql/sp_rcontext.cc @@ -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 it(*const_cast(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 &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); - 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; diff --git a/sql/sp_type_def.h b/sql/sp_type_def.h new file mode 100644 index 00000000000..da47003fcef --- /dev/null +++ b/sql/sp_type_def.h @@ -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 *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 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 diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 115fd4f3b4a..9db15e662b4 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -4110,14 +4110,13 @@ int select_dumpvar::prepare(List &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 &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()); diff --git a/sql/sql_class.h b/sql/sql_class.h index 7a46b1002ca..a98998c7a18 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -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 &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