MDEV-34319: DECLARE TYPE .. TABLE OF .. INDEX BY

in stored routines

This patch adds support for associative arrays in stored procedures
for sql_mode=ORACLE.
The syntax follows Oracle's PL/SQL syntax for associative arrays -
TYPE assoc_array_t IS TABLE OF VARCHAR2(100) INDEX BY INTEGER;
or
TYPE assoc_array_t IS TABLE OF record_t INDEX BY VARCHAR2(100);
where record_t is a record type.

The following functions were added for associative arrays:
- COUNT - Retrieve the number of elements within the arra
- EXISTS - Check whether given key exists in the array
- FIRST - Retrieve the first key in the array
- LAST - Retrieve the last key in the array
- PRIOR - Retrieve the key before the given key
- NEXT - Retrieve the key after the given key
- DELETE - Remove the element with the given key or remove all elements
if no key is given

The arrays/elements can be initialized with the following methods:
- Constructor
i.e. array:= assoc_array_t('key1'=>1, 'key2'=>2, 'key3'=>3)
- Assignment
i.e. array(key):= record_t(1, 2)
- SELECT INTO
i.e. SELECT x INTO array(key)

TODOs:
- Nested tables are not supported yet.
i.e. TYPE assoc_array_t IS TABLE OF other_assoc_array_t INDEX BY INTEGER;
- Associative arrays comparisons are not supported yet.
This commit is contained in:
Iqbal Hassan 2024-10-11 13:06:20 +08:00 committed by Sergei Golubchik
parent 9e13cf0862
commit 5930c48c41
49 changed files with 11651 additions and 967 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_composite.cc ../sql/item_composite.cc
../sql/sql_type_row.cc
../sql/sql_mode.cc
../sql/sql_type_string.cc

View File

@ -0,0 +1,18 @@
# Copyright (c) 2023-2025, MariaDB corporation
#
# 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 Street, Fifth Floor, Boston, MA 02110-1335 USA
MYSQL_ADD_PLUGIN(type_assoc_array sql_type_assoc_array.cc
RECOMPILE_FOR_EMBEDDED
MANDATORY COMPONENT Assoc_array)

View File

@ -0,0 +1,89 @@
SET sql_mode=oracle;
CREATE TABLE t1 (a VARCHAR(64));
#
# MDEV-34319 DECLARE TYPE .. TABLE OF .. INDEX BY in stored routines
#
DECLARE
TYPE person_t IS RECORD
(
first_name VARCHAR(64),
last_name VARCHAR(64)
);
TYPE table_of_peson_t IS TABLE OF person_t INDEX BY VARCHAR2(20);
person_by_nickname table_of_peson_t;
BEGIN
person_by_nickname('Monty') := person_t('Michael', 'Widenius');
INSERT INTO t1 VALUES (person_by_nickname.FIRST);
INSERT INTO t1 VALUES (person_by_nickname('Monty').first_name);
DELETE FROM t1 WHERE person_by_nickname.EXISTS('Monty');
INSERT INTO t1 VALUES ('1');
DELETE FROM t1 WHERE person_by_nickname IS NOT NULL;
person_by_nickname:= NULL;
INSERT INTO t1 VALUES ('1');
DELETE FROM t1 WHERE person_by_nickname IS NULL;
END;
$$
DECLARE
TYPE first_names_t IS TABLE OF VARCHAR2(64) INDEX BY VARCHAR2(20);
first_names first_names_t;
nick VARCHAR(64):= 'Monty';
BEGIN
first_names('Monty') := 'Michael';
INSERT INTO t1 VALUES (first_names('Monty'));
INSERT INTO t1 VALUES (first_names(nick));
INSERT INTO t1 VALUES (first_names(TRIM(nick || ' ')));
INSERT INTO t1 VALUES (first_names(TRIM(first_names.LAST)));
SELECT * FROM t1;
CREATE TABLE t2 AS SELECT first_names('Monty');
END;
$$
a
Michael
Michael
Michael
Michael
DROP TABLE t1;
DROP TABLE t2;
include/show_binlog_events.inc
Log_name Pos Event_type Server_id End_log_pos Info
master-bin.000001 # Gtid # # GTID #-#-#
master-bin.000001 # Query # # use `test`; CREATE TABLE t1 (a VARCHAR(64))
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES (NAME_CONST('person_by_nickname.FIRST',_utf8mb4'Monty' COLLATE 'utf8mb4_uca1400_ai_ci'))
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES (NAME_CONST('person_by_nickname(\'Monty\').first_name',_utf8mb4'Michael' COLLATE 'utf8mb4_uca1400_ai_ci'))
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; DELETE FROM t1 WHERE NAME_CONST('person_by_nickname.EXISTS(\'Monty\')',1)
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES ('1')
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; DELETE FROM t1 WHERE NAME_CONST('person_by_nickname',1) IS NOT NULL
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES ('1')
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; DELETE FROM t1 WHERE NAME_CONST('person_by_nickname',NULL) IS NULL
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES (NAME_CONST('first_names(\'Monty\')',_utf8mb4'Michael' COLLATE 'utf8mb4_uca1400_ai_ci'))
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES (NAME_CONST('first_names(nick)',_utf8mb4'Michael' COLLATE 'utf8mb4_uca1400_ai_ci'))
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES (NAME_CONST('first_names(TRIM(nick || \' \'))',_utf8mb4'Michael' COLLATE 'utf8mb4_uca1400_ai_ci'))
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES (NAME_CONST('first_names(TRIM(first_names.LAST))',_utf8mb4'Michael' COLLATE 'utf8mb4_uca1400_ai_ci'))
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # GTID #-#-#
master-bin.000001 # Query # # use `test`; CREATE TABLE t2 AS SELECT NAME_CONST('first_names(\'Monty\')',_utf8mb4'Michael' COLLATE 'utf8mb4_uca1400_ai_ci')
master-bin.000001 # Gtid # # GTID #-#-#
master-bin.000001 # Query # # use `test`; DROP TABLE "t1" /* generated by server */
master-bin.000001 # Gtid # # GTID #-#-#
master-bin.000001 # Query # # use `test`; DROP TABLE "t2" /* generated by server */

View File

@ -0,0 +1,67 @@
--source include/not_embedded.inc
--source include/have_binlog_format_statement.inc
SET sql_mode=oracle;
--disable_query_log
reset master; # get rid of previous tests binlog
--enable_query_log
CREATE TABLE t1 (a VARCHAR(64));
--echo #
--echo # MDEV-34319 DECLARE TYPE .. TABLE OF .. INDEX BY in stored routines
--echo #
DELIMITER $$;
DECLARE
TYPE person_t IS RECORD
(
first_name VARCHAR(64),
last_name VARCHAR(64)
);
TYPE table_of_peson_t IS TABLE OF person_t INDEX BY VARCHAR2(20);
person_by_nickname table_of_peson_t;
BEGIN
person_by_nickname('Monty') := person_t('Michael', 'Widenius');
INSERT INTO t1 VALUES (person_by_nickname.FIRST);
INSERT INTO t1 VALUES (person_by_nickname('Monty').first_name);
DELETE FROM t1 WHERE person_by_nickname.EXISTS('Monty');
INSERT INTO t1 VALUES ('1');
DELETE FROM t1 WHERE person_by_nickname IS NOT NULL;
person_by_nickname:= NULL;
INSERT INTO t1 VALUES ('1');
DELETE FROM t1 WHERE person_by_nickname IS NULL;
END;
$$
DELIMITER ;$$
DELIMITER $$;
DECLARE
TYPE first_names_t IS TABLE OF VARCHAR2(64) INDEX BY VARCHAR2(20);
first_names first_names_t;
nick VARCHAR(64):= 'Monty';
BEGIN
first_names('Monty') := 'Michael';
INSERT INTO t1 VALUES (first_names('Monty'));
INSERT INTO t1 VALUES (first_names(nick));
INSERT INTO t1 VALUES (first_names(TRIM(nick || ' ')));
INSERT INTO t1 VALUES (first_names(TRIM(first_names.LAST)));
SELECT * FROM t1;
CREATE TABLE t2 AS SELECT first_names('Monty');
END;
$$
DELIMITER ;$$
DROP TABLE t1;
DROP TABLE t2;
--let $binlog_file = LAST
source include/show_binlog_events.inc;

View File

@ -0,0 +1,75 @@
include/master-slave.inc
[connection master]
#
# MDEV-34319 DECLARE TYPE .. TABLE OF .. INDEX BY in stored routines
#
SET sql_mode=oracle;
SET NAMES utf8;
connection master;
CREATE TABLE t1 (a VARCHAR(64));
DECLARE
TYPE person_t IS RECORD
(
first_name VARCHAR(64),
last_name VARCHAR(64)
);
TYPE table_of_peson_t IS TABLE OF person_t INDEX BY VARCHAR2(20);
person_by_nickname table_of_peson_t;
BEGIN
person_by_nickname('Monty') := person_t('Michael', 'Widenius');
INSERT INTO t1 VALUES (person_by_nickname.FIRST);
INSERT INTO t1 VALUES (person_by_nickname('Monty').first_name);
END;
$$
connection slave;
SELECT a FROM t1;
a
Monty
Michael
connection master;
DECLARE
TYPE person_t IS RECORD
(
first_name VARCHAR(64),
last_name VARCHAR(64)
);
TYPE table_of_peson_t IS TABLE OF person_t INDEX BY VARCHAR2(20);
person_by_nickname table_of_peson_t;
BEGIN
person_by_nickname('Monty') := person_t('Michael', 'Widenius');
DELETE FROM t1 WHERE person_by_nickname IS NOT NULL;
END;
$$
connection slave;
SELECT a FROM t1;
a
connection master;
DECLARE
TYPE first_names_t IS TABLE OF VARCHAR2(64) INDEX BY VARCHAR2(20);
first_names first_names_t;
`first names` first_names_t;
nick VARCHAR(64):= 'Monty';
BEGIN
first_names('Monty') := 'Michael';
`first names`('Alex') := 'Alexander';
INSERT INTO t1 VALUES (first_names('Monty'));
INSERT INTO t1 VALUES (first_names(nick));
INSERT INTO t1 VALUES (first_names(TRIM(nick || ' ')));
INSERT INTO t1 VALUES (first_names(TRIM(first_names.LAST)));
INSERT INTO t1 VALUES (`first names`(`first names`.LAST));
CREATE TABLE t2 AS SELECT first_names('Monty');
END;
$$
connection slave;
SELECT a FROM t1;
a
Michael
Michael
Michael
Michael
Alexander
connection master;
DROP TABLE t1;
DROP TABLE t2;
connection slave;
include/rpl_end.inc

View File

@ -0,0 +1,87 @@
--source include/master-slave.inc
--echo #
--echo # MDEV-34319 DECLARE TYPE .. TABLE OF .. INDEX BY in stored routines
--echo #
SET sql_mode=oracle;
SET NAMES utf8;
connection master;
CREATE TABLE t1 (a VARCHAR(64));
DELIMITER $$;
DECLARE
TYPE person_t IS RECORD
(
first_name VARCHAR(64),
last_name VARCHAR(64)
);
TYPE table_of_peson_t IS TABLE OF person_t INDEX BY VARCHAR2(20);
person_by_nickname table_of_peson_t;
BEGIN
person_by_nickname('Monty') := person_t('Michael', 'Widenius');
INSERT INTO t1 VALUES (person_by_nickname.FIRST);
INSERT INTO t1 VALUES (person_by_nickname('Monty').first_name);
END;
$$
DELIMITER ;$$
sync_slave_with_master;
SELECT a FROM t1;
connection master;
DELIMITER $$;
DECLARE
TYPE person_t IS RECORD
(
first_name VARCHAR(64),
last_name VARCHAR(64)
);
TYPE table_of_peson_t IS TABLE OF person_t INDEX BY VARCHAR2(20);
person_by_nickname table_of_peson_t;
BEGIN
person_by_nickname('Monty') := person_t('Michael', 'Widenius');
DELETE FROM t1 WHERE person_by_nickname IS NOT NULL;
END;
$$
DELIMITER ;$$
sync_slave_with_master;
SELECT a FROM t1;
connection master;
DELIMITER $$;
DECLARE
TYPE first_names_t IS TABLE OF VARCHAR2(64) INDEX BY VARCHAR2(20);
first_names first_names_t;
`first names` first_names_t;
nick VARCHAR(64):= 'Monty';
BEGIN
first_names('Monty') := 'Michael';
`first names`('Alex') := 'Alexander';
INSERT INTO t1 VALUES (first_names('Monty'));
INSERT INTO t1 VALUES (first_names(nick));
INSERT INTO t1 VALUES (first_names(TRIM(nick || ' ')));
INSERT INTO t1 VALUES (first_names(TRIM(first_names.LAST)));
INSERT INTO t1 VALUES (`first names`(`first names`.LAST));
CREATE TABLE t2 AS SELECT first_names('Monty');
END;
$$
DELIMITER ;$$
sync_slave_with_master;
SELECT a FROM t1;
connection master;
DROP TABLE t1;
DROP TABLE t2;
sync_slave_with_master;
--source include/rpl_end.inc

View File

@ -0,0 +1,107 @@
SET sql_mode=ORACLE;
#
# MDEV-34319 DECLARE TYPE .. TABLE OF .. INDEX BY in stored routines
#
CREATE PROCEDURE p1() AS
TYPE marks_t IS TABLE OF NUMBER INDEX BY VARCHAR2(20);
marks marks_t:= marks_t('1' => 43, '2' => 99);
BEGIN
marks(1) := 62;
SELECT marks(1);
END;
$$
SHOW PROCEDURE CODE p1;
Pos Instruction
0 set marks@0 marks_t('1'=>43,'2'=>99)
1 set marks@0[1] 62
2 stmt 0 "SELECT marks(1)"
3 destruct associative_array marks@0
DROP PROCEDURE p1;
CREATE PROCEDURE p1() AS
TYPE person_t IS RECORD
(
first_name VARCHAR(64),
last_name VARCHAR(64)
);
TYPE table_of_peson_t IS TABLE OF person_t INDEX BY VARCHAR2(20);
person_by_nickname table_of_peson_t:=
table_of_peson_t(
'Monty' => person_t('Michael', 'Widenius'),
'Serg' => person_t('Sergei ', 'Golubchik')) ;
nick VARCHAR(20);
BEGIN
nick:= person_by_nickname.FIRST;
person_by_nickname(nick).first_name:= 'Michael';
person_by_nickname(nick):= person_t('Michael', 'Widenius');
END;
$$
SHOW PROCEDURE CODE p1;
Pos Instruction
0 set person_by_nickname@0 table_of_peson_t('Monty'=>('Michael','Widenius'),'Serg'=>('Sergei ','Golubchik'))
1 set nick@1 NULL
2 set nick@1 person_by_nickname@0.first()
3 set person_by_nickname@0[nick@1].first_name 'Michael'
4 set person_by_nickname@0[nick@1] ('Michael','Widenius')
5 destruct associative_array person_by_nickname@0
DROP PROCEDURE p1;
#
# Make sure assoc array variables generate sp_instr_destruct_variable
#
CREATE PROCEDURE p1 AS
TYPE person_t IS RECORD
(
first_name VARCHAR(64),
last_name VARCHAR(64)
);
TYPE table_of_peson_t IS TABLE OF person_t INDEX BY VARCHAR2(20);
TYPE table_of_int_t IS TABLE OF INT INDEX BY INT;
BEGIN
SELECT '>block#0' AS comment;
DECLARE
assoc_of_record_0 table_of_peson_t;
assoc_of_scalar_0 table_of_int_t;
BEGIN
SELECT '>block#1' AS comment;
DECLARE
assoc_of_record_1 table_of_peson_t;
assoc_of_scalar_1 table_of_int_t;
BEGIN
SELECT '>block#2' AS comment;
DECLARE
assoc_of_record_2 table_of_peson_t;
assoc_of_scalar_2 table_of_int_t;
BEGIN
SELECT '>block#3' AS comment;
NULL;
SELECT '<block#3' AS comment;
END;
SELECT '<block#2' AS comment;
END;
SELECT '<block#1' AS comment;
END;
SELECT '<block#0' AS comment;
END;
$$
SHOW PROCEDURE CODE p1;
Pos Instruction
0 stmt 0 "SELECT '>block#0' AS comment"
1 set assoc_of_record_0@0 NULL
2 set assoc_of_scalar_0@1 NULL
3 stmt 0 "SELECT '>block#1' AS comment"
4 set assoc_of_record_1@2 NULL
5 set assoc_of_scalar_1@3 NULL
6 stmt 0 "SELECT '>block#2' AS comment"
7 set assoc_of_record_2@4 NULL
8 set assoc_of_scalar_2@5 NULL
9 stmt 0 "SELECT '>block#3' AS comment"
10 stmt 0 "SELECT '<block#3' AS comment"
11 destruct associative_array assoc_of_scalar_2@5
12 destruct associative_array assoc_of_record_2@4
13 stmt 0 "SELECT '<block#2' AS comment"
14 destruct associative_array assoc_of_scalar_1@3
15 destruct associative_array assoc_of_record_1@2
16 stmt 0 "SELECT '<block#1' AS comment"
17 destruct associative_array assoc_of_scalar_0@1
18 destruct associative_array assoc_of_record_0@0
19 stmt 0 "SELECT '<block#0' AS comment"
DROP PROCEDURE p1;

View File

