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:
parent
9e13cf0862
commit
5930c48c41
@ -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
|
||||
|
18
plugin/type_assoc_array/CMakeLists.txt
Normal file
18
plugin/type_assoc_array/CMakeLists.txt
Normal 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)
|
@ -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 */
|
@ -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;
|
@ -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
|
@ -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
|
@ -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;
|
@ -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
@ -0,0 +1,7 @@
|
||||
package My::Suite::Type_assoc_array;
|
||||
|
||||
@ISA = qw(My::Suite);
|
||||
|
||||
sub is_default { 1 }
|
||||
|
||||
bless { };
|
2573
plugin/type_assoc_array/sql_type_assoc_array.cc
Normal file
2573
plugin/type_assoc_array/sql_type_assoc_array.cc
Normal file
File diff suppressed because it is too large
Load Diff
462
plugin/type_assoc_array/sql_type_assoc_array.h
Normal file
462
plugin/type_assoc_array/sql_type_assoc_array.h
Normal 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 */
|
@ -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
|
@ -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 ;$$
|
@ -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}
|
||||
|
18
sql/field.h
18
sql/field.h
@ -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
73
sql/field_composite.h
Normal 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 ¶m) 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
31
sql/item_composite.cc
Normal 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
103
sql/item_composite.h
Normal 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 */
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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)},
|
||||
|
@ -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"
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
115
sql/sp_instr.cc
115
sql/sp_instr.cc
@ -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));
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
||||
/**
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
||||
|
@ -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) */
|
||||
|
||||
|
||||
|
@ -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
|
||||
{
|
||||
|
292
sql/sql_lex.cc
292
sql/sql_lex.cc
@ -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();
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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()
|
||||
{
|
||||
|
369
sql/sql_type.cc
369
sql/sql_type.cc
@ -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"));
|
||||
|
147
sql/sql_type.h
147
sql/sql_type.h
@ -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
211
sql/sql_type_composite.cc
Normal 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
425
sql/sql_type_composite.h
Normal 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 */
|
@ -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;
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
444
sql/sql_yacc.yy
444
sql/sql_yacc.yy
@ -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;
|
||||
}
|
||||
;
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user