@ -0,0 +1,89 @@
-- source include/have_debug.inc
SET sql_mode=ORACLE;
--echo #
--echo # MDEV-34319 DECLARE TYPE .. TABLE OF .. INDEX BY in stored routines
--echo #
DELIMITER $$;
CREATE PROCEDURE p1() AS
TYPE marks_t IS TABLE OF NUMBER INDEX BY VARCHAR2(20);
marks marks_t:= marks_t('1' => 43, '2' => 99);
BEGIN
marks(1) := 62;
SELECT marks(1);
END;
$$
DELIMITER ;$$
SHOW PROCEDURE CODE p1;
DROP PROCEDURE p1;
DELIMITER $$;
CREATE PROCEDURE p1() AS
TYPE person_t IS RECORD
(
first_name VARCHAR(64),
last_name VARCHAR(64)
);
TYPE table_of_peson_t IS TABLE OF person_t INDEX BY VARCHAR2(20);
person_by_nickname table_of_peson_t:=
table_of_peson_t(
'Monty' => person_t('Michael', 'Widenius'),
'Serg' => person_t('Sergei ', 'Golubchik')) ;
nick VARCHAR(20);
BEGIN
nick:= person_by_nickname.FIRST;
person_by_nickname(nick).first_name:= 'Michael';
person_by_nickname(nick):= person_t('Michael', 'Widenius');
END;
$$
DELIMITER ;$$
SHOW PROCEDURE CODE p1;
DROP PROCEDURE p1;
--echo #
--echo # Make sure assoc array variables generate sp_instr_destruct_variable
--echo #
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE person_t IS RECORD
(
first_name VARCHAR(64),
last_name VARCHAR(64)
);
TYPE table_of_peson_t IS TABLE OF person_t INDEX BY VARCHAR2(20);
TYPE table_of_int_t IS TABLE OF INT INDEX BY INT;
BEGIN
SELECT '>block#0' AS comment;
DECLARE
assoc_of_record_0 table_of_peson_t;
assoc_of_scalar_0 table_of_int_t;
BEGIN
SELECT '>block#1' AS comment;
DECLARE
assoc_of_record_1 table_of_peson_t;
assoc_of_scalar_1 table_of_int_t;
BEGIN
SELECT '>block#2' AS comment;
DECLARE
assoc_of_record_2 table_of_peson_t;
assoc_of_scalar_2 table_of_int_t;
BEGIN
SELECT '>block#3' AS comment;
NULL;
SELECT '<block#3' AS comment;
END;
SELECT '<block#2' AS comment;
END;
SELECT '<block#1' AS comment;
END;
SELECT '<block#0' AS comment;
END;
$$
DELIMITER ;$$
SHOW PROCEDURE CODE p1;
DROP PROCEDURE p1;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,7 @@
package My::Suite::Type_assoc_array;
@ISA = qw(My::Suite);
sub is_default { 1 }
bless { };

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,462 @@
/*
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_ASSOC_ARRAY_INCLUDED
#define SQL_TYPE_ASSOC_ARRAY_INCLUDED
#include "sql_type_composite.h"
#include "field_composite.h"
#include "item_composite.h"
#include "sp_type_def.h"
class Item_field_packable;
/*
Special handler associative arrays
*/
class Type_handler_assoc_array: public Type_handler_composite
{
public:
static const Type_handler_assoc_array *singleton();
public:
bool has_methods() const override { return true; }
bool has_functors() const override { return true; }
bool is_complex() const override
{
/*
Have an assoc array variable generate an sp_instr_destruct_variable
instruction in the end of the DECLARE/BEGIN/END block declaring
the variable.
*/
return true;
}
const Type_collection *type_collection() const override;
const Type_handler *type_handler_for_comparison() const override
{
return singleton();
}
bool Spvar_definition_with_complex_data_types(Spvar_definition *def)
const override;
bool Column_definition_set_attributes(THD *thd,
Column_definition *def,
const Lex_field_type_st &attr,
column_definition_type_t type)
const override
{
/*
Disallow wrong use of associative_array:
CREATE TABLE t1 (a ASSOCIATIVE_ARRAY);
CREATE FUNCTION .. RETURN ASSOCIATEIVE ARRAY ..;
*/
if (!def->get_attr_const_void_ptr(0))
{
my_error(ER_NOT_ALLOWED_IN_THIS_CONTEXT, MYF(0), name().ptr());
return true;
}
return Type_handler_composite::Column_definition_set_attributes(thd, def,
attr,
type);
}
bool sp_variable_declarations_finalize(THD *thd,
LEX *lex, int nvars,
const Column_definition &def)
const override;
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;
Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const
override
{
MY_ASSERT_UNREACHABLE();
return nullptr;
}
protected:
static bool lex_ident_col_eq(Lex_ident_column *a, Lex_ident_column *b)
{
return a->streq(*b);
}
bool sp_check_assoc_array_args(const sp_type_def &def, List<Item> &args) const
{
List<Lex_ident_column> names;
List_iterator<Item> it(args);
for (Item *item= it++; item; item= it++)
{
/*
Make sure all value have keys:
assoc_array_type('key1'=>'val1', 'key2'=>'val2') -- correct
assoc_array_type('val1' , 'val2' ) -- wrong
*/
if (unlikely(!item->is_explicit_name()))
{
my_error(ER_NEED_NAMED_ASSOCIATION, MYF(0), def.get_name());
return true;
}
/*
Make sure keys are unique in:
assoc_array_type('key1'=>'val1', 'key2'=>'val2')
*/
if (unlikely(names.add_unique(&item->name, lex_ident_col_eq)))
{
my_error(ER_DUP_UNKNOWN_IN_INDEX, MYF(0), item->name.str);
return true;
}
}
return false;
}
public:
/*
SELECT 1 INTO spvar(arg);
SELECT 1 INTO spvar(arg).field_name;
*/
my_var *make_outvar_lvalue_functor(THD *thd, const Lex_ident_sys_st &name,
Item *arg,
const Lex_ident_sys &opt_field,
sp_head *sphead,
const sp_rcontext_addr &addr,
bool validate_only) const override;
// assoc_array_var:= assoc_array_type('key1'=>'val1', 'key2'=>'val2')
Item *make_typedef_constructor_item(THD *thd, const sp_type_def &def,
List<Item> *args) const override;
Item_cache *Item_get_cache(THD *thd, const Item *item) const override
{
MY_ASSERT_UNREACHABLE();
return nullptr;
}
bool set_comparator_func(THD *thd, Arg_comparator *cmp) const override
{
MY_ASSERT_UNREACHABLE();
return true;
}
cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const override
{
MY_ASSERT_UNREACHABLE();
return nullptr;
}
in_vector *make_in_vector(THD *thd, const Item_func_in *f, uint nargs) const
override
{
MY_ASSERT_UNREACHABLE();
return nullptr;
}
bool Item_func_in_fix_comparator_compatible_types(THD *thd,
Item_func_in *) const
override
{
MY_ASSERT_UNREACHABLE();
return false;
}
String *print_item_value(THD *thd, Item *item, String *str) const override;
virtual Item_splocal *create_item_functor(THD *thd,
const Lex_ident_sys &varname,
const sp_rcontext_addr &addr,
List<Item> *args,
const Lex_ident_sys &member,
const Lex_ident_cli_st &name_cli)
const override;
sp_instr *create_instr_set_assign_functor(THD *thd, LEX *lex,
const Qualified_ident &ident,
const sp_rcontext_addr &addr,
List<Item> *params,
const Lex_ident_sys_st &field_name,
Item *item,
const LEX_CSTRING &expr_str)
const override;
virtual
Item *create_item_method(THD *thd,
const Lex_ident_sys &ca,
const Lex_ident_sys &cb,
List<Item> *args,
const Lex_ident_cli_st &query_fragment)
const override;
bool key_to_lex_cstring(THD *thd,
Item **key,
const LEX_CSTRING& name,
LEX_CSTRING& out_key) const override;
bool get_item_index(THD *thd,
const Item_field *item,
const LEX_CSTRING& name,
uint& idx) const override
{
DBUG_ASSERT(0);
return true;
}
Item_field *get_item(THD *thd,
const Item_field *item,
const LEX_CSTRING& name) const override;
Item_field *get_or_create_item(THD *thd,
Item_field *item,
const LEX_CSTRING& name) const override;
void prepare_for_set(Item_field *item) const override;
bool finalize_for_set(Item_field *item) const override;
};
class Field_assoc_array final :public Field_composite
{
protected:
MEM_ROOT m_mem_root;
TREE m_tree;
TABLE *m_table;
TABLE_SHARE *m_table_share;
Row_definition_list *m_def;
Field *m_key_field;
Field *m_element_field;
Item_field_packable *m_item_pack;
Item *m_item;
/*
Modified copy of the key definition
*/
Spvar_definition m_key_def;
public:
Field_assoc_array(uchar *ptr_arg,
const LEX_CSTRING *field_name_arg);
~Field_assoc_array();
void set_array_def(Row_definition_list *def)
{
DBUG_ASSERT(def);
DBUG_ASSERT(def->elements == 2);
m_def= def;
}
const Row_definition_list *get_array_def() const
{
return m_def;
}
Item_field *make_item_field_spvar(THD *thd,
const Spvar_definition &def) override;
bool sp_prepare_and_store_item(THD *thd, Item **value) override;
uint rows() const override;
bool get_key(String *key, bool is_first) override;
bool get_next_key(const String *curr_key, String *next_key) override;
bool get_prior_key(const String *curr_key, String *prior_key) override;
Item_field *element_by_key(THD *thd, String *key) override;
Item_field *element_by_key(THD *thd, String *key) const override;
Item **element_addr_by_key(THD *thd, String *key) override;
bool delete_all_elements() override;
bool delete_element_by_key(String *key) override;
void expr_event_handler(THD *thd, expr_event_t event) override
{
if ((bool) (event & expr_event_t::DESTRUCT_ANY))
{
delete_all_elements();
set_null();
return;
}
DBUG_ASSERT(0);
}
Item *get_element_item() const override { return m_item; }
Field *get_key_field() const { return m_key_field; }
protected:
bool copy_and_convert_key(const String *key, String &key_copy) const;
bool unpack_key(const Binary_string &key, String *key_dst) const;
bool create_fields(THD *thd);
/*
Initialize the element base Field and Item_field for the
associative array.
*/
bool init_element_base(THD *thd);
bool create_element_buffer(THD *thd, Binary_string *buffer);
bool insert_element(String &&key, Binary_string &&element);
bool get_next_or_prior_key(const String *curr_key,
String *new_key,
bool is_next);
};
/**
Item_field for the associative array data type
*/
class Item_field_assoc_array: public Item_field, public Item_composite_base
{
public:
Item_field_assoc_array(THD *thd, Field *field)
:Item_field(thd, field)
{ }
Item *do_get_copy(THD *thd) const override
{ return get_item_copy<Item_field_assoc_array>(thd, this); }
const Type_handler *type_handler() const override
{ return Type_handler_assoc_array::singleton(); }
bool set_array_def(THD *thd, Row_definition_list *def);
uint rows() const override
{
return get_composite_field()->rows();
}
bool get_key(String *key, bool is_first) override
{
return get_composite_field()->get_key(key, is_first);
}
bool get_next_key(const String *curr_key, String *next_key) override
{
return get_composite_field()->get_next_key(curr_key, next_key);
}
Item *element_by_key(THD *thd, String *key) override
{
return ((const Field_composite *)get_composite_field())->
element_by_key(thd, key);
}
Item **element_addr_by_key(THD *thd, Item **ref, String *key) override
{
return get_composite_field()->element_addr_by_key(thd, key);
}
Field_composite *get_composite_field() const override
{
return dynamic_cast<Field_composite *>(field);
}
};
class Item_assoc_array: public Item_composite
{
protected:
Lex_ident_column m_name;
public:
Item_assoc_array(THD *thd, const Lex_ident_column &name)
:Item_composite(thd),
m_name(name)
{ }
Item_assoc_array(THD *thd, const Lex_ident_column &name, List<Item> &list)
:Item_composite(thd, list),
m_name(name)
{ }
const Type_handler *type_handler() const override
{
return Type_handler_assoc_array::singleton();
}
Field *create_tmp_field_ex(MEM_ROOT *root, TABLE *table, Tmp_field_src *src,
const Tmp_field_param *param) override
{
return NULL;
}
uint rows() const override;
bool get_key(String *key, bool is_first) override;
bool get_next_key(const String *curr_key, String *next_key) override;
Item *element_by_key(THD *thd, String *key) override;
Item **element_addr_by_key(THD *thd, Item **addr_arg, String *key) override;
bool fix_fields(THD *thd, Item **ref) override;
void bring_value() override;
void print(String *str, enum_query_type query_type) override;
Item *do_get_copy(THD *thd) const override
{ return get_item_copy<Item_assoc_array>(thd, this); }
Item *do_build_clone(THD *thd) const override;
};
class Item_splocal_assoc_array_base :public Item_composite_base
{
protected:
Item *m_key;
public:
Item_splocal_assoc_array_base(Item *key);
};
class Item_splocal_assoc_array_element :public Item_splocal,
public Item_splocal_assoc_array_base
{
protected:
bool set_value(THD *thd, sp_rcontext *ctx, Item **it) override;
public:
Item_splocal_assoc_array_element(THD *thd,
const sp_rcontext_addr &addr,
const Lex_ident_sys &sp_var_name,
Item *key, const Type_handler *handler,
uint pos_in_q= 0, uint len_in_q= 0);
bool fix_fields(THD *thd, Item **) override;
Item *this_item() override;
const Item *this_item() const override;
Item **this_item_addr(THD *thd, Item **) override;
bool append_for_log(THD *thd, String *str) override;
void print(String *str, enum_query_type query_type) override;
Item *do_get_copy(THD *) const override { return nullptr; }
Item *do_build_clone(THD *thd) const override { return nullptr; }
Item_composite_base *get_composite_variable(sp_rcontext *ctx) const;
};
class Item_splocal_assoc_array_element_field :
public Item_splocal_row_field_by_name,
public Item_splocal_assoc_array_base
{
protected:
Item_field *m_element_item;
public:
Item_splocal_assoc_array_element_field(THD *thd,
const sp_rcontext_addr &addr,
const Lex_ident_sys &sp_var_name,
Item *key,
const Lex_ident_sys &sp_field_name,
const Type_handler *handler,
uint pos_in_q= 0, uint len_in_q= 0);
bool fix_fields(THD *thd, Item **) override;
Item *this_item() override;
const Item *this_item() const override;
Item **this_item_addr(THD *thd, Item **) override;
bool append_for_log(THD *thd, String *str) override;
void print(String *str, enum_query_type query_type) override;
Item_composite_base *get_composite_variable(sp_rcontext *ctx) const;
};
#endif /* SQL_TYPE_ASSOC_ARRAY_INCLUDED */

View File

@ -0,0 +1,35 @@
#
# MDEV-34319 DECLARE TYPE type_name IS RECORD (..) with scalar members in stored routines
#
#
# Demonstrate UDT field type for associative array element
#
SET sql_mode=ORACLE;
DECLARE
TYPE uuids_t IS TABLE OF UUID INDEX BY INTEGER;
uuids uuids_t;
BEGIN
uuids(1):= 'e7a69166-a557-4bbe-ab4d-d390114b51fa';
SELECT uuids(1);
END;
$$
uuids(1)
e7a69166-a557-4bbe-ab4d-d390114b51fa
#
# Demonstrate UDT field type for associative array RECORD field
#
SET sql_mode=ORACLE;
DECLARE
TYPE rec_t IS RECORD (
a INT,
b UUID
);
TYPE uuids_t IS TABLE OF rec_t INDEX BY INTEGER;
uuids uuids_t;
BEGIN
uuids(1):= rec_t(1, 'e7a69166-a557-4bbe-ab4d-d390114b51fa');
SELECT uuids(1).a,uuids(1).b;
END;
$$
uuids(1).a uuids(1).b
1 e7a69166-a557-4bbe-ab4d-d390114b51fa

View File

@ -0,0 +1,38 @@
--echo #
--echo # MDEV-34319 DECLARE TYPE type_name IS RECORD (..) with scalar members in stored routines
--echo #
--echo #
--echo # Demonstrate UDT field type for associative array element
--echo #
SET sql_mode=ORACLE;
DELIMITER $$;
DECLARE
TYPE uuids_t IS TABLE OF UUID INDEX BY INTEGER;
uuids uuids_t;
BEGIN
uuids(1):= 'e7a69166-a557-4bbe-ab4d-d390114b51fa';
SELECT uuids(1);
END;
$$
DELIMITER ;$$
--echo #
--echo # Demonstrate UDT field type for associative array RECORD field
--echo #
SET sql_mode=ORACLE;
DELIMITER $$;
DECLARE
TYPE rec_t IS RECORD (
a INT,
b UUID
);
TYPE uuids_t IS TABLE OF rec_t INDEX BY INTEGER;
uuids uuids_t;
BEGIN
uuids(1):= rec_t(1, 'e7a69166-a557-4bbe-ab4d-d390114b51fa');
SELECT uuids(1).a,uuids(1).b;
END;
$$
DELIMITER ;$$

View File

@ -196,6 +196,8 @@ SET (SQL_SOURCE
opt_hints_parser.cc opt_hints_parser.h scan_char.h
opt_hints.cc opt_hints.h
sql_type_row.cc
sql_type_composite.cc sql_type_composite.h
item_composite.cc item_composite.h
${CMAKE_CURRENT_BINARY_DIR}/lex_hash.h
${CMAKE_CURRENT_BINARY_DIR}/lex_token.h
${GEN_SOURCES}

View File

@ -44,6 +44,7 @@ class Item_bool_func;
class Item_equal;
class Virtual_tmp_table;
class Qualified_column_ident;
class Qualified_ident;
class Table_ident;
class SEL_ARG;
class RANGE_OPT_PARAM;
@ -5722,6 +5723,7 @@ public:
- variables with explicit data types: DECLARE a INT;
- variables with data type references: DECLARE a t1.a%TYPE;
- ROW type variables
- Associative arrays
Notes:
- Scalar variables have m_field_definitions==NULL.
@ -5757,6 +5759,14 @@ public:
m_cursor_rowtype_offset(0),
m_row_field_definitions(NULL)
{ }
Spvar_definition(const Column_definition &col_def)
: Column_definition(col_def),
m_column_type_ref(NULL),
m_table_rowtype_ref(NULL),
m_cursor_rowtype_ref(false),
m_cursor_rowtype_offset(0),
m_row_field_definitions(NULL)
{ }
const Type_handler *type_handler() const
{
return Type_handler_hybrid_field_type::type_handler();
@ -5812,7 +5822,8 @@ public:
}
uint is_row() const
{
return m_row_field_definitions != NULL;
return m_row_field_definitions != NULL &&
type_handler() == &type_handler_row;
}
// Check if "this" defines a ROW variable with n elements
uint is_row(uint n) const
@ -5824,10 +5835,11 @@ public:
{
return m_row_field_definitions;
}
void set_row_field_definitions(Row_definition_list *list)
void set_row_field_definitions(const Type_handler *th,
Row_definition_list *list)
{
DBUG_ASSERT(list);
set_handler(&type_handler_row);
set_handler(th);
m_row_field_definitions= list;
}

73
sql/field_composite.h Normal file
View File

@ -0,0 +1,73 @@
/*
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 FIELD_COMPOSITE_INCLUDED
#define FIELD_COMPOSITE_INCLUDED
#include "field.h"
class Field_composite: public Field_null
{
public:
Field_composite(uchar *ptr_arg, const LEX_CSTRING *field_name_arg)
:Field_null(ptr_arg, 0, Field::NONE, field_name_arg, &my_charset_bin)
{}
en_fieldtype tmp_engine_column_type(bool use_packed_rows) const override
{
DBUG_ASSERT(0);
return Field::tmp_engine_column_type(use_packed_rows);
}
enum_conv_type rpl_conv_type_from(const Conv_source &source,
const Relay_log_info *rli,
const Conv_param &param) const override
{
DBUG_ASSERT(0);
return CONV_TYPE_IMPOSSIBLE;
}
virtual uint rows() const { return 0; }
virtual bool get_key(String *key, bool is_first) { return true; }
virtual bool get_next_key(const String *curr_key, String *next_key)
{
return true;
}
virtual bool get_prior_key(const String *curr_key, String *prior_key)
{
return true;
}
virtual Item_field *element_by_key(THD *thd, String *key) { return NULL; }
virtual Item_field *element_by_key(THD *thd, String *key) const
{
return NULL;
}
virtual Item **element_addr_by_key(THD *thd, String *key) { return NULL; }
virtual bool delete_all_elements() { return true; }
virtual bool delete_element_by_key(String *key) { return true; }
/*
Retrieve the Item representing the element of the composite field.
Note that the item here may not represent a particular element but
it does contain the information about the element type.
Use element_by_key() to retrieve the item representing a particular
element of the composite field.
*/
virtual Item *get_element_item() const { return NULL; }
};
#endif /* FIELD_COMPOSITE_INCLUDED */

31
sql/item_composite.cc Normal file
View File

@ -0,0 +1,31 @@
/*
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
*/
#include "my_global.h"
#include "item.h"
#include "item_composite.h"
void Item_composite::illegal_method_call(const char *method)
{
DBUG_ENTER("Item_composite::illegal_method_call");
DBUG_PRINT("error", ("!!! %s method was called for %s",
method, type_handler()->name().ptr()));
DBUG_ASSERT(0);
my_error(ER_OPERAND_COLUMNS, MYF(0), 1);
DBUG_VOID_RETURN;
}

103
sql/item_composite.h Normal file
View File

@ -0,0 +1,103 @@
/*
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 ITEM_COMPOSITE_INCLUDED
#define ITEM_COMPOSITE_INCLUDED
#include "item.h"
class Field_composite;
class Item_composite_base
{
public:
virtual ~Item_composite_base() = default;
// For associative arrays
/// Returns the number of columns for the elements of the array
virtual uint rows() const { return 1; }
virtual bool get_key(String *key, bool is_first) { return true; }
virtual bool get_next_key(const String *curr_key, String *next_key)
{
return true;
}
virtual Item *element_by_key(THD *thd, String *key) { return nullptr; }
virtual Item **element_addr_by_key(THD *thd, Item **addr_arg, String *key)
{
return addr_arg;
}
/*
Get the composite field for the item, when applicable.
*/
virtual Field_composite *get_composite_field() const { return nullptr; }
};
class Item_composite: public Item_fixed_hybrid,
protected Item_args,
public Item_composite_base
{
public:
Item_composite(THD *thd, List<Item> &list)
:Item_fixed_hybrid(thd), Item_args(thd, list)
{ }
Item_composite(THD *thd, Item_args *other)
:Item_fixed_hybrid(thd), Item_args(thd, other)
{ }
Item_composite(THD *thd)
:Item_fixed_hybrid(thd)
{ }
enum Type type() const override { return ROW_ITEM; }
void illegal_method_call(const char *);
void make_send_field(THD *thd, Send_field *) override
{
illegal_method_call((const char*)"make_send_field");
};
double val_real() override
{
illegal_method_call((const char*)"val");
return 0;
};
longlong val_int() override
{
illegal_method_call((const char*)"val_int");
return 0;
};
String *val_str(String *) override
{
illegal_method_call((const char*)"val_str");
return 0;
};
my_decimal *val_decimal(my_decimal *) override
{
illegal_method_call((const char*)"val_decimal");
return 0;
};
bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) override
{
illegal_method_call((const char*)"get_date");
return true;
}
};
#endif /* ITEM_COMPOSITE_INCLUDED */

View File

@ -4812,7 +4812,7 @@ bool Item_func_set_user_var::fix_fields(THD *thd, Item **ref)
break;
case ROW_RESULT:
DBUG_ASSERT(0);
set_handler(&type_handler_row);
set_handler(args[0]->type_handler());
break;
}
if (thd->lex->current_select)

View File

@ -826,6 +826,8 @@ public:
protected:
const Handler *m_func_handler;
public:
Item_handled_func(THD *thd)
:Item_func(thd), m_func_handler(NULL) { }
Item_handled_func(THD *thd, Item *a)
:Item_func(thd, a), m_func_handler(NULL) { }
Item_handled_func(THD *thd, Item *a, Item *b)

View File

@ -25,15 +25,6 @@
#include "sql_class.h" // THD, set_var.h: THD
#include "set_var.h"
void Item_row::illegal_method_call(const char *method)
{
DBUG_ENTER("Item_row::illegal_method_call");
DBUG_PRINT("error", ("!!! %s method was called for row item", method));
DBUG_ASSERT(0);
my_error(ER_OPERAND_COLUMNS, MYF(0), 1);
DBUG_VOID_RETURN;
}
bool Item_row::fix_fields(THD *thd, Item **ref)
{
DBUG_ASSERT(fixed() == 0);

View File

@ -1,6 +1,8 @@
#ifndef ITEM_ROW_INCLUDED
#define ITEM_ROW_INCLUDED
#include "item_composite.h"
/*
Copyright (c) 2002, 2013, Oracle and/or its affiliates.
@ -33,8 +35,7 @@
Item which stores (x,y,...) and ROW(x,y,...).
Note that this can be recursive: ((x,y),(z,t)) is a ROW of ROWs.
*/
class Item_row: public Item_fixed_hybrid,
private Item_args,
class Item_row: public Item_composite,
private Used_tables_and_const_cache
{
table_map not_null_tables_cache;
@ -45,53 +46,22 @@ class Item_row: public Item_fixed_hybrid,
bool with_null;
public:
Item_row(THD *thd, List<Item> &list)
:Item_fixed_hybrid(thd), Item_args(thd, list),
:Item_composite(thd, list),
not_null_tables_cache(0), with_null(0)
{ }
Item_row(THD *thd, Item_row *row)
:Item_fixed_hybrid(thd), Item_args(thd, static_cast<Item_args*>(row)),
:Item_composite(thd, static_cast<Item_args*>(row)),
Used_tables_and_const_cache(),
not_null_tables_cache(0), with_null(0)
{ }
enum Type type() const override { return ROW_ITEM; };
const Type_handler *type_handler() const override { return &type_handler_row; }
Field *create_tmp_field_ex(MEM_ROOT *root, TABLE *table, Tmp_field_src *src,
const Tmp_field_param *param) override
{
return NULL; // Check with Vicentiu why it's called for Item_row
}
void illegal_method_call(const char *);
bool is_null() override { return null_value; }
void make_send_field(THD *thd, Send_field *) override
{
illegal_method_call((const char*)"make_send_field");
};
double val_real() override
{
illegal_method_call((const char*)"val");
return 0;
};
longlong val_int() override
{
illegal_method_call((const char*)"val_int");
return 0;
};
String *val_str(String *) override
{
illegal_method_call((const char*)"val_str");
return 0;
};
my_decimal *val_decimal(my_decimal *) override
{
illegal_method_call((const char*)"val_decimal");
return 0;
};
bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) override
{
illegal_method_call((const char*)"get_date");
return true;
}
bool fix_fields(THD *thd, Item **ref) override;
void fix_after_pullout(st_select_lex *new_parent, Item **ref, bool merge)
override;

View File

@ -55,6 +55,7 @@ SYMBOL symbols[] = {
{ "<<", SYM(SHIFT_LEFT)},
{ ">>", SYM(SHIFT_RIGHT)},
{ "<=>", SYM(EQUAL_SYM)},
{ "=>", SYM(ARROW_SYM)},
{ "ACCOUNT", SYM(ACCOUNT_SYM)},
{ "ACTION", SYM(ACTION)},
{ "ADD", SYM(ADD)},

View File

@ -12328,3 +12328,9 @@ ER_WARN_MALFORMED_HINT
eng "Hint %s is ignored as malformed"
ER_WARN_HINTS_ON_INSERT_PART_OF_INSERT_SELECT
eng "Optimizer hints at the INSERT part of a INSERT..SELECT statement are not supported"
ER_ASSOC_ARRAY_ELEM_NOT_FOUND
eng "Element not found with key '%s'"
ER_NULL_FOR_ASSOC_ARRAY_INDEX
eng "NULL key used for associative array '%s'"
ER_NEED_NAMED_ASSOCIATION
eng "Initializing %s requires named association"

View File

@ -3856,14 +3856,14 @@ sp_head::set_local_variable_row_field_by_name(THD *thd, sp_pcontext *spcont,
if (!(val= adjust_assignment_source(thd, val, NULL)))
return true;
sp_instr_set_row_field_by_name *sp_set=
new (thd->mem_root) sp_instr_set_row_field_by_name(instructions(),
spcont, rh,
spv->offset,
*field_name,
val,
lex, true,
value_query);
sp_instr_set_composite_field_by_name *sp_set=
new (thd->mem_root) sp_instr_set_composite_field_by_name(instructions(),
spcont, rh,
spv->offset,
*field_name,
val,
lex, true,
value_query);
return sp_set == NULL || add_instr(sp_set);
}
@ -3982,7 +3982,7 @@ bool sp_head::spvar_fill_row(THD *thd,
sp_variable *spvar,
Row_definition_list *defs)
{
spvar->field_def.set_row_field_definitions(defs);
spvar->field_def.set_row_field_definitions(&type_handler_row, defs);
spvar->field_def.field_name= spvar->name;
if (fill_spvar_definition(thd, &spvar->field_def))
return true;
@ -4042,7 +4042,7 @@ bool sp_head::spvar_def_fill_type_reference(THD *thd, Spvar_definition *def,
if (!(ref= new (thd->mem_root) Qualified_column_ident(thd, &db, &table,
&column)))
return true;
def->set_column_type_ref(ref);
m_flags|= sp_head::HAS_COLUMN_TYPE_REFS;
@ -4050,6 +4050,35 @@ bool sp_head::spvar_def_fill_type_reference(THD *thd, Spvar_definition *def,
}
bool sp_head::spvar_def_fill_rowtype_reference(THD *thd, Spvar_definition *def,
const LEX_CSTRING &table)
{
Table_ident *ref;
if (!(ref= new (thd->mem_root) Table_ident(&table)))
return true;
def->set_table_rowtype_ref(ref);
m_flags|= sp_head::HAS_COLUMN_TYPE_REFS;
return false;
}
bool sp_head::spvar_def_fill_rowtype_reference(THD *thd, Spvar_definition *def,
const LEX_CSTRING &db,
const LEX_CSTRING &table)
{
Table_ident *ref;
if (!(ref= new (thd->mem_root) Table_ident(thd, &db, &table, false)))
return true;
def->set_table_rowtype_ref(ref);
m_flags|= sp_head::HAS_COLUMN_TYPE_REFS;
return false;
}
bool sp_head::spvar_fill_table_rowtype_reference(THD *thd,
sp_variable *spvar,
const LEX_CSTRING &table)

View File

@ -825,6 +825,11 @@ public:
const LEX_CSTRING &db,
const LEX_CSTRING &table,
const LEX_CSTRING &column);
bool spvar_def_fill_rowtype_reference(THD *thd, Spvar_definition *def,
const LEX_CSTRING &table);
bool spvar_def_fill_rowtype_reference(THD *thd, Spvar_definition *def,
const LEX_CSTRING &db,
const LEX_CSTRING &table);
void set_c_chistics(const st_sp_chistics &chistics);
void set_info(longlong created, longlong modified,

View File

@ -1313,13 +1313,23 @@ sp_instr_set_row_field::print(String *str)
/*
sp_instr_set_field_by_name class functions
sp_instr_set_composite_field_by_name class functions
*/
int
sp_instr_set_row_field_by_name::exec_core(THD *thd, uint *nextp)
sp_instr_set_composite_field_by_name::exec_core(THD *thd, uint *nextp)
{
int res= get_rcontext(thd)->set_variable_row_field_by_name(thd, m_offset,
if (m_key)
{
auto var= get_rcontext(thd)->get_variable(m_offset);
auto handler= var->type_handler()->to_composite();
DBUG_ASSERT(handler);
if (handler->key_to_lex_cstring(thd, &m_key, var->name, m_field_name))
return true;
}
int res= get_rcontext(thd)->set_variable_composite_by_name(thd, m_offset,
m_field_name,
&m_value);
*nextp= m_ip + 1;
@ -1328,30 +1338,93 @@ sp_instr_set_row_field_by_name::exec_core(THD *thd, uint *nextp)
void
sp_instr_set_row_field_by_name::print(String *str)
sp_instr_set_composite_field_by_name::print(String *str)
{
/* set name.field@offset["field"] ... */
size_t rsrv= SP_INSTR_UINT_MAXLEN + 6 + 6 + 3 + 2;
/* set name.field["key"] ... */
sp_variable *var= m_ctx->find_variable(m_offset);
const LEX_CSTRING *prefix= m_rcontext_handler->get_name_prefix();
DBUG_ASSERT(var);
DBUG_ASSERT(var->field_def.is_table_rowtype_ref() ||
var->field_def.is_cursor_rowtype_ref());
DBUG_ASSERT(dynamic_cast<const Type_handler_composite*>(var->type_handler()));
rsrv+= var->name.length + 2 * m_field_name.length + prefix->length;
if (str->reserve(rsrv))
return;
str->qs_append(STRING_WITH_LEN("set "));
str->qs_append(prefix);
str->qs_append(&var->name);
str->qs_append('.');
str->qs_append(&m_field_name);
str->qs_append('@');
str->qs_append(m_offset);
str->qs_append("[\"",2);
str->qs_append(&m_field_name);
str->qs_append("\"]",2);
str->qs_append(' ');
str->append(STRING_WITH_LEN("set "));
str->append(prefix);
str->append(&var->name);
if (!m_key)
{
str->append('.');
str->append(&m_field_name);
}
str->append('@');
str->append_ulonglong(m_offset);
if (!m_key)
{
str->append(STRING_WITH_LEN("[\""));
str->append(&m_field_name);
str->append(STRING_WITH_LEN("\"]"));
}
else
{
str->append('[');
m_key->print(str, enum_query_type(QT_ORDINARY |
QT_ITEM_ORIGINAL_FUNC_NULLIF));
str->append(']');
}
str->append(' ');
m_value->print(str, enum_query_type(QT_ORDINARY |
QT_ITEM_ORIGINAL_FUNC_NULLIF));
}
/*
sp_instr_set_composite_field_by_key class functions
*/
int
sp_instr_set_composite_field_by_key::exec_core(THD *thd, uint *nextp)
{
auto var= get_rcontext(thd)->get_variable(m_offset);
auto handler= var->type_handler()->to_composite();
DBUG_ASSERT(handler);
LEX_CSTRING key;
if (handler->key_to_lex_cstring(thd, &m_key, var->name, key))
return true;
int res= get_rcontext(thd)->set_variable_composite_field_by_key(thd,
m_offset,
key,
m_field_name,
&m_value);
*nextp= m_ip + 1;
return res;
}
void
sp_instr_set_composite_field_by_key::print(String *str)
{
sp_variable *var= m_ctx->find_variable(m_offset);
const LEX_CSTRING *prefix= m_rcontext_handler->get_name_prefix();
DBUG_ASSERT(var);
DBUG_ASSERT(dynamic_cast<const Type_handler_composite*>(var->type_handler()));
str->append(STRING_WITH_LEN("set "));
str->append(prefix);
str->append(&var->name);
str->append('@');
str->append_ulonglong(m_offset);
str->append('[');
m_key->print(str, enum_query_type(QT_ORDINARY |
QT_ITEM_ORIGINAL_FUNC_NULLIF));
str->append(']');
str->append('.');
str->append(&m_field_name);
str->append(' ');
m_value->print(str, enum_query_type(QT_ORDINARY |
QT_ITEM_ORIGINAL_FUNC_NULLIF));
}

View File

@ -749,35 +749,100 @@ public:
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,
So sp_instr_set_composite_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
Additionally, this class is used for assignments of associative arrays
by key:
DECLARE
TYPE t IS TABLE OF rec_t INDEX BY VARCHAR2(20);
arr t;
BEGIN
arr('key'):= rec_t(10, 20); -- This instruction
END;
*/
class sp_instr_set_composite_field_by_name : public sp_instr_set
{
// Prevent use of this
sp_instr_set_row_field_by_name(const sp_instr_set_row_field &);
void operator=(sp_instr_set_row_field_by_name &);
const LEX_CSTRING m_field_name;
using SELF= sp_instr_set_composite_field_by_name;
sp_instr_set_composite_field_by_name(const SELF &);
void operator=(SELF &);
LEX_CSTRING m_field_name;
Item* m_key;
public:
sp_instr_set_row_field_by_name(uint ip, sp_pcontext *ctx,
const Sp_rcontext_handler *rh,
uint offset, const LEX_CSTRING &field_name,
Item *val,
LEX *lex, bool lex_resp,
const LEX_CSTRING &value_query)
sp_instr_set_composite_field_by_name(uint ip, sp_pcontext *ctx,
const Sp_rcontext_handler *rh,
uint offset,
const LEX_CSTRING &field_name,
Item *val,
LEX *lex, bool lex_resp,
const LEX_CSTRING &value_query)
: sp_instr_set(ip, ctx, rh, offset, val, lex, lex_resp, value_query),
m_field_name(field_name)
m_field_name(field_name),
m_key(nullptr)
{}
sp_instr_set_composite_field_by_name(uint ip, sp_pcontext *ctx,
const sp_rcontext_addr &addr,
Item* key, Item *val,
LEX *lex, bool lex_resp,
const LEX_CSTRING &value_query)
: sp_instr_set(ip, ctx,
addr.rcontext_handler(),
addr.offset(), val, lex,
lex_resp, value_query),
m_key(key)
{}
virtual ~sp_instr_set_row_field_by_name() = default;
virtual ~sp_instr_set_composite_field_by_name() = default;
int exec_core(THD *thd, uint *nextp) override;
void print(String *str) override;
}; // class sp_instr_set_field_by_name : public sp_instr_set
}; // class sp_instr_set_composite_field_by_name : public sp_instr_set
/*
This class handles assignments of non scalar associative array's element
assignments.
DECLARE
TYPE t IS TABLE OF rec_t INDEX BY VARCHAR2(20);
arr t;
BEGIN
arr('key'):= rec_t(10, 20);
arr('key').field:= 30; -- This instruction
END;
*/
class sp_instr_set_composite_field_by_key : public sp_instr_set
{
using SELF= sp_instr_set_composite_field_by_key;
sp_instr_set_composite_field_by_key(const SELF &);
void operator=(SELF &);
Item* m_key;
const LEX_CSTRING m_field_name;
public:
sp_instr_set_composite_field_by_key(uint ip, sp_pcontext *ctx,
const sp_rcontext_addr &addr,
Item* key,
const LEX_CSTRING &field_name,
Item *val,
LEX *lex, bool lex_resp,
const LEX_CSTRING &value_query)
: sp_instr_set(ip, ctx,
addr.rcontext_handler(),
addr.offset(), val, lex,
lex_resp, value_query),
m_key(key),
m_field_name(field_name)
{}
virtual ~sp_instr_set_composite_field_by_key() = default;
int exec_core(THD *thd, uint *nextp) override;
void print(String *str) override;
}; // class sp_instr_set_composite_field_by_key : public sp_instr_set
/**

View File

@ -458,6 +458,21 @@ sp_type_def *sp_pcontext::find_type_def(const LEX_CSTRING &name,
}
bool sp_type_def_list::type_defs_add_composite2(THD *thd,
const Lex_ident_column &name,
const Type_handler *th,
Spvar_definition *key,
Spvar_definition *value)
{
auto p= new (thd->mem_root) sp_type_def_composite2(name, th, key, value);
if (p == nullptr)
return true;
return m_type_defs.append(p);
}
sp_condition_value *
sp_pcontext::find_declared_or_predefined_condition(THD *thd,
const LEX_CSTRING *name)

View File

@ -22,6 +22,7 @@
#include "field.h" // Create_field
#include "sql_array.h" // Dynamic_array
#include "sp_type_def.h"
#include "sp_rcontext_handler.h"
/// This class represents a stored program variable or a parameter
@ -343,30 +344,6 @@ public:
};
///////////////////////////////////////////////////////////////////////////
/// This class represents 'DECLARE RECORD' statement.
class sp_record : public Sql_alloc
{
public:
/// Name of the record.
Lex_ident_column name;
Row_definition_list *field;
public:
sp_record(const Lex_ident_column &name_arg, Row_definition_list *prmfield)
:Sql_alloc(),
name(name_arg),
field(prmfield)
{ }
bool eq_name(const LEX_CSTRING *str) const
{
return name.streq(*str);
}
};
///////////////////////////////////////////////////////////////////////////
/// The class represents parse-time context, which keeps track of declared
@ -565,6 +542,11 @@ public:
void declare_var_boundary(uint n)
{ m_pboundary= n; }
const sp_variable *get_pvariable(const sp_rcontext_addr &addr) const
{
return addr.rcontext_handler()->get_pvariable(this, addr.offset());
}
/////////////////////////////////////////////////////////////////////////
// CASE expressions.
/////////////////////////////////////////////////////////////////////////
@ -773,6 +755,23 @@ public:
return type_defs_add_record(thd, name, field);
}
/////////////////////////////////////////////////////////////////////////
// Composites, e.g. associative arrays.
/////////////////////////////////////////////////////////////////////////
bool type_defs_declare_composite2(THD *thd,
const Lex_ident_column &name,
const Type_handler *th,
Spvar_definition *key,
Spvar_definition *value)
{
if (unlikely(find_type_def(name, true)))
{
my_error(ER_SP_DUP_DECL, MYF(0), name.str);
return true;
}
return type_defs_add_composite2(thd, name, th, key, value);
}
private:
/// Constructor for a tree node.
/// @param prev the parent parsing context

View File

@ -33,6 +33,12 @@ Sp_rcontext_handler_package_body sp_rcontext_handler_package_body;
Sp_rcontext_handler_statement sp_rcontext_handler_statement;
const sp_variable *
Sp_rcontext_handler_local::get_pvariable(const sp_pcontext *pctx, uint i) const
{
return pctx->find_variable(i);
}
sp_cursor *Sp_rcontext_handler::get_open_cursor_or_error(THD *thd,
const sp_rcontext_ref &ref)
{
@ -49,6 +55,14 @@ sp_rcontext *Sp_rcontext_handler_local::get_rcontext(sp_rcontext *ctx) const
return ctx;
}
const sp_variable *
Sp_rcontext_handler_package_body::get_pvariable(const sp_pcontext *pctx, uint i)
const
{
DBUG_ASSERT(0);
return nullptr;
}
sp_rcontext *Sp_rcontext_handler_package_body::get_rcontext(sp_rcontext *ctx) const
{
return ctx->m_sp->m_parent->m_rcontext;
@ -663,7 +677,10 @@ int sp_rcontext::set_variable(THD *thd, uint idx, Item **value)
{
DBUG_ENTER("sp_rcontext::set_variable");
DBUG_ASSERT(value);
DBUG_RETURN(thd->sp_eval_expr(m_var_table->field[idx], value));
auto handler= get_variable(idx)->type_handler()->to_composite();
DBUG_RETURN(thd->sp_eval_expr(m_var_table->field[idx], value) ||
(handler && handler->finalize_for_set(get_variable(idx))));
}
@ -677,18 +694,6 @@ int sp_rcontext::set_variable_row_field(THD *thd, uint var_idx, uint field_idx,
}
int sp_rcontext::set_variable_row_field_by_name(THD *thd, uint var_idx,
const LEX_CSTRING &field_name,
Item **value)
{
DBUG_ENTER("sp_rcontext::set_variable_row_field_by_name");
uint field_idx;
if (find_row_field_by_name_or_error(&field_idx, var_idx, field_name))
DBUG_RETURN(1);
DBUG_RETURN(set_variable_row_field(thd, var_idx, field_idx, value));
}
int sp_rcontext::set_variable_row(THD *thd, uint var_idx, List<Item> &items)
{
DBUG_ENTER("sp_rcontext::set_variable_row");
@ -709,6 +714,64 @@ Virtual_tmp_table *sp_rcontext::virtual_tmp_table_for_row(uint var_idx)
}
int sp_rcontext::set_variable_composite_by_name(THD *thd, uint var_idx,
const LEX_CSTRING &name,
Item **value)
{
DBUG_ENTER("sp_rcontext::set_variable_composite_by_name");
DBUG_ASSERT(get_variable(var_idx)->type() == Item::FIELD_ITEM);
DBUG_ASSERT(get_variable(var_idx)->cmp_type() == ROW_RESULT);
DBUG_RETURN(set_variable_composite_by_name(thd, get_variable(var_idx),
name, value));
}
int sp_rcontext::set_variable_composite_by_name(THD *thd, Item_field *composite,
const LEX_CSTRING &name,
Item **value)
{
DBUG_ENTER("sp_rcontext::set_variable_composite_by_name");
auto handler= composite->type_handler()->to_composite();
DBUG_ASSERT(handler);
auto elem= handler->get_or_create_item(thd, composite, name);
if (!elem)
DBUG_RETURN(1);
DBUG_RETURN(thd->sp_eval_expr(elem->field, value) ||
handler->finalize_for_set(elem));
}
int
sp_rcontext::set_variable_composite_field_by_key(THD *thd,
uint var_idx,
const LEX_CSTRING &elem_name,
const LEX_CSTRING &field_name,
Item **value)
{
DBUG_ENTER("sp_rcontext::set_variable_composite_field_by_key");
DBUG_ASSERT(value);
DBUG_ASSERT(get_variable(var_idx)->type() == Item::FIELD_ITEM);
auto composite= get_variable(var_idx);
auto handler= composite->type_handler()->to_composite();
DBUG_ASSERT(handler);
auto elem= handler->get_item(thd, composite, elem_name);
if (!elem)
DBUG_RETURN(1);
handler->prepare_for_set(elem);
DBUG_RETURN(set_variable_composite_by_name(thd, elem, field_name, value) ||
handler->finalize_for_set(elem));
}
bool sp_rcontext::find_row_field_by_name_or_error(uint *field_idx,
uint var_idx,
const LEX_CSTRING &field_name)

View File

@ -209,11 +209,18 @@ public:
int set_variable(THD *thd, uint var_idx, Item **value);
int set_variable_row_field(THD *thd, uint var_idx, uint field_idx,
Item **value);
int set_variable_row_field_by_name(THD *thd, uint var_idx,
const LEX_CSTRING &field_name,
Item **value);
int set_variable_row(THD *thd, uint var_idx, List<Item> &items);
int set_variable_composite_field_by_key(THD *thd,
uint var_idx,
const LEX_CSTRING &elem_name,
const LEX_CSTRING &field_name,
Item **value);
int set_variable_composite_by_name(THD *thd, uint var_idx,
const LEX_CSTRING &name,
Item **value);
int set_variable_composite_by_name(THD *thd, Item_field *composite,
const LEX_CSTRING &name, Item **value);
int set_parameter(THD *thd, uint var_idx, Item **value)
{
DBUG_ASSERT(var_idx < argument_count());

View File

@ -18,6 +18,7 @@
class sp_rcontext;
class sp_pcontext;
class sp_cursor;
/**
@ -73,6 +74,11 @@ public:
name with a package body variable.
*/
virtual const LEX_CSTRING *get_name_prefix() const= 0;
// Find a parse time SP variable
virtual const sp_variable *get_pvariable(const sp_pcontext *pctx,
uint offset) const= 0;
/**
At execution time THD->spcont points to the run-time context (sp_rcontext)
of the currently executed routine.
@ -95,6 +101,8 @@ class Sp_rcontext_handler_local final :public Sp_rcontext_handler
{
public:
const LEX_CSTRING *get_name_prefix() const override;
const sp_variable *get_pvariable(const sp_pcontext *pctx,
uint offset) const override;
sp_rcontext *get_rcontext(sp_rcontext *ctx) const override;
Item_field *get_variable(THD *thd, uint offset) const override;
sp_cursor *get_cursor(THD *thd, uint offset) const override;
@ -111,6 +119,8 @@ class Sp_rcontext_handler_package_body final :public Sp_rcontext_handler
{
public:
const LEX_CSTRING *get_name_prefix() const override;
const sp_variable *get_pvariable(const sp_pcontext *pctx,
uint offset) const override;
sp_rcontext *get_rcontext(sp_rcontext *ctx) const override;
Item_field *get_variable(THD *thd, uint offset) const override;
sp_cursor *get_cursor(THD *thd, uint offset) const override
@ -145,6 +155,12 @@ public:
DBUG_ASSERT(0); // There are no session wide SP variables yet.
return nullptr;
}
const sp_variable *get_pvariable(const sp_pcontext *pctx,
uint offset) const override
{
DBUG_ASSERT(0);
return nullptr;
}
sp_cursor *get_cursor(THD *thd, uint offset) const override;
sp_cursor *get_cursor_by_ref(THD *thd, const sp_rcontext_addr &ref,
bool for_open) const override;

View File

@ -72,6 +72,25 @@ public:
};
/*
This class represents 'DECLARE TYPE .. TABLE OF' statement.
*/
class sp_type_def_composite2 : public sp_type_def
{
public:
Spvar_definition *m_def[2];
public:
sp_type_def_composite2(const Lex_ident_column &name_arg,
const Type_handler *th,
Spvar_definition *key_def_arg,
Spvar_definition *value_def_arg)
:sp_type_def(name_arg, th),
m_def{key_def_arg, value_def_arg}
{ }
};
class sp_type_def_list
{
protected:
@ -92,6 +111,11 @@ public:
bool type_defs_add_record(THD *thd, const Lex_ident_column &name,
Row_definition_list *field);
bool type_defs_add_composite2(THD *thd,
const Lex_ident_column &name,
const Type_handler *th,
Spvar_definition *key,
Spvar_definition *value);
};

View File

@ -8949,6 +8949,51 @@ bool Qualified_column_ident::append_to(THD *thd, String *str) const
}
Qualified_ident::Qualified_ident(THD *thd, const Lex_ident_cli_st &a)
:m_cli_pos(a.pos()),
m_spvar(nullptr),
m_defined_parts(1)
{
m_parts[0]= Lex_ident_sys(thd, &a);
m_parts[1]= m_parts[2]= Lex_ident_sys();
}
Qualified_ident::Qualified_ident(THD *thd, const Lex_ident_cli_st &a,
const Lex_ident_sys_st &b)
:m_cli_pos(a.pos()),
m_spvar(nullptr),
m_defined_parts(2)
{
m_parts[0]= Lex_ident_sys(thd, &a);
m_parts[1]= b;
m_parts[2]= Lex_ident_sys();
}
Qualified_ident::Qualified_ident(THD *thd, const Lex_ident_cli_st &a,
const Lex_ident_sys_st &b,
const Lex_ident_sys_st &c)
:m_cli_pos(a.pos()),
m_spvar(nullptr),
m_defined_parts(3)
{
m_parts[0]= Lex_ident_sys(thd, &a);
m_parts[1]= b;
m_parts[2]= c;
}
Qualified_ident::Qualified_ident(const Lex_ident_sys_st &a)
:m_cli_pos(nullptr),
m_spvar(nullptr),
m_defined_parts(1)
{
m_parts[0]= a;
m_parts[1]= m_parts[2]= Lex_ident_sys();
}
#endif /* !defined(MYSQL_CLIENT) */

View File

@ -7401,6 +7401,60 @@ public:
};
class Qualified_ident: public Sql_alloc
{
protected:
const char *m_cli_pos;
Lex_ident_sys_st m_parts[3];
sp_variable *m_spvar;
uint m_defined_parts;
public:
Qualified_ident(THD *thd, const Lex_ident_cli_st &a);
Qualified_ident(THD *thd,
const Lex_ident_cli_st &a,
const Lex_ident_sys_st &b);
Qualified_ident(THD *thd,
const Lex_ident_cli_st &a,
const Lex_ident_sys_st &b,
const Lex_ident_sys_st &c);
Qualified_ident(const Lex_ident_sys_st &a);
/*
Returns the position of the first character of the identifier in
the client string.
*/
const char *pos() const
{
return m_cli_pos;
}
sp_variable *spvar() const
{
return m_spvar;
}
const Lex_ident_sys_st &part(uint i) const
{
DBUG_ASSERT(i < array_elements(m_parts));
return m_parts[i];
}
uint defined_parts() const
{
return m_defined_parts;
}
void set_cli_pos(const char *pos)
{
m_cli_pos= pos;
}
void set_spvar(sp_variable *spvar)
{
m_spvar= spvar;
}
};
// this is needed for user_vars hash
class user_var_entry: public Type_handler_hybrid_field_type
{

View File

@ -6709,7 +6709,7 @@ bool LEX::sf_return_fill_definition(const Lex_field_type_st &def)
bool LEX::sf_return_fill_definition_row(Row_definition_list *def)
{
sphead->m_return_field_def.set_row_field_definitions(def);
sphead->m_return_field_def.set_row_field_definitions(&type_handler_row, def);
return sphead->fill_spvar_definition(thd, &sphead->m_return_field_def) ||
sphead->row_fill_field_definitions(thd, def);
}
@ -6914,7 +6914,7 @@ LEX::sp_variable_declarations_copy_type_finalize_internal(THD *thd, int nvars,
if (fields)
{
DBUG_ASSERT(ref.type_handler() == &type_handler_row);
spvar->field_def.set_row_field_definitions(fields);
spvar->field_def.set_row_field_definitions(&type_handler_row, fields);
}
spvar->field_def.field_name= spvar->name;
}
@ -6959,6 +6959,41 @@ bool LEX::sp_variable_declarations_finalize(THD *thd, int nvars,
}
/*
Make instructions for:
var('key') := expr;
var('key').member := expr;
*/
bool LEX::sp_set_assign_lvalue_function(THD *thd,
const Qualified_ident *ident,
List<Item> *args,
const Lex_ident_sys_st &field_name,
Item *item, const LEX_CSTRING &expr_str)
{
DBUG_ASSERT(ident);
DBUG_ASSERT(item);
sp_pcontext *ctx;
const Sp_rcontext_handler *rh;
sp_variable *spv= find_variable(&ident->part(0), &ctx, &rh);
if (!spv->type_handler()->has_functors())
{
spv->type_handler()->raise_bad_data_type_for_functor(*ident);
return true;
}
const sp_rcontext_addr addr(rh, spv->offset);
item= sphead->adjust_assignment_source(thd, item, nullptr);
sp_instr *i= spv->type_handler()->
create_instr_set_assign_functor(thd, this, *ident, addr,
args, field_name,
item, expr_str);
return !i || sphead->add_instr(i);
}
bool LEX::sp_variable_declarations_row_finalize(THD *thd, int nvars,
Row_definition_list *row,
Item *dflt_value_item,
@ -8717,7 +8752,45 @@ Item_splocal *LEX::create_item_spvar_row_field(THD *thd,
}
my_var *LEX::create_outvar(THD *thd,const Lex_ident_sys_st &name)
/*
Generate an Item for expressions of these types:
1. varname(args)
2. varname(args).member
@param thd - Current thd
@param name - The variable name. It's known to be an existing variable.
@param args - The list of arguments
@param member - The member name. If member.is_null() then it's
an expression of the type #1, otherwise of the type #2.
@param name_cli - The query fragment for the entire expression,
starting from 'ident' and ending after ')' or 'field'.
*/
Item_splocal *
LEX::create_item_functor(THD *thd,
const Lex_ident_sys &varname, List<Item> *args,
const Lex_ident_sys &member,
const Lex_ident_cli_st &name_cli)
{
DBUG_ASSERT(!varname.is_null());
const Sp_rcontext_handler *rh;
sp_variable *spv= find_variable(&varname, &rh);
DBUG_ASSERT(spv);
DBUG_ASSERT(spv->type_handler()->has_functors());
const sp_rcontext_addr addr(rh, spv->offset);
Item_splocal *item= spv->type_handler()->create_item_functor(thd, varname,
addr, args,
member,
name_cli);
#ifdef DBUG_ASSERT_EXISTS
if (item)
item->m_sp= sphead;
#endif
return item;
}
my_var *LEX::create_outvar(THD *thd, const Lex_ident_sys_st &name)
{
const Sp_rcontext_handler *rh;
sp_variable *spv;
@ -8753,6 +8826,55 @@ my_var *LEX::create_outvar(THD *thd,
}
/*
In a statement like:
SELECT val INTO spvar(key); -- if field_name.length == 0 or
SELECT val INTO spvar(key).field; -- if field_name.length > 0
validate the INTO expression and optionally create a my_var instance.
spvar is a structured variable, such as an assoc array.
We don't support other kinds of lvalue functions yet.
@param thd - Current thd
@param name - The SP variable name
@param key - The argument (e.g. an assoc array key value)
@param field_name - The field name to be used in the expression
spvar(key).field .
@returns - The pointer to a new my_var created or nullptr.
* nullptr if the INTO expression is not a correct
lvalue expression.
* nullptr if LEX::result is NULL.
* nullptr if EOM happened (e.g. during "new").
* A pointer to a new my_var instance if
LEX::result is not NULL and the lvalue expression
spvar(key) is correct.
*/
my_var *LEX::create_outvar_lvalue_function(THD *thd,
const Lex_ident_sys_st &name,
Item *key,
const Lex_ident_sys &opt_field_name)
{
DBUG_ASSERT(key);
// So far we support only data type functors as lvalue functions.
const Sp_rcontext_handler *rh;
sp_variable *t;
if (unlikely(!(t= find_variable(&name, &rh))))
{
my_error(ER_SP_UNDECLARED_VAR, MYF(0), name.str);
return NULL;
}
const sp_rcontext_addr addr(rh, t->offset);
my_var *var= t->type_handler()->make_outvar_lvalue_functor(thd, name, key,
opt_field_name,
sphead, addr,
!result);
DBUG_ASSERT(var || thd->is_error() || !result);
return var;
}
Item *LEX::create_item_func_nextval(THD *thd, Table_ident *table_ident)
{
TABLE_LIST *table;
@ -8832,11 +8954,22 @@ Item *LEX::create_item_ident(THD *thd,
Lex_ident_sys a(thd, ca), b(thd, cb);
if (a.is_null() || b.is_null())
return NULL; // OEM
if ((spv= find_variable(&a, &rh)) &&
(spv->field_def.is_row() ||
if ((spv= find_variable(&a, &rh)))
{
if (spv->field_def.is_row() ||
spv->field_def.is_table_rowtype_ref() ||
spv->field_def.is_cursor_rowtype_ref()))
return create_item_spvar_row_field(thd, rh, &a, &b, spv, start, end);
spv->field_def.is_cursor_rowtype_ref())
return create_item_spvar_row_field(thd, rh, &a, &b, spv, start, end);
if (spv->type_handler()->has_methods())
{
const Lex_ident_sys sys_a(thd, ca), sys_b(thd, cb);
const Lex_ident_cli query_fragment(start, end - start);
if (sys_a.is_null() || sys_b.is_null())
return nullptr; // EOM
return spv->type_handler()->create_item_method(thd, sys_a, sys_b,
NULL, query_fragment);
}
}
if ((thd->variables.sql_mode & MODE_ORACLE) && b.length == 7)
{
@ -9153,6 +9286,22 @@ bool LEX::set_variable(const Lex_ident_sys_st *name1,
}
bool LEX::set_variable(const Qualified_ident *ident,
Item *item, const LEX_CSTRING &expr_str)
{
if (unlikely(ident->part(2).length))
{
thd->parse_error(ER_SYNTAX_ERROR, ident->pos());
return true;
}
if (ident->part(1).length)
return set_variable(&ident->part(0), &ident->part(1), item, expr_str);
return set_variable(&ident->part(0), item, expr_str);
}
bool LEX::set_default_system_variable(enum_var_type var_type,
const Lex_ident_sys_st *name,
Item *val)
@ -9911,7 +10060,7 @@ bool LEX::call_statement_start(THD *thd,
DBUG_ASSERT(db->str);
Identifier_chain2 q_pkg_proc(*pkg, *proc);
sp_name *spname;
value_list.empty();
sql_command= SQLCOM_CALL;
const Lex_ident_db_normalized dbn= thd->to_ident_db_normalized_with_error(*db);
@ -9937,6 +10086,61 @@ bool LEX::call_statement_start(THD *thd,
}
bool LEX::call_statement_start(THD *thd, const Qualified_ident *ident)
{
if (ident->part(2).length)
return call_statement_start(thd, &ident->part(0),
&ident->part(1), &ident->part(2));
else if (ident->part(1).length)
return call_statement_start(thd, &ident->part(0), &ident->part(1));
return call_statement_start(thd, &ident->part(0));
}
bool LEX::call_statement_start_or_lvalue_assign(THD *thd,
Qualified_ident *ident)
{
sp_variable *spv;
if (spcont &&
(spv= spcont->find_variable(&ident->part(0), false)) &&
(likely(spv->field_def.type_handler()->has_methods())))
{
ident->set_spvar(spv);
thd->where= THD_WHERE::USE_WHERE_STRING;
thd->where_str= "SPVAR LVALUE METHOD";
return false;
}
// Direct procedure call (without the CALL keyword)
if (unlikely(call_statement_start(thd, ident)))
return true;
thd->where= THD_WHERE::USE_WHERE_STRING;
thd->where_str= "CALL";
return false;
}
bool LEX::assoc_assign_start(THD *thd, Qualified_ident *ident)
{
if (unlikely(ident->spvar() == NULL))
{
thd->parse_error();
return true;
}
LEX *lex= this;
lex->set_stmt_init();
if (sp_create_assignment_lex(thd, ident->pos()))
return true;
return false;
}
sp_package *LEX::get_sp_package() const
{
return sphead ? sphead->get_package() : NULL;
@ -10304,6 +10508,36 @@ Item *LEX::make_item_func_call_generic(THD *thd,
}
Item *LEX::make_item_func_or_method_call(THD *thd,
const Lex_ident_cli_st &ca,
const Lex_ident_cli_st &cb,
List<Item> *args,
const Lex_ident_cli_st &query_fragment)
{
DBUG_ASSERT(!thd->is_error());
const Lex_ident_sys sys_a(thd, &ca), sys_b(thd, &cb);
if (sys_a.is_null() || sys_b.is_null())
return nullptr; // EOM
sp_variable *spv;
if (spcont &&
(spv= spcont->find_variable(&sys_a, false)) &&
spv->type_handler()->has_methods())
{
if (Item *item= spv->type_handler()->create_item_method(thd,
sys_a, sys_b, args,
query_fragment))
{
item->set_name(thd, query_fragment, thd->charset());
return item;
}
DBUG_ASSERT(thd->is_error());
return nullptr;
}
return make_item_func_call_generic(thd, &ca, &cb, args);
}
Item *LEX::make_item_func_call_generic(THD *thd,
const Lex_ident_db &db,
const Lex_ident_routine &name,
@ -12454,6 +12688,13 @@ bool LEX::stmt_alter_procedure_start(sp_name *name)
Spvar_definition *LEX::row_field_name(THD *thd, const Lex_ident_sys_st &name)
{
return init_spvar_definition(thd, name);
}
Spvar_definition *LEX::init_spvar_definition(THD *thd,
const Lex_ident_sys_st &name)
{
Spvar_definition *res;
if (unlikely(check_string_char_length(&name, 0, NAME_CHAR_LEN,
@ -12548,6 +12789,41 @@ bool LEX::set_cast_type_udt(Lex_cast_type_st *type,
}
bool LEX::set_field_type_composite(Lex_field_type_st *type,
const LEX_CSTRING &name,
bool with_collection,
bool *is_composite)
{
DBUG_ASSERT(type);
DBUG_ASSERT(is_composite);
sp_type_def *composite= NULL;
*is_composite= false;
if (spcont)
{
if ((composite= spcont->find_type_def(name, false)))
{
if (with_collection ||
likely(composite->type_handler() == &type_handler_row))
{
type->set(composite->type_handler(), NULL);
last_field->set_attr_const_void_ptr(0, composite);
}
else
{
my_error(ER_NOT_SUPPORTED_YET, MYF(0), "nested associative arrays");
return true;
}
*is_composite= true;
}
}
return false;
}
bool sp_expr_lex::sp_repeat_loop_finalize(THD *thd)
{
uint ip= sphead->instructions();

View File

@ -165,6 +165,7 @@ class sp_variable;
class sp_fetch_target;
class sp_expr_lex;
class sp_assignment_lex;
class sp_type_def;
class partition_info;
class Event_parse_data;
class set_var_base;
@ -3914,6 +3915,11 @@ public:
const Lex_ident_sys_st *db,
const Lex_ident_sys_st *pkg,
const Lex_ident_sys_st *proc);
bool call_statement_start(THD *thd, const Qualified_ident *ident);
bool call_statement_start_or_lvalue_assign(THD *thd,
Qualified_ident *ident);
bool assoc_assign_start(THD *thd, Qualified_ident *ident);
sp_variable *find_variable(const LEX_CSTRING *name,
sp_pcontext **ctx,
const Sp_rcontext_handler **rh) const;
@ -3937,6 +3943,8 @@ public:
bool set_variable(const Lex_ident_sys_st *name1,
const Lex_ident_sys_st *name2, Item *item,
const LEX_CSTRING &expr_str);
bool set_variable(const Qualified_ident *ident, Item *item,
const LEX_CSTRING &expr_str);
void sp_variable_declarations_init(THD *thd, int nvars);
bool sp_variable_declarations_finalize(THD *thd, int nvars,
const Column_definition *cdef,
@ -3944,10 +3952,11 @@ public:
const LEX_CSTRING &expr_str);
bool sp_variable_declarations_set_default(THD *thd, int nvars, Item *def,
const LEX_CSTRING &expr_str);
bool sp_variable_declarations_rec_finalize(THD *thd, int nvars,
Row_definition_list *src_row,
Item *def,
const LEX_CSTRING &expr_str);
bool sp_set_assign_lvalue_function(THD *thd,
const Qualified_ident *ident,
List<Item> *params,
const Lex_ident_sys_st &field_name,
Item *item, const LEX_CSTRING &expr_str);
bool sp_variable_declarations_row_finalize(THD *thd, int nvars,
Row_definition_list *row,
Item *def,
@ -4177,6 +4186,17 @@ public:
return nullptr;
}
/*
Create items of this kind:
SELECT name(args); -- e.g. spvar_assoc_array('key')
SELECT name(args).member; -- e.g. spvar_assoc_array('key').member
*/
Item_splocal *create_item_functor(THD *thd,
const Lex_ident_sys &name,
List<Item> *args,
const Lex_ident_sys &member,
const Lex_ident_cli_st &query_fragment);
/*
Create an item for "NEXT VALUE FOR sequence_name"
*/
@ -4274,6 +4294,12 @@ public:
Item *make_item_func_call_native_or_parse_error(THD *thd,
Lex_ident_cli_st &name,
List<Item> *args);
Item *make_item_func_or_method_call(THD *thd,
const Lex_ident_cli_st &ident0,
const Lex_ident_cli_st &ident1,
List<Item> *args,
const Lex_ident_cli_st &query_fragment);
my_var *create_outvar(THD *thd, const Lex_ident_sys_st &name);
/*
@ -4287,6 +4313,10 @@ public:
const Lex_ident_sys_st &var_name,
const Lex_ident_sys_st &field_name);
my_var *create_outvar_lvalue_function(THD *thd, const Lex_ident_sys_st &name,
Item *arg,
const Lex_ident_sys &opt_field_name);
bool is_trigger_new_or_old_reference(const LEX_CSTRING *name) const;
Item *create_and_link_Item_trigger_field(THD *thd, const LEX_CSTRING *name,
@ -4949,6 +4979,8 @@ public:
sp_condition_value *stmt_signal_value(const Lex_ident_sys_st &ident);
Spvar_definition *row_field_name(THD *thd, const Lex_ident_sys_st &name);
Spvar_definition *init_spvar_definition(THD *thd,
const Lex_ident_sys_st &name);
bool set_field_type_udt(Lex_field_type_st *type,
const LEX_CSTRING &name,
@ -4956,6 +4988,11 @@ public:
bool set_cast_type_udt(Lex_cast_type_st *type,
const LEX_CSTRING &name);
bool set_field_type_composite(Lex_field_type_st *type,
const LEX_CSTRING &name,
bool with_collection,
bool *is_composite);
bool map_data_type(const Lex_ident_sys_st &schema,
Lex_field_type_st *type) const;

View File

@ -800,6 +800,15 @@ void Binary_string::qs_append(ulonglong i)
}
void Binary_string::qs_append_int64(longlong i)
{
char *buff= Ptr + str_length;
char *end= longlong10_to_str(i, buff, -10);
ASSERT_LENGTH((size_t) (end-buff));
str_length+= (uint32) (end-buff);
}
bool Binary_string::copy_printable_hhhh(CHARSET_INFO *to_cs,
CHARSET_INFO *from_cs,
const char *from,

View File

@ -392,6 +392,12 @@ public:
int4store(Ptr + str_length, n);
str_length += 4;
}
void q_append_int64(const longlong n)
{
ASSERT_LENGTH(8);
int8store(Ptr + str_length, n);
str_length += 8;
}
void q_append(double d)
{
ASSERT_LENGTH(8);
@ -475,6 +481,8 @@ public:
str_length+= (uint32) (end-buff);
}
void qs_append_int64(longlong i);
/* Mark variable thread specific it it's not allocated already */
inline void set_thread_specific()
{

View File

@ -123,8 +123,6 @@ bool DTCollation::merge_collation(Sql_used *used,
}
Named_type_handler<Type_handler_row> type_handler_row("row");
Named_type_handler<Type_handler_null> type_handler_null("null");
Named_type_handler<Type_handler_bool> type_handler_bool("boolean");
@ -217,55 +215,6 @@ bool Type_handler::is_traditional_scalar_type() const
}
class Type_collection_row: public Type_collection
{
public:
bool init(Type_handler_data *data) override
{
return false;
}
const Type_handler *aggregate_for_result(const Type_handler *a,
const Type_handler *b)
const override
{
return NULL;
}
const Type_handler *aggregate_for_comparison(const Type_handler *a,
const Type_handler *b)
const override
{
/*
Allowed combinations:
ROW+ROW, NULL+ROW, ROW+NULL
*/
DBUG_ASSERT(a == &type_handler_row || a == &type_handler_null);
DBUG_ASSERT(b == &type_handler_row || b == &type_handler_null);
DBUG_ASSERT(a == &type_handler_row || b == &type_handler_row);
return &type_handler_row;
}
const Type_handler *aggregate_for_min_max(const Type_handler *a,
const Type_handler *b)
const override
{
return NULL;
}
const Type_handler *aggregate_for_num_op(const Type_handler *a,
const Type_handler *b)
const override
{
return NULL;
}
};
static Type_collection_row type_collection_row;
const Type_collection *Type_handler_row::type_collection() const
{
return &type_collection_row;
}
bool Type_handler_data::init()
{
return type_collection_geometry.init(this);
@ -1771,11 +1720,6 @@ const Type_handler *Type_handler_timestamp_common::type_handler_for_comparison()
}
const Type_handler *Type_handler_row::type_handler_for_comparison() const
{
return &type_handler_row;
}
/***************************************************************************/
const Type_handler *
@ -3128,19 +3072,6 @@ bool Type_handler_null::
return false;
}
bool Type_handler_row::
Column_definition_prepare_stage1(THD *thd,
MEM_ROOT *mem_root,
Column_definition *def,
column_definition_type_t type,
const Column_derived_attributes
*derived_attr)
const
{
def->charset= &my_charset_bin;
def->create_length_to_internal_length_null();
return false;
}
bool Type_handler_temporal_result::
Column_definition_prepare_stage1(THD *thd,
@ -3446,24 +3377,6 @@ bool Type_handler_bit::
}
/*************************************************************************/
bool Type_handler_row::Spvar_definition_with_complex_data_types(
Spvar_definition *def) const
{
if (def->row_field_definitions())
{
List_iterator<Spvar_definition> it(*(def->row_field_definitions()));
Spvar_definition *member;
while ((member= it++))
{
if (member->type_handler()->is_complex())
return true;
}
}
return false;
}
/*************************************************************************/
bool Type_handler::Key_part_spec_init_primary(Key_part_spec *part,
const Column_definition &def,
@ -3699,6 +3612,23 @@ my_var *Type_handler::make_outvar_field(THD *thd,
}
/*
SELECT 1 INTO spvar(arg);
SELECT 1 INTO spvar(arg).field;
*/
my_var *Type_handler::make_outvar_lvalue_functor(THD *thd,
const Lex_ident_sys_st &name,
Item *arg,
const Lex_ident_sys &opt_field,
sp_head *sphead,
const sp_rcontext_addr &addr,
bool validate_only) const
{
raise_bad_data_type_for_functor(Qualified_ident(name));
return nullptr;
}
/*************************************************************************/
Field *Type_handler::make_and_init_table_field(MEM_ROOT *root,
const LEX_CSTRING *name,
@ -4393,13 +4323,6 @@ Type_handler_bit::Bit_decimal_notation_int_digits_by_nbits(uint nbits)
/*************************************************************************/
void Type_handler_row::Item_update_null_value(Item *item) const
{
DBUG_ASSERT(0);
item->null_value= true;
}
void Type_handler_time_common::Item_update_null_value(Item *item) const
{
MYSQL_TIME ltime;
@ -4514,12 +4437,6 @@ int Type_handler_bool::Item_save_in_field(Item *item, Field *field,
/***********************************************************************/
bool Type_handler_row::
set_comparator_func(THD *thd, Arg_comparator *cmp) const
{
return cmp->set_cmp_func_row(thd);
}
bool Type_handler_int_result::
set_comparator_func(THD *thd, Arg_comparator *cmp) const
{
@ -4660,12 +4577,6 @@ bool Type_handler_numeric::
/*************************************************************************/
Item_cache *
Type_handler_row::Item_get_cache(THD *thd, const Item *item) const
{
return new (thd->mem_root) Item_cache_row(thd);
}
Item_cache *
Type_handler_int_result::Item_get_cache(THD *thd, const Item *item) const
{
@ -5379,6 +5290,32 @@ Type_handler::Item_func_hybrid_field_type_val_ref(THD *thd,
}
void Type_handler::
raise_bad_data_type_for_functor(const Qualified_ident &ident,
const Lex_ident_sys &field) const
{
DBUG_ASSERT(ident.defined_parts() > 0 && ident.defined_parts() <= 3);
char param[MYSQL_ERRMSG_SIZE];
uint used= 0;
for (uint i= 0; i < ident.defined_parts() && used < sizeof(param); i++)
{
used+= my_snprintf(param + used,
sizeof(param) - used,
"%sQ.",
ident.part(i).str);
}
used-= 1;
if (!field.str)
my_snprintf(param + used, sizeof(param) - used, "(..)");
else
my_snprintf(param + used, sizeof(param) - used, "(..).%sQ", field.str);
my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0),
name().ptr(), param);
}
/************************************************************************/
void Type_handler_decimal_result::Item_get_date(THD *thd, Item *item,
Temporal::Warn *warn,
@ -5927,14 +5864,6 @@ bool Type_handler_string_result::
}
longlong Type_handler_row::
Item_func_between_val_int(Item_func_between *func) const
{
DBUG_ASSERT(0);
func->null_value= true;
return 0;
}
longlong Type_handler_string_result::
Item_func_between_val_int(Item_func_between *func) const
{
@ -6004,12 +5933,6 @@ cmp_item *Type_handler_string_result::make_cmp_item(THD *thd,
return new (thd->mem_root) cmp_item_sort_string(cs);
}
cmp_item *Type_handler_row::make_cmp_item(THD *thd,
CHARSET_INFO *cs) const
{
return new (thd->mem_root) cmp_item_row;
}
cmp_item *Type_handler_time_common::make_cmp_item(THD *thd,
CHARSET_INFO *cs) const
{
@ -6096,13 +6019,6 @@ Type_handler_timestamp_common::make_in_vector(THD *thd,
}
in_vector *Type_handler_row::make_in_vector(THD *thd,
const Item_func_in *func,
uint nargs) const
{
return new (thd->mem_root) in_row(thd, nargs, 0);
}
/***************************************************************************/
bool Type_handler_string_result::
@ -6173,14 +6089,6 @@ bool Type_handler_temporal_result::
}
bool Type_handler_row::Item_func_in_fix_comparator_compatible_types(THD *thd,
Item_func_in *func) const
{
return func->compatible_types_row_bisection_possible() ?
func->fix_for_row_comparison_using_bisection(thd) :
func->fix_for_row_comparison_using_cmp_items(thd);
}
/***************************************************************************/
String *Type_handler_string_result::
@ -6449,32 +6357,6 @@ bool Type_handler_timestamp_common::
/***************************************************************************/
/**
Get a string representation of the Item value.
See sql_type.h for details.
*/
String *Type_handler_row::
print_item_value(THD *thd, Item *item, String *str) const
{
CHARSET_INFO *cs= thd->variables.character_set_client;
StringBuffer<STRING_BUFFER_USUAL_SIZE> val(cs);
str->append(STRING_WITH_LEN("ROW("));
for (uint i= 0 ; i < item->cols(); i++)
{
if (i > 0)
str->append(',');
Item *elem= item->element_index(i);
String *tmp= elem->type_handler()->print_item_value(thd, elem, &val);
if (tmp)
str->append(*tmp);
else
str->append(NULL_clex_str);
}
str->append(')');
return str;
}
/**
Get a string representation of the Item value,
using the character string format with its charset and collation, e.g.
@ -6513,6 +6395,18 @@ String *Type_handler_numeric::
}
String *Type_handler_bool::
print_item_value(THD *thd, Item *item, String *str) const
{
DBUG_ASSERT(item->fixed());
bool b=item->val_bool();
if (item->null_value)
return 0;
str->set_int(b, item->unsigned_flag, item->collation.collation);
return str;
}
String *Type_handler::
print_item_value_temporal(THD *thd, Item *item, String *str,
const Name &type_name, String *buf) const
@ -6567,14 +6461,6 @@ String *Type_handler_timestamp_common::
/***************************************************************************/
bool Type_handler_row::
Item_func_round_fix_length_and_dec(Item_func_round *item) const
{
DBUG_ASSERT(0);
return false;
}
bool Type_handler_int_result::
Item_func_round_fix_length_and_dec(Item_func_round *item) const
{
@ -6685,14 +6571,6 @@ bool Type_handler_string_result::
/***************************************************************************/
bool Type_handler_row::
Item_func_int_val_fix_length_and_dec(Item_func_int_val *item) const
{
DBUG_ASSERT(0);
return false;
}
bool Type_handler_int_result::
Item_func_int_val_fix_length_and_dec(Item_func_int_val *item) const
{
@ -6797,14 +6675,6 @@ bool Type_handler_string_result::
/***************************************************************************/
bool Type_handler_row::
Item_func_abs_fix_length_and_dec(Item_func_abs *item) const
{
DBUG_ASSERT(0);
return false;
}
bool Type_handler_int_result::
Item_func_abs_fix_length_and_dec(Item_func_abs *item) const
{
@ -6855,14 +6725,6 @@ bool Type_handler_string_result::
/***************************************************************************/
bool Type_handler_row::
Item_func_neg_fix_length_and_dec(Item_func_neg *item) const
{
DBUG_ASSERT(0);
return false;
}
bool Type_handler_int_result::
Item_func_neg_fix_length_and_dec(Item_func_neg *item) const
{
@ -7070,14 +6932,6 @@ bool Type_handler::
/***************************************************************************/
bool Type_handler_row::
Item_func_plus_fix_length_and_dec(Item_func_plus *item) const
{
DBUG_ASSERT(0);
return true;
}
bool Type_handler_int_result::
Item_func_plus_fix_length_and_dec(Item_func_plus *item) const
{
@ -7119,14 +6973,6 @@ bool Type_handler_string_result::
/***************************************************************************/
bool Type_handler_row::
Item_func_minus_fix_length_and_dec(Item_func_minus *item) const
{
DBUG_ASSERT(0);
return true;
}
bool Type_handler_int_result::
Item_func_minus_fix_length_and_dec(Item_func_minus *item) const
{
@ -7168,14 +7014,6 @@ bool Type_handler_string_result::
/***************************************************************************/
bool Type_handler_row::
Item_func_mul_fix_length_and_dec(Item_func_mul *item) const
{
DBUG_ASSERT(0);
return true;
}
bool Type_handler_int_result::
Item_func_mul_fix_length_and_dec(Item_func_mul *item) const
{
@ -7217,14 +7055,6 @@ bool Type_handler_string_result::
/***************************************************************************/
bool Type_handler_row::
Item_func_div_fix_length_and_dec(Item_func_div *item) const
{
DBUG_ASSERT(0);
return true;
}
bool Type_handler_int_result::
Item_func_div_fix_length_and_dec(Item_func_div *item) const
{
@ -7266,14 +7096,6 @@ bool Type_handler_string_result::
/***************************************************************************/
bool Type_handler_row::
Item_func_mod_fix_length_and_dec(Item_func_mod *item) const
{
DBUG_ASSERT(0);
return true;
}
bool Type_handler_int_result::
Item_func_mod_fix_length_and_dec(Item_func_mod *item) const
{
@ -7588,15 +7410,6 @@ bool Type_handler_null::
}
bool Type_handler_row::
Item_save_in_value(THD *thd, Item *item, st_value *value) const
{
DBUG_ASSERT(0);
value->m_type= DYN_COL_NULL;
return true;
}
bool Type_handler_int_result::
Item_save_in_value(THD *thd, Item *item, st_value *value) const
{
@ -7657,18 +7470,6 @@ bool Type_handler_time_common::
/***************************************************************************/
bool Type_handler_row::
Item_param_set_from_value(THD *thd,
Item_param *param,
const Type_all_attributes *attr,
const st_value *val) const
{
DBUG_ASSERT(0);
param->set_null();
return true;
}
bool Type_handler_real_result::
Item_param_set_from_value(THD *thd,
Item_param *param,
@ -7937,38 +7738,6 @@ Item *Type_handler_temporal_with_date::
}
Item *Type_handler_row::
make_const_item_for_comparison(THD *thd, Item *item, const Item *cmp) const
{
if (item->type() == Item::ROW_ITEM && cmp->type() == Item::ROW_ITEM)
{
/*
Substitute constants only in Item_row's. Don't affect other Items
with ROW_RESULT (eg Item_singlerow_subselect).
For such Items more optimal is to detect if it is constant and replace
it with Item_row. This would optimize queries like this:
SELECT * FROM t1 WHERE (a,b) = (SELECT a,b FROM t2 LIMIT 1);
*/
Item_row *item_row= (Item_row*) item;
Item_row *comp_item_row= (Item_row*) cmp;
uint col;
/*
If item and comp_item are both Item_row's and have same number of cols
then process items in Item_row one by one.
We can't ignore NULL values here as this item may be used with <=>, in
which case NULL's are significant.
*/
DBUG_ASSERT(item->result_type() == cmp->result_type());
DBUG_ASSERT(item_row->cols() == comp_item_row->cols());
col= item_row->cols();
while (col-- > 0)
resolve_const_item(thd, item_row->addr(col),
comp_item_row->element_index(col));
}
return NULL;
}
/***************************************************************************/
/*
@ -8403,19 +8172,6 @@ void Type_handler_typelib::Item_param_set_param_func(Item_param *param,
/***************************************************************************/
Field *Type_handler_row::
make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root,
const LEX_CSTRING *name,
const Record_addr &rec, const Bit_addr &bit,
const Column_definition_attributes *attr,
uint32 flags) const
{
DBUG_ASSERT(attr->length == 0);
DBUG_ASSERT(f_maybe_null(attr->pack_flag));
return new (mem_root) Field_row(rec.ptr(), name);
}
Field *Type_handler_olddecimal::
make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root,
const LEX_CSTRING *name,
@ -9146,14 +8902,6 @@ Type_handler_hex_hybrid::cast_to_int_type_handler() const
/***************************************************************************/
bool Type_handler_row::Item_eq_value(THD *thd, const Type_cmp_attributes *attr,
Item *a, Item *b) const
{
DBUG_ASSERT(0);
return false;
}
bool Type_handler_int_result::Item_eq_value(THD *thd,
const Type_cmp_attributes *attr,
Item *a, Item *b) const
@ -9646,13 +9394,6 @@ bool Type_handler_datetime_common::validate_implicit_default_value(THD *thd,
/***************************************************************************/
const Name & Type_handler_row::default_value() const
{
DBUG_ASSERT(0);
static Name def(STRING_WITH_LEN(""));
return def;
}
const Name & Type_handler_numeric::default_value() const
{
static Name def(STRING_WITH_LEN("0"));

View File

@ -35,6 +35,7 @@ C_MODE_START
C_MODE_END
class Field;
class Qualified_ident;
class Column_definition;
class Column_definition_attributes;
class Key_part_spec;
@ -79,6 +80,7 @@ class Item_func_mul;
class Item_func_div;
class Item_func_mod;
class Item_type_holder;
class Item_splocal;
class cmp_item;
class in_vector;
class Type_handler_data;
@ -98,8 +100,10 @@ class Conv_source;
class ST_FIELD_INFO;
class Type_collection;
class Create_func;
class Type_handler_composite;
class sp_type_def;
class sp_head;
class sp_instr;
class my_var;
#define my_charset_numeric my_charset_latin1
@ -4135,6 +4139,15 @@ public:
return false;
}
/*
Convert "this" to a composite type handler.
Scalar type handlers return nullptr meaning that they are not composite.
*/
virtual const Type_handler_composite *to_composite() const
{
return nullptr;
}
virtual bool partition_field_check(const LEX_CSTRING &field_name, Item *)
const
{
@ -4185,6 +4198,12 @@ 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_methods() const { return false; }
/*
If an SP variable supports: spvar(expr_list).
For example, assoc arrays support: spvar_assoc_array('key')
*/
virtual bool has_functors() 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;
@ -4409,6 +4428,17 @@ public:
const Lex_ident_sys_st &field,
sp_head *sphead,
bool validate_only) const;
/*
SELECT 1 INTO spvar(arg);
SELECT 1 INTO spvar(arg).field;
*/
virtual my_var *make_outvar_lvalue_functor(THD *thd,
const Lex_ident_sys_st &name,
Item *arg,
const Lex_ident_sys &opt_field,
sp_head *sphead,
const sp_rcontext_addr &addr,
bool validate_only) const;
virtual void
Column_definition_attributes_frm_pack(const Column_definition_attributes *at,
uchar *buff) const;
@ -4627,6 +4657,50 @@ public:
return nullptr;
}
virtual Item_copy *create_item_copy(THD *thd, Item *item) const;
/*
Create an Item for an expression of this kind:
SELECT spvar(args); -- e.g. spvar_assoc_array('key')
SELECT spvar(args).field; -- e.g. spvar_assoc_array('key').field
*/
virtual Item_splocal *create_item_functor(THD *thd,
const Lex_ident_sys &a,
const sp_rcontext_addr &addr,
List<Item> *item_list,
const Lex_ident_sys &b,
const Lex_ident_cli_st &name)
const
{
DBUG_ASSERT(0); // Should have checked has_functors().
return nullptr;
}
/*
Generate instructions for:
spvar(args) := expr; -- e.g. spvar_assoc_array('key') := 10;
spvar(args).member := expr; -- e.g. spvar_assoc_array('key').field:= 10;
*/
virtual
sp_instr *create_instr_set_assign_functor(THD *thd, LEX *lex,
const Qualified_ident &ident,
const sp_rcontext_addr &addr,
List<Item> *args,
const Lex_ident_sys_st &member,
Item *item,
const LEX_CSTRING &expr_str) const
{
DBUG_ASSERT(0); // Should have checked has_functors().
return nullptr;
}
virtual Item *create_item_method(THD *thd,
const Lex_ident_sys &ca,
const Lex_ident_sys &cb,
List<Item> *args,
const Lex_ident_cli_st &query_fragment)
const
{
DBUG_ASSERT(0); // Should have checked has_methods().
return nullptr;
}
virtual int cmp_native(const Native &a, const Native &b) const
{
MY_ASSERT_UNREACHABLE();
@ -4778,6 +4852,10 @@ public:
Item_func_mod_fix_length_and_dec(Item_func_mod *func) const= 0;
virtual const Vers_type_handler *vers() const { return NULL; }
void raise_bad_data_type_for_functor(const Qualified_ident &ident,
const Lex_ident_sys &field=
Lex_ident_sys()) const;
};
@ -5076,14 +5154,53 @@ public:
};
class Type_limits_int
class Type_range_int
{
const longlong m_min_signed;
const longlong m_max_signed;
const ulonglong m_max_unsigned;
public:
Type_range_int(longlong min_signed, longlong max_signed,
ulonglong max_unsigned)
:m_min_signed(min_signed), m_max_signed(max_signed),
m_max_unsigned(max_unsigned)
{ }
longlong min_signed() const { return m_min_signed; }
longlong max_signed() const { return m_max_signed; }
ulonglong max_unsigned() const { return m_max_unsigned; }
static Type_range_int range8()
{
return Type_range_int(INT_MIN8, INT_MAX8, UINT_MAX8);
}
static Type_range_int range16()
{
return Type_range_int(INT_MIN16, INT_MAX16, UINT_MAX16);
}
static Type_range_int range24()
{
return Type_range_int(INT_MIN24, INT_MAX24, UINT_MAX24);
}
static Type_range_int range32()
{
return Type_range_int(INT_MIN32, INT_MAX32, UINT_MAX32);
}
static Type_range_int range64()
{
return Type_range_int(INT_MIN64, INT_MAX64, UINT64_MAX);
}
};
class Type_limits_int: public Type_range_int
{
private:
uint32 m_precision;
uint32 m_char_length;
public:
Type_limits_int(uint32 prec, uint32 nchars)
:m_precision(prec), m_char_length(nchars)
Type_limits_int(uint32 prec, uint32 nchars, const Type_range_int &range)
:Type_range_int(range),
m_precision(prec), m_char_length(nchars)
{ }
uint32 precision() const { return m_precision; }
uint32 char_length() const { return m_char_length; }
@ -5098,7 +5215,7 @@ class Type_limits_uint8: public Type_limits_int
{
public:
Type_limits_uint8()
:Type_limits_int(MAX_TINYINT_WIDTH, MAX_TINYINT_WIDTH)
:Type_limits_int(MAX_TINYINT_WIDTH, MAX_TINYINT_WIDTH, range8())
{ }
};
@ -5107,7 +5224,7 @@ class Type_limits_sint8: public Type_limits_int
{
public:
Type_limits_sint8()
:Type_limits_int(MAX_TINYINT_WIDTH, MAX_TINYINT_WIDTH + 1)
:Type_limits_int(MAX_TINYINT_WIDTH, MAX_TINYINT_WIDTH + 1, range8())
{ }
};
@ -5120,7 +5237,7 @@ class Type_limits_uint16: public Type_limits_int
{
public:
Type_limits_uint16()
:Type_limits_int(MAX_SMALLINT_WIDTH, MAX_SMALLINT_WIDTH)
:Type_limits_int(MAX_SMALLINT_WIDTH, MAX_SMALLINT_WIDTH, range16())
{ }
};
@ -5129,7 +5246,7 @@ class Type_limits_sint16: public Type_limits_int
{
public:
Type_limits_sint16()
:Type_limits_int(MAX_SMALLINT_WIDTH, MAX_SMALLINT_WIDTH + 1)
:Type_limits_int(MAX_SMALLINT_WIDTH, MAX_SMALLINT_WIDTH + 1, range16())
{ }
};
@ -5142,7 +5259,7 @@ class Type_limits_uint24: public Type_limits_int
{
public:
Type_limits_uint24()
:Type_limits_int(MAX_MEDIUMINT_WIDTH, MAX_MEDIUMINT_WIDTH)
:Type_limits_int(MAX_MEDIUMINT_WIDTH, MAX_MEDIUMINT_WIDTH, range24())
{ }
};
@ -5151,7 +5268,7 @@ class Type_limits_sint24: public Type_limits_int
{
public:
Type_limits_sint24()
:Type_limits_int(MAX_MEDIUMINT_WIDTH - 1, MAX_MEDIUMINT_WIDTH)
:Type_limits_int(MAX_MEDIUMINT_WIDTH - 1, MAX_MEDIUMINT_WIDTH, range24())
{ }
};
@ -5164,7 +5281,7 @@ class Type_limits_uint32: public Type_limits_int
{
public:
Type_limits_uint32()
:Type_limits_int(MAX_INT_WIDTH, MAX_INT_WIDTH)
:Type_limits_int(MAX_INT_WIDTH, MAX_INT_WIDTH, range32())
{ }
};
@ -5174,7 +5291,7 @@ class Type_limits_sint32: public Type_limits_int
{
public:
Type_limits_sint32()
:Type_limits_int(MAX_INT_WIDTH, MAX_INT_WIDTH + 1)
:Type_limits_int(MAX_INT_WIDTH, MAX_INT_WIDTH + 1, range32())
{ }
};
@ -5186,7 +5303,8 @@ public:
class Type_limits_uint64: public Type_limits_int
{
public:
Type_limits_uint64(): Type_limits_int(MAX_BIGINT_WIDTH, MAX_BIGINT_WIDTH)
Type_limits_uint64()
:Type_limits_int(MAX_BIGINT_WIDTH, MAX_BIGINT_WIDTH, range64())
{ }
};
@ -5195,7 +5313,7 @@ class Type_limits_sint64: public Type_limits_int
{
public:
Type_limits_sint64()
:Type_limits_int(MAX_BIGINT_WIDTH - 1, MAX_BIGINT_WIDTH)
:Type_limits_int(MAX_BIGINT_WIDTH - 1, MAX_BIGINT_WIDTH, range64())
{ }
};
@ -5817,6 +5935,7 @@ public:
Item_cache *Item_get_cache(THD *thd, const Item *item) const override;
int Item_save_in_field(Item *item, Field *field, bool no_conversions)
const override;
String *print_item_value(THD *thd, Item *item, String *str) const override;
};
@ -7615,7 +7734,7 @@ class Named_type_handler : public TypeHandler
{ Type_handler::set_name(Name(n, static_cast<uint>(strlen(n)))); }
};
extern Named_type_handler<Type_handler_row> type_handler_row;
extern const Type_handler_composite &type_handler_row;
extern Named_type_handler<Type_handler_null> type_handler_null;
extern Named_type_handler<Type_handler_float> type_handler_float;

211
sql/sql_type_composite.cc Normal file
View File

@ -0,0 +1,211 @@
/*
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
*/
#include "sql_type.h"
#include "sql_type_composite.h"
#include "item.h"
#include "item_cmpfunc.h"
#include "sp_head.h"
const Name & Type_handler_composite::default_value() const
{
DBUG_ASSERT(0);
static Name def(STRING_WITH_LEN(""));
return def;
}
bool Type_handler_composite::
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(&type_handler_row, row);
if (lex->sphead->fill_spvar_definition(thd, &spvar->field_def,
&spvar->name))
return true;
}
return false;
}
bool Type_handler_composite::
Column_definition_prepare_stage1(THD *thd,
MEM_ROOT *mem_root,
Column_definition *def,
column_definition_type_t type,
const Column_derived_attributes
*derived_attr)
const
{
def->charset= &my_charset_bin;
def->create_length_to_internal_length_null();
return false;
}
bool Type_handler_composite::Item_eq_value(THD *thd,
const Type_cmp_attributes *attr,
Item *a, Item *b) const
{
DBUG_ASSERT(0);
return false;
}
bool Type_handler_composite::
Item_save_in_value(THD *thd, Item *item, st_value *value) const
{
DBUG_ASSERT(0);
value->m_type= DYN_COL_NULL;
return true;
}
bool Type_handler_composite::
Item_param_set_from_value(THD *thd,
Item_param *param,
const Type_all_attributes *attr,
const st_value *val) const
{
DBUG_ASSERT(0);
param->set_null();
return true;
}
void Type_handler_composite::Item_update_null_value(Item *item) const
{
DBUG_ASSERT(0);
item->null_value= true;
}
longlong Type_handler_composite::
Item_func_between_val_int(Item_func_between *func) const
{
DBUG_ASSERT(0);
func->null_value= true;
return 0;
}
bool Type_handler_composite::
Item_func_round_fix_length_and_dec(Item_func_round *item) const
{
DBUG_ASSERT(0);
return false;
}
bool Type_handler_composite::
Item_func_int_val_fix_length_and_dec(Item_func_int_val *item) const
{
DBUG_ASSERT(0);
return false;
}
bool Type_handler_composite::
Item_func_abs_fix_length_and_dec(Item_func_abs *item) const
{
DBUG_ASSERT(0);
return false;
}
bool Type_handler_composite::
Item_func_neg_fix_length_and_dec(Item_func_neg *item) const
{
DBUG_ASSERT(0);
return false;
}
bool Type_handler_composite::
Item_func_plus_fix_length_and_dec(Item_func_plus *item) const
{
DBUG_ASSERT(0);
return true;
}
bool Type_handler_composite::
Item_func_minus_fix_length_and_dec(Item_func_minus *item) const
{
DBUG_ASSERT(0);
return true;
}
bool Type_handler_composite::
Item_func_mul_fix_length_and_dec(Item_func_mul *item) const
{
DBUG_ASSERT(0);
return true;
}
bool Type_handler_composite::
Item_func_div_fix_length_and_dec(Item_func_div *item) const
{
DBUG_ASSERT(0);
return true;
}
bool Type_handler_composite::
Item_func_mod_fix_length_and_dec(Item_func_mod *item) const
{
DBUG_ASSERT(0);
return true;
}
bool Type_handler_composite::
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;
}

425
sql/sql_type_composite.h Normal file
View File

@ -0,0 +1,425 @@
/*
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_COMPOSITE_INCLUDED
#define SQL_TYPE_COMPOSITE_INCLUDED
#include "sql_type.h"
class Item_splocal;
class Field_composite;
class Item_composite;
class Item_field;
class Row_definition_list;
class Type_handler_composite: 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_composite() = 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_handler_composite *to_composite() const override
{
return this;
}
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;
}
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;
}
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;
}
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;
}
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_copy *create_item_copy(THD *, Item *) const override
{
MY_ASSERT_UNREACHABLE();
return nullptr;
}
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;
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;
virtual bool key_to_lex_cstring(THD *thd, Item **key,
const LEX_CSTRING& name,
LEX_CSTRING& out_key) const
{
return false;
}
/*
Get the index of the item with the given name in the composite item.
This is only implemented for composite items that have a fixed number of
fields, such as ROWs.
*/
virtual bool get_item_index(THD *thd, const Item_field *item,
const LEX_CSTRING& name, uint& idx) const = 0;
virtual Item_field *get_item(THD *thd, const Item_field *item,
const LEX_CSTRING& name) const = 0;
virtual Item_field *get_or_create_item(THD *thd, Item_field *item,
const LEX_CSTRING& name) const = 0;
virtual void prepare_for_set(Item_field *item) const
{
return;
}
virtual bool finalize_for_set(Item_field *item) const
{
return false;
}
};
#endif /* SQL_TYPE_COMPOSITE_INCLUDED */

View File

@ -23,39 +23,60 @@
#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)
class Type_collection_row: public Type_collection
{
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++)
public:
bool init(Type_handler_data *data) override
{
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;
}
return false;
const Type_handler *aggregate_for_result(const Type_handler *a,
const Type_handler *b)
const override
{
return NULL;
}
const Type_handler *aggregate_for_comparison(const Type_handler *a,
const Type_handler *b)
const override
{
/*
Allowed combinations:
ROW+ROW, NULL+ROW, ROW+NULL
*/
DBUG_ASSERT(a == &type_handler_row || a == &type_handler_null);
DBUG_ASSERT(b == &type_handler_row || b == &type_handler_null);
DBUG_ASSERT(a == &type_handler_row || b == &type_handler_row);
return &type_handler_row;
}
const Type_handler *aggregate_for_min_max(const Type_handler *a,
const Type_handler *b)
const override
{
return NULL;
}
const Type_handler *aggregate_for_num_op(const Type_handler *a,
const Type_handler *b)
const override
{
return NULL;
}
};
static Type_collection_row type_collection_row;
const Type_collection *Type_handler_row::type_collection() const
{
return &type_collection_row;
}
const Type_handler *Type_handler_row::type_handler_for_comparison() const
{
return &type_handler_row;
}
@ -170,6 +191,23 @@ Item_field *Field_row::make_item_field_spvar(THD *thd,
}
bool Type_handler_row::Spvar_definition_with_complex_data_types(
Spvar_definition *def) const
{
if (def->row_field_definitions() && def->is_row())
{
List_iterator<Spvar_definition> it(*(def->row_field_definitions()));
Spvar_definition *member;
while ((member= it++))
{
if (member->type_handler()->is_complex())
return true;
}
}
return false;
}
bool
Type_handler_row::
sp_variable_declarations_finalize(THD *thd, LEX *lex, int nvars,
@ -188,10 +226,114 @@ Type_handler_row::
// 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);
Type_handler_composite::sp_variable_declarations_row_finalize(thd,
lex,
nvars,
row);
}
bool Type_handler_row::
set_comparator_func(THD *thd, Arg_comparator *cmp) const
{
return cmp->set_cmp_func_row(thd);
}
Item_cache *
Type_handler_row::Item_get_cache(THD *thd, const Item *item) const
{
return new (thd->mem_root) Item_cache_row(thd);
}
cmp_item *Type_handler_row::make_cmp_item(THD *thd,
CHARSET_INFO *cs) const
{
return new (thd->mem_root) cmp_item_row;
}
in_vector *Type_handler_row::make_in_vector(THD *thd,
const Item_func_in *func,
uint nargs) const
{
return new (thd->mem_root) in_row(thd, nargs, 0);
}
bool Type_handler_row::Item_func_in_fix_comparator_compatible_types(THD *thd,
Item_func_in *func) const
{
return func->compatible_types_row_bisection_possible() ?
func->fix_for_row_comparison_using_bisection(thd) :
func->fix_for_row_comparison_using_cmp_items(thd);
}
/**
Get a string representation of the Item value.
See sql_type.h for details.
*/
String *Type_handler_row::
print_item_value(THD *thd, Item *item, String *str) const
{
CHARSET_INFO *cs= thd->variables.character_set_client;
StringBuffer<STRING_BUFFER_USUAL_SIZE> val(cs);
str->append(STRING_WITH_LEN("ROW("));
for (uint i= 0 ; i < item->cols(); i++)
{
if (i > 0)
str->append(',');
Item *elem= item->element_index(i);
String *tmp= elem->type_handler()->print_item_value(thd, elem, &val);
if (tmp)
str->append(*tmp);
else
str->append(NULL_clex_str);
}
str->append(')');
return str;
}
Item *Type_handler_row::
make_const_item_for_comparison(THD *thd, Item *item, const Item *cmp) const
{
if (item->type() == Item::ROW_ITEM && cmp->type() == Item::ROW_ITEM)
{
/*
Substitute constants only in Item_row's. Don't affect other Items
with ROW_RESULT (eg Item_singlerow_subselect).
For such Items more optimal is to detect if it is constant and replace
it with Item_row. This would optimize queries like this:
SELECT * FROM t1 WHERE (a,b) = (SELECT a,b FROM t2 LIMIT 1);
*/
Item_row *item_row= (Item_row*) item;
Item_row *comp_item_row= (Item_row*) cmp;
uint col;
/*
If item and comp_item are both Item_row's and have same number of cols
then process items in Item_row one by one.
We can't ignore NULL values here as this item may be used with <=>, in
which case NULL's are significant.
*/
DBUG_ASSERT(item->result_type() == cmp->result_type());
DBUG_ASSERT(item_row->cols() == comp_item_row->cols());
col= item_row->cols();
while (col-- > 0)
resolve_const_item(thd, item_row->addr(col),
comp_item_row->element_index(col));
}
return NULL;
}
Field *Type_handler_row::
make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root,
const LEX_CSTRING *name,
const Record_addr &rec, const Bit_addr &bit,
const Column_definition_attributes *attr,
uint32 flags) const
{
DBUG_ASSERT(attr->length == 0);
DBUG_ASSERT(f_maybe_null(attr->pack_flag));
return new (mem_root) Field_row(rec.ptr(), name);
}
@ -209,15 +351,40 @@ Item *Type_handler_row::make_typedef_constructor_item(THD *thd,
}
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
bool Type_handler_row::get_item_index(THD *thd,
const Item_field *item,
const LEX_CSTRING& name,
uint& idx) const
{
my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0),
name().ptr(), opname.str);
return true;
auto item_row=
dynamic_cast<Item_field_row *>(const_cast<Item_field *> (item));
DBUG_ASSERT(item_row);
auto vtable= item_row->field->virtual_tmp_table();
if (!vtable)
return true;
return vtable->sp_find_field_by_name_or_error(&idx,
item_row->field->field_name,
name);
}
Item_field *Type_handler_row::get_item(THD *thd,
const Item_field *item,
const LEX_CSTRING& name) const
{
auto item_row=
dynamic_cast<Item_field_row *>(const_cast<Item_field *> (item));
DBUG_ASSERT(item_row);
uint field_idx;
if (get_item_index(thd, item_row, name, field_idx))
return nullptr;
return item_row->element_index(field_idx)->field_for_view_update();
}
Named_type_handler<Type_handler_row> type_handler_row_internal("row");
const Type_handler_composite &type_handler_row=
type_handler_row_internal;

View File

@ -20,127 +20,26 @@
#define SQL_TYPE_ROW_INCLUDED
#include "sql_type_composite.h"
class Row_definition_list;
/*
Special handler for ROW
*/
class Type_handler_row: public Type_handler
class Type_handler_row: public Type_handler_composite
{
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,
@ -148,62 +47,6 @@ public:
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,
@ -218,216 +61,31 @@ public:
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
bool get_item_index(THD *thd,
const Item_field *item,
const LEX_CSTRING& name,
uint& idx) const override;
Item_field *get_item(THD *thd,
const Item_field *item,
const LEX_CSTRING& name) const override;
Item_field *get_or_create_item(THD *thd,
Item_field *item,
const LEX_CSTRING& name) const override
{
MY_ASSERT_UNREACHABLE();
return true;
return get_item(thd, const_cast<const Item_field *>(item), name);
}
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;
};

View File

@ -280,6 +280,7 @@ void _CONCAT_UNDERSCORED(turn_parser_debug_on,yyparse)()
Table_ident *table;
Qualified_column_ident *qualified_column_ident;
Optimizer_hint_parser_output *opt_hints;
Qualified_ident *qualified_ident;
char *simple_string;
const char *const_simple_string;
chooser_compare_func_creator boolfunc2creator;
@ -447,6 +448,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%token <NONE> SET_VAR /* OPERATOR */
%token <NONE> SHIFT_LEFT /* OPERATOR */
%token <NONE> SHIFT_RIGHT /* OPERATOR */
%token <NONE> ARROW_SYM /* OPERATOR */
/*
@ -1338,6 +1340,11 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
sp_block_label sp_control_label opt_place opt_db
udt_name
%ifdef ORACLE
%type <lex_str>
assoc_name
%endif
%type <ident_sys>
IDENT_sys
ident
@ -1400,6 +1407,12 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%type <qualified_column_ident>
optionally_qualified_column_ident
%ifdef ORACLE
%type <qualified_ident>
optionally_qualified_directly_assignable
direct_call_or_lvalue_assign
%endif
%type <simple_string>
remember_name remember_end
remember_tok_start
@ -1424,7 +1437,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%type <json_on_response> json_on_response
%type <Lex_field_type> field_type field_type_all field_type_all_builtin
field_type_all_with_record
field_type_all_with_composites
qualified_field_type
field_type_numeric
field_type_string
@ -1433,6 +1446,15 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
field_type_misc
json_table_field_type
%ifdef ORACLE
%type <spvar_definition> assoc_array_table_types
%type <spvar_definition> assoc_array_index_type
%type <Lex_field_type> field_type_all_with_record assoc_array_index_types
%endif
%type <ident_cli>
opt_object_member_access
%type <Lex_exact_charset_extended_collation_attrs>
binary
opt_binary
@ -1576,6 +1598,20 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
ident_list ident_list_arg opt_expr_list
execute_using
execute_params
opt_sp_cparam_list
opt_sp_cparams
sp_cparams
%ifdef ORACLE
%type <item_list>
parenthesized_opt_sp_cparams
opt_parenthesized_opt_sp_cparams
%endif
%ifdef ORACLE
%type <item> named_expr
%type <item_list> named_expr_list
%endif
%type <sp_cursor_stmt>
sp_cursor_stmt_lex
@ -1990,6 +2026,7 @@ rule:
%type <kwd> reserved_keyword_udt_param_type
%else
%type <NONE> set_assign
%type <NONE> set_assign_lvalue_function
%type <spvar_mode> sp_opt_inout
%type <NONE> sp_tail_standalone
%type <NONE> sp_labelable_stmt
@ -2010,6 +2047,7 @@ rule:
%type <spblock_handlers> sp_block_statements_and_exceptions
%type <sp_instr_addr> sp_instr_addr
%type <num> opt_exception_clause exception_handlers
%type <ident_sys> typed_ident
%endif ORACLE
%%
@ -3349,27 +3387,43 @@ call:
/* CALL parameters */
opt_sp_cparam_list:
/* Empty */
/* Empty */ { $$= nullptr; }
| '('
{
thd->where= THD_WHERE::USE_WHERE_STRING;
thd->where_str= "CALL";
} opt_sp_cparams ')'
{
$$= $3;
}
;
%ifdef ORACLE
opt_parenthesized_opt_sp_cparams:
/* Empty */ { $$= nullptr; }
| parenthesized_opt_sp_cparams { $$= $1; }
;
parenthesized_opt_sp_cparams:
'(' opt_sp_cparams ')' { $$= $2; }
;
%endif
opt_sp_cparams:
/* Empty */
| sp_cparams
/* Empty */ { $$= nullptr; }
| sp_cparams { $$= $1; }
;
sp_cparams:
sp_cparams ',' expr
{
Lex->value_list.push_back($3, thd->mem_root);
($$= $1)->push_back($3, thd->mem_root);
}
| expr
{
Lex->value_list.push_back($1, thd->mem_root);
($$= &Lex->value_list)->push_back($1, thd->mem_root);
}
;
@ -3501,8 +3555,9 @@ optionally_qualified_column_ident:
row_field_definition:
row_field_name field_type
{
Lex->last_field->set_attributes(thd, $2,
COLUMN_DEFINITION_ROUTINE_LOCAL);
if (Lex->last_field->set_attributes(thd, $2,
COLUMN_DEFINITION_ROUTINE_LOCAL))
MYSQL_YYABORT;
}
;
@ -3527,8 +3582,9 @@ row_type_body:
rec_field_definition:
row_field_name field_type
{
Lex->last_field->set_attributes(thd, $2,
COLUMN_DEFINITION_ROUTINE_LOCAL);
if (Lex->last_field->set_attributes(thd, $2,
COLUMN_DEFINITION_ROUTINE_LOCAL))
MYSQL_YYABORT;
}
| rec_field_definition_anchored
;
@ -3577,10 +3633,11 @@ sp_decl_idents_init_vars:
sp_decl_variable_list:
sp_decl_idents_init_vars
field_type_all_with_record
field_type_all_with_composites
{
Lex->last_field->set_attributes(thd, $2,
COLUMN_DEFINITION_ROUTINE_LOCAL);
if (Lex->last_field->set_attributes(thd, $2,
COLUMN_DEFINITION_ROUTINE_LOCAL))
MYSQL_YYABORT;
}
sp_opt_default
{
@ -6247,8 +6304,9 @@ field_spec:
field_type_or_serial:
qualified_field_type
{
Lex->last_field->set_attributes(thd, $1,
COLUMN_DEFINITION_TABLE_FIELD);
if (Lex->last_field->set_attributes(thd, $1,
COLUMN_DEFINITION_TABLE_FIELD))
MYSQL_YYABORT;
}
field_def
{
@ -6475,6 +6533,7 @@ field_type_all:
}
;
%ifdef ORACLE
field_type_all_with_record:
field_type_all_builtin
{
@ -6482,19 +6541,36 @@ field_type_all_with_record:
}
| udt_name float_options srid_option
{
sp_type_def *sprec = NULL;
if (Lex->spcont)
sprec = Lex->spcont->find_type_def($1, false);
bool is_composite= false;
if (unlikely(Lex->set_field_type_composite(&$$, $1, false,
&is_composite)))
MYSQL_YYABORT;
if (sprec == NULL)
if (!is_composite)
{
if (Lex->set_field_type_udt(&$$, $1, $2))
MYSQL_YYABORT;
}
else
}
;
%endif
field_type_all_with_composites:
field_type_all_builtin
{
Lex->map_data_type(Lex_ident_sys(), &($$= $1));
}
| udt_name float_options srid_option
{
bool is_composite= false;
if (unlikely(Lex->set_field_type_composite(&$$, $1, true,
&is_composite)))
MYSQL_YYABORT;
if (!is_composite)
{
$$.set(&type_handler_row, NULL);
Lex->last_field->set_attr_const_void_ptr(0, sprec);
if (Lex->set_field_type_udt(&$$, $1, $2))
MYSQL_YYABORT;
}
}
;
@ -10953,13 +11029,15 @@ function_call_generic:
$<udf>$= udf;
#endif
}
opt_udf_expr_list ')'
opt_udf_expr_list ')' opt_object_member_access
{
const Type_handler *h;
Create_func *builder;
Item *item= NULL;
const sp_type_def *tdef= NULL;
const Lex_ident_sys ident(thd, &$1);
sp_variable *spv= NULL;
bool allow_field_accessor= false;
if (unlikely(ident.is_null() ||
Lex_ident_routine::check_name_with_error(ident)))
@ -10991,6 +11069,22 @@ function_call_generic:
{
item= tdef->make_constructor_item(thd, $4);
}
else if (Lex->spcont &&
(spv= Lex->spcont->find_variable(&ident, false)) &&
spv->type_handler()->has_functors())
{
const char *end= $6.str ? $6.end() : $5.end();
const Lex_ident_cli name_cli($1.pos(), end - $1.pos());
auto ident2= $6.str ? Lex_ident_sys(thd, &$6) : Lex_ident_sys();
if (($6.str && ident2.is_null()) ||
!(item= Lex->create_item_functor(thd, ident, $4,
ident2, name_cli)))
MYSQL_YYABORT;
item->set_name(thd, $1.pos(), end - $1.pos(), thd->charset());
// Only allow 'result accessors' for associative arrays
allow_field_accessor= true;
}
else
{
#ifdef HAVE_DLOPEN
@ -11015,6 +11109,13 @@ function_call_generic:
}
}
if ($6.str && !allow_field_accessor)
{
Lex_ident_sys field_sys(thd, &$6);
my_error(ER_BAD_FIELD_ERROR, MYF(0), field_sys.str, ident.str);
MYSQL_YYABORT;
}
if (unlikely(! ($$= item)))
MYSQL_YYABORT;
}
@ -11038,7 +11139,10 @@ function_call_generic:
}
| ident_cli '.' ident_cli '(' opt_expr_list ')'
{
if (unlikely(!($$= Lex->make_item_func_call_generic(thd, &$1, &$3, $5))))
const Lex_ident_cli pos($1.pos(), $6.pos() - $1.pos() + 1);
if (unlikely(!($$= Lex->make_item_func_or_method_call(thd, $1,
$3, $5,
pos))))
MYSQL_YYABORT;
}
| ident_cli '.' ident_cli '.' ident_cli '(' opt_expr_list ')'
@ -11085,6 +11189,11 @@ function_call_generic:
*/
;
opt_object_member_access:
/* empty */ { $$= Lex_ident_cli((const char *)nullptr, 0); }
| '.' ident_cli { $$= $2; }
;
fulltext_options:
opt_natural_language_mode opt_query_expansion
{ $$= $1 | $2; }
@ -11105,6 +11214,9 @@ opt_query_expansion:
opt_udf_expr_list:
/* empty */ { $$= NULL; }
| udf_expr_list { $$= $1; }
%ifdef ORACLE
| named_expr_list { $$= $1; }
%endif
;
udf_expr_list:
@ -11901,16 +12013,18 @@ json_table_column_type:
{
Lex_field_type_st type;
type.set(&type_handler_slong);
Lex->last_field->set_attributes(thd, type,
COLUMN_DEFINITION_TABLE_FIELD);
if (Lex->last_field->set_attributes(thd, type,
COLUMN_DEFINITION_TABLE_FIELD))
MYSQL_YYABORT;
Lex->json_table->m_cur_json_table_column->
set(Json_table_column::FOR_ORDINALITY);
}
| json_table_field_type PATH_SYM json_text_literal
json_opt_on_empty_or_error
{
Lex->last_field->set_attributes(thd, $1,
COLUMN_DEFINITION_TABLE_FIELD);
if (Lex->last_field->set_attributes(thd, $1,
COLUMN_DEFINITION_TABLE_FIELD))
MYSQL_YYABORT;
if (Lex->json_table->m_cur_json_table_column->
set(thd, Json_table_column::PATH, $3,
$1.charset_collation_attrs()))
@ -11920,8 +12034,9 @@ json_table_column_type:
}
| json_table_field_type EXISTS PATH_SYM json_text_literal
{
Lex->last_field->set_attributes(thd, $1,
COLUMN_DEFINITION_TABLE_FIELD);
if (Lex->last_field->set_attributes(thd, $1,
COLUMN_DEFINITION_TABLE_FIELD))
MYSQL_YYABORT;
if (Lex->json_table->m_cur_json_table_column->
set(thd, Json_table_column::EXISTS_PATH, $4,
$1.charset_collation_attrs()))
@ -13358,6 +13473,15 @@ select_outvar:
if (unlikely(!($$= Lex->create_outvar(thd, $1, $3)) && Lex->result))
MYSQL_YYABORT;
}
| ident '(' expr ')' opt_object_member_access
{
auto field= $5.str ? Lex_ident_sys(thd, &$5) : Lex_ident_sys();
if (unlikely(($5.str && !field.str) ||
(!($$= Lex->create_outvar_lvalue_function(thd, $1, $3,
field)) &&
Lex->result)))
MYSQL_YYABORT;
}
;
into:
@ -19282,46 +19406,56 @@ sp_unlabeled_block_not_atomic:
statement:
verb_clause
| set_assign
| set_assign_lvalue_function
;
direct_call_or_lvalue_assign:
optionally_qualified_directly_assignable
{
if (Lex->call_statement_start_or_lvalue_assign(thd, $$= $1))
MYSQL_YYABORT;
}
;
direct_call_statement:
direct_call_or_lvalue_assign opt_parenthesized_opt_sp_cparams
{
if (unlikely($1->spvar()))
{
thd->parse_error(ER_SYNTAX_ERROR, $1->pos());
MYSQL_YYABORT;
}
if (Lex->check_cte_dependencies_and_resolve_references())
MYSQL_YYABORT;
}
;
set_assign_lvalue_function:
direct_call_or_lvalue_assign parenthesized_opt_sp_cparams
opt_object_member_access SET_VAR
{
if (unlikely(Lex->assoc_assign_start(thd, $1)))
MYSQL_YYABORT;
}
set_expr_or_default
{
const Lex_ident_sys member= $3.str ? Lex_ident_sys(thd, &$3) :
Lex_ident_sys();
if (unlikely($3.str && member.is_null()) ||
unlikely(Lex->sp_set_assign_lvalue_function(thd, $1, $2,
member,
$6.expr,
$6.expr_str)) ||
unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY,
false)))
MYSQL_YYABORT;
}
;
sp_statement:
statement
| ident_cli_directly_assignable
{
// Direct procedure call (without the CALL keyword)
Lex_ident_sys tmp(thd, &$1);
if (unlikely(!tmp.str) ||
unlikely(Lex->call_statement_start(thd, &tmp)))
MYSQL_YYABORT;
}
opt_sp_cparam_list
{
if (Lex->check_cte_dependencies_and_resolve_references())
MYSQL_YYABORT;
}
| ident_cli_directly_assignable '.' ident
{
Lex_ident_sys tmp(thd, &$1);
if (unlikely(!tmp.str) ||
unlikely(Lex->call_statement_start(thd, &tmp, &$3)))
MYSQL_YYABORT;
}
opt_sp_cparam_list
{
if (Lex->check_cte_dependencies_and_resolve_references())
MYSQL_YYABORT;
}
| ident_cli_directly_assignable '.' ident '.' ident
{
Lex_ident_sys tmp(thd, &$1);
if (unlikely(Lex->call_statement_start(thd, &tmp, &$3, &$5)))
MYSQL_YYABORT;
}
opt_sp_cparam_list
{
if (Lex->check_cte_dependencies_and_resolve_references())
MYSQL_YYABORT;
}
| direct_call_statement
;
sp_if_then_statements:
@ -19565,39 +19699,38 @@ ident_cli_directly_assignable:
;
optionally_qualified_directly_assignable:
ident_cli_directly_assignable
{
if (unlikely(!($$= new (thd->mem_root) Qualified_ident(thd, $1))))
MYSQL_YYABORT;
}
| ident_cli_directly_assignable '.' ident
{
if (unlikely(!($$= new (thd->mem_root) Qualified_ident(thd, $1,
$3))))
MYSQL_YYABORT;
}
| ident_cli_directly_assignable '.' ident '.' ident
{
if (unlikely(!($$= new (thd->mem_root) Qualified_ident(thd, $1,
$3, $5))))
MYSQL_YYABORT;
}
;
set_assign:
ident_cli_directly_assignable SET_VAR
optionally_qualified_directly_assignable SET_VAR
{
LEX *lex=Lex;
lex->set_stmt_init();
if (sp_create_assignment_lex(thd, $1.pos()))
if (sp_create_assignment_lex(thd, $1->pos()))
MYSQL_YYABORT;
}
set_expr_or_default
{
Lex_ident_sys tmp(thd, &$1);
if (unlikely(!tmp.str) ||
unlikely(Lex->set_variable(&tmp, $4.expr, $4.expr_str)) ||
unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY,
false)))
MYSQL_YYABORT;
}
| ident_cli_directly_assignable '.' ident SET_VAR
{
LEX *lex=Lex;
lex->set_stmt_init();
if (sp_create_assignment_lex(thd, $1.pos()))
MYSQL_YYABORT;
}
set_expr_or_default
{
LEX *lex= Lex;
DBUG_ASSERT(lex->var_list.is_empty());
Lex_ident_sys tmp(thd, &$1);
if (unlikely(!tmp.str) ||
unlikely(lex->set_variable(&tmp, &$3, $6.expr, $6.expr_str)) ||
if (unlikely(Lex->set_variable($1, $4.expr, $4.expr_str)) ||
unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY,
false)))
MYSQL_YYABORT;
@ -19762,6 +19895,40 @@ package_implementation_executable_section:
| BEGIN_ORACLE_SYM sp_block_statements_and_exceptions END { $$= $2; }
;
named_expr_list:
remember_name named_expr remember_end
{
if (unlikely(!($$= new (thd->mem_root) List<Item>) ||
$$->push_back($2, thd->mem_root)))
MYSQL_YYABORT;
}
| named_expr_list ',' remember_name named_expr remember_end
{
if (($$= $1)->push_back($4, thd->mem_root))
MYSQL_YYABORT;
}
;
assoc_name:
TEXT_STRING_sys
| LONG_NUM
| ULONGLONG_NUM
| DECIMAL_NUM
| NUM
;
named_expr:
assoc_name ARROW_SYM expr
{
if ($1.str)
{
$3->base_flags|= item_base_t::IS_EXPLICIT_NAME;
$3->set_name(thd, $1);
}
$$= $3;
}
;
%endif ORACLE
@ -20154,6 +20321,79 @@ opt_sp_decl_handler_list:
| sp_decl_handler_list
;
typed_ident:
TYPE_SYM ident_directly_assignable
{
if (unlikely(!Lex->init_spvar_definition(thd, ($$= $2))))
MYSQL_YYABORT;
}
;
assoc_array_table_types:
field_type_all_with_record
{
if (Lex->last_field->set_attributes(thd, $1,
COLUMN_DEFINITION_ROUTINE_LOCAL))
MYSQL_YYABORT;
$$= static_cast<Spvar_definition*>(Lex->last_field);
}
| sp_decl_ident '.' ident PERCENT_ORACLE_SYM TYPE_SYM
{
$$= static_cast<Spvar_definition*>(Lex->last_field);
if (unlikely(Lex->sphead->spvar_def_fill_type_reference(thd, $$,
$1, $3)))
MYSQL_YYABORT;
}
| sp_decl_ident '.' ident '.' ident PERCENT_ORACLE_SYM TYPE_SYM
{
$$= static_cast<Spvar_definition*>(Lex->last_field);
if (unlikely(Lex->sphead->spvar_def_fill_type_reference(thd, $$,
$1, $3,
$5)))
MYSQL_YYABORT;
}
| ident PERCENT_ORACLE_SYM ROWTYPE_ORACLE_SYM
{
$$= static_cast<Spvar_definition*>(Lex->last_field);
if (unlikely(Lex->sphead->spvar_def_fill_rowtype_reference(thd, $$,
$1)))
MYSQL_YYABORT;
}
| sp_decl_ident '.' ident PERCENT_ORACLE_SYM ROWTYPE_ORACLE_SYM
{
$$= static_cast<Spvar_definition*>(Lex->last_field);
if (unlikely(Lex->sphead->spvar_def_fill_rowtype_reference(thd,
$$,
$1, $3)))
MYSQL_YYABORT;
}
;
assoc_array_index_types:
int_type opt_field_length last_field_options
{
$$.set_handler_length_flags($1, $2, (uint32) $3);
}
| varchar opt_field_length opt_binary_and_compression
{
$$.set(&type_handler_varchar, $2, $3);
}
| VARCHAR2_ORACLE_SYM opt_field_length opt_binary_and_compression
{
$$.set(&type_handler_varchar, $2, $3);
}
;
assoc_array_index_type:
INDEX_SYM BY assoc_array_index_types
{
if (Lex->last_field->set_attributes(thd, $3,
COLUMN_DEFINITION_ROUTINE_LOCAL))
MYSQL_YYABORT;
$$= new (thd->mem_root) Spvar_definition(*Lex->last_field);
}
;
sp_decl_non_handler:
sp_decl_variable_list
| ident_directly_assignable CONDITION_SYM FOR_SYM sp_cond
@ -20192,14 +20432,32 @@ sp_decl_non_handler:
$$.vars= $$.conds= $$.hndlrs= 0;
$$.curs= 1;
}
| TYPE_SYM ident_directly_assignable IS RECORD_SYM rec_type_body
| typed_ident IS RECORD_SYM rec_type_body
{
if (unlikely(Lex->spcont->
type_defs_declare_record(thd, Lex_ident_column($2), $5)))
type_defs_declare_record(thd, Lex_ident_column($1), $4)))
MYSQL_YYABORT;
$$.vars= $$.conds= $$.hndlrs= $$.curs= 0;
}
| typed_ident IS TABLE_SYM OF_SYM assoc_array_table_types
{
Lex->init_last_field(new (thd->mem_root) Column_definition(),
&empty_clex_str);
}
assoc_array_index_type
{
const auto aa= "associative_array"_Lex_ident_plugin;
const Type_handler *th=
Type_handler::handler_by_name_or_error(thd, aa);
if (unlikely(!th ||
Lex->spcont->
type_defs_declare_composite2(thd,
Lex_ident_column($1),
th, $7, $5)))
MYSQL_YYABORT;
$$.vars= $$.conds= $$.hndlrs= $$.curs= 0;
}
;