1
0
mirror of https://github.com/MariaDB/server.git synced 2025-11-06 13:10:12 +03:00

Cleanup#1 for MDEV-34319: DECLARE TYPE .. TABLE OF .. INDEX BY

- Checking that the key expression is compatible with the INDEX BY data type
  for assignment in expressions:
    assoc_array_variable(key_expr)
    assoc_array_variable(key_expr).field

  in all contexts: SELECT, assignment target, INTO target.
  Raising an error in case it's not compatible.

- Disallowing non-constant expressions as a key,
  as the key is evaluated during the fix_fields() time.

- Disallowing stored functions as a key:
    assoc_array(stored_function())
    assoc_array(stored_function()).field

  The underlying MariaDB code is not ready to call a stored function
  during the fix_fields() time. This will be fixed in a separate MDEV.

- Removing the move Assoc_array_data's constructor.
  Using the usual constructor instead.

- Setting m_key.thread_specific and m_value.thread_specific to true
  in the Assoc_array_data constructor. This is needed to get assoc array
  element data counted by the @@session.memory_used status variable.

  Adding DBUG_ASSERTs to make sure the thread_specific flag never
  disappears in Assoc_array_data members.

- Removing my_free(item) from Field_assoc_array::element_by_key.
  It was a remainder from an earlier patch version.
  In the current patch version all Items behind an assoc array are
  created on a mem_root. It's wrong to use my_free() with them.

- Adding a helper method Field_assoc_array::assoc_tree_search()

- Fixing assoc_array_var.delete() to work as a procedure
  rather than a function. It does not need SELECT/DO any more.

- Fixing the crash in a few ctype_xxx tests, caused by the grammar change.

- Fixing compilation failure on Windows

- Adding a new method LEX::set_field_type_udt_or_typedef()
  and removing duplicate code from sql_yacc.yy

- Renaming the grammar rule field_type_all_with_composites to
  field_type_all_with_typedefs

- Removing the grammar rule assoc_array_index_types.
  Changing the grammar to "INDEX_SYM BY field_type".

  Removing the grammar rule field_type_all_with_record.
  Allow field_type_all_with_typedefs as an assoc array element.

  Catching wrong index and element data types has been moved to
  Type_handler_assoc_array::Column_definition_set_attributes().
  It raises an SQL error on things like:
  * assoc array of assoc arrays in TABLE OF
  * index by a non-supported types in INDEX BY

- Removing four methods:
  * sp_type_def_list::type_defs_add_record()
  * sp_type_def_list::type_defs_add_composite2()
  * sp_pcontext::type_defs_declare_record()
  * sp_type_def_list::type_defs_declare_composite2()
  Adding two methods instead:
  * sp_type_def_list::type_defs_add()
  * sp_pcontext::type_defs_add()
  This allows to get rid of the duplicate code detecting data type
  declarations with the same name in the same sp_pcontext frame.

- Adding new methods:
  * LEX::declare_type_assoc_array()
  * LEX::LEX::declare_type_record()
  They create a type specific sp_type_def_xxx and the call the generic
  sp_pcontext::type_defs_add().

- m_key_def.sp_prepare_create_field() inside
  Field_assoc_array::create_fields() is now called for all key data types
  (not only for integers)

- Removing the assignment of key_def->charset in
  Type_handler_assoc_array::sp_variable_declarations_finalize().
  The charset is now evaluated in m_key_def.sp_prepare_create_field().

- Fixing Item_assoc_array::get_key() to set the character set of the "key"
  to utf8mb3 instead of binary

- Fixing Field_assoc_array::copy_and_convert_key() to set the key length
  limit in terms of the character length as specified in
  INDEX BY VARCHAR(N), instead of octet length. This is needed to make
  keys with multi-byte characters work correctly.
  Also it now raises different errors depending on the reason of the
  key conversion failures:
  * ER_INVALID_CHARACTER_STRING
  * ER_CANNOT_CONVERT_CHARACTER

- Changing the prototype for Type_handler_composite::key_to_lex_cstring() to

   virtual LEX_CSTRING key_to_lex_cstring(THD *thd,
                                          const sp_rcontext_addr &var,
                                          Item **key,
                                          String *buffer) const;
   * Now it returns a LEX_CSTRING, instead of getting it as an out parameter.
   * Gets an sp_rcontext_addr instead of "name" and "def"
   * Gets a String buffer which can be used to be passed to val_str(),
     or for character set conversion purposes.

- Removing Field_assoc_array::m_key_def, as all required information
  is available from Field_assoc_array::m_key_field.
  In Field_assoc_array::create_fields turning m_key_def to a local variable
  key_def.

- Fixing Field_assoc_array::copy_and_convert_key() to follow MariaDB coding
  style: only constants can be passed by-reference, not-constants should
  be passed by-pointer.

- Adding DBUG_ASSERTs into Type_handler_assoc_array::get_item()
  and Type_handler_assoc_array::get_or_create_item() that the passed
  key in "name" is well formed according to the charset of INDEX BY.

- Changing the error ER_TOO_LONG_KEY to ER_WRONG_STRING_LENGTH.
  The former prints length limit in bytes, which is not applicable
  for INDEX BY values, because its limit is in characters.
  Also, the latter is more verbose.

- Fixing the problem that these wrong uses of an assoc array variable:

    BEGIN
      assoc_var;
      assoc_var(1);
    END;

  raised a weird error message:
    ERROR 1054 (42S22): Unknown column 'assoc_var' in '(null)'

  Now a more readable parse error is raised.

- Adding a "Duplicate key" warning for the cases when assigning
  between two assoc arrays rejects some records due to different
  collations in their INDEX BY key definitions.

- Disallow INDEX OF propagation from VARCHAR to TEXT.
  The underlying code cannot handle TEXT.
  Adding tests.

- Adding a helper class StringBufferKey to pass to val_str() when
  a key value is evaluated.
  Fixing all val_str() calls to val_str(&buffer), as the former is
  not desirable.

- Fixing a wrong use of args[0]->null_value in
  Item_func_assoc_array_exists::val_bool()

- Fixing a problem that using TABLE OF TEXT crashed the server.
  Thanks to Iqbal Hassan for the proposed patch.

- Changes in Qualified_ident:
  * Fixing the Qualified_ident constructors to get all parst as
    Lex_ident_cli_st, rather than the first part as Lex_ident_cli_st
    with the following parts Lex_ident_sys.
    This makes the code more symmetric.
  * Fixing the grammar in sql_yacc.yy accordinly.
  * Fixing the data type storing the possition in the client query
    from "const char *" to Lex_ident_cli.
  * Adding a new method Qualified_ident::is_sane().
    It allows to reduce the code side in sql_yacc.yy.
    Thanks to Iqbal Hassan for the idea.

- Replacing qs_append() to append_ulonglong() in:
  * Item_method_func::print()
  * Item_splocal_assoc_array_element::print()
  * Item_splocal_assoc_array_element_field::print()

  These methods do not use reserve()/alloc(), so calling qs_append()
  was wrong and caused a crash.

- Changing the output formats of these methods:
  * Item_splocal_assoc_array_element::print()
  * Item_splocal_assoc_array_element_field::print()
  not to print the key two times.
  Also moving the `@123` part (the variable offset) immediately
  after the variabl name and before the `[key]` part.

- Fixing a memory leak happened when trying to insert a duplicate
  key into an assoc array. Also adding a new "THD *" parameter to
  Field_assoc_array::insert_element(). Thanks to Iqbal Hassan for the fix.
  Adding a test into sp-assoc-array-ctype.test.

- In  Field_assoc_array::create_fields: m_element_field->field_name is now
  set for all element data types (not only for records).
  This fixed a wrong variable name in warnings. Adding tests.

- Adding tests:
  * Adding tests for assoc array elements in UNIONs.

  * Copying from an assoc array with a varchar key
    to an assoc array with a shorter varchar key.

  * A relatively big associative array.

  * Memory usage for x86_64.

  * Package variable as assoc array keys.

  * Character set conversion

  * TABLE OF TEXT

  * TABLE OF VARCHAR(>64k bytes) propagation to TABLE OF TEXT.

  * TEXT element fields in an array of records.

  * VARCHAR->TEXT propagation in elements in an array of records.

  * Some more tests
This commit is contained in:
Alexander Barkov
2025-05-06 15:06:59 +04:00
committed by Sergei Golubchik
parent 41014a4ecd
commit e9d541f912
48 changed files with 3865 additions and 478 deletions

View File

@@ -1997,6 +1997,10 @@ public:
:Well_formed_prefix_status(cs, str.str, str.str + str.length, nchars), :Well_formed_prefix_status(cs, str.str, str.str + str.length, nchars),
m_str(str.str) m_str(str.str)
{ } { }
Well_formed_prefix(CHARSET_INFO *cs, LEX_CSTRING str)
:Well_formed_prefix_status(cs, str.str, str.str + str.length, str.length),
m_str(str.str)
{ }
size_t length() const { return m_source_end_pos - m_str; } size_t length() const { return m_source_end_pos - m_str; }
}; };

View File

@@ -28,6 +28,12 @@ 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'; nick VARCHAR(64):= 'Monty';
BEGIN BEGIN
-- Make sure the method "DELETE" is not bin-logged per se
first_names('Liv') := 'Liver';
first_names('Sly') := 'Sylvester';
first_names.DELETE('Liv');
first_names.DELETE;
-- Only DML statements are bin-logged
first_names('Monty') := 'Michael'; first_names('Monty') := 'Michael';
INSERT INTO t1 VALUES (first_names('Monty')); INSERT INTO t1 VALUES (first_names('Monty'));
INSERT INTO t1 VALUES (first_names(nick)); INSERT INTO t1 VALUES (first_names(nick));

View File

@@ -45,6 +45,13 @@ DECLARE
first_names first_names_t; first_names first_names_t;
nick VARCHAR(64):= 'Monty'; nick VARCHAR(64):= 'Monty';
BEGIN BEGIN
-- Make sure the method "DELETE" is not bin-logged per se
first_names('Liv') := 'Liver';
first_names('Sly') := 'Sylvester';
first_names.DELETE('Liv');
first_names.DELETE;
-- Only DML statements are bin-logged
first_names('Monty') := 'Michael'; first_names('Monty') := 'Michael';
INSERT INTO t1 VALUES (first_names('Monty')); INSERT INTO t1 VALUES (first_names('Monty'));

View File

@@ -0,0 +1,59 @@
#
# MDEV-34319 DECLARE TYPE .. TABLE OF .. INDEX BY in stored routines
#
set sql_mode=oracle;
SET NAMES utf8mb4;
#
# Checking memory_used increment when inserting into an assoc array.
# Checking that when the routine execution leaves
# a DECLARE..BEGIN..END block with an assoc array declared,
# the memory used by the assoc array is freed.
#
CREATE FUNCTION memory_used() RETURN BIGINT AS
BEGIN
RETURN (SELECT variable_value FROM INFORMATION_SCHEMA.SESSION_STATUS
WHERE variable_name='memory_used');
END;
/
CREATE PROCEDURE p1 AS
memory_used0 BIGINT:= memory_used();
BEGIN
DECLARE
TYPE person_t IS RECORD
(
first_name VARCHAR(2048),
last_name VARCHAR(2048)
);
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('p0'):= person_t('','');
SELECT 'p0' AS id, memory_used()-memory_used0 AS diff;
person_by_nickname('p1'):= person_t('first_name', 'last_name');
SELECT 'p1' AS id, memory_used()-memory_used0 AS diff;
person_by_nickname('p2'):= person_t(CONCAT('first_name', REPEAT('a',1024)),
CONCAT('last_name', REPEAT('a',1024)));
SELECT 'p2' AS id, memory_used()-memory_used0 AS diff;
person_by_nickname('p2'):= person_t('first_name', 'last_name');
SELECT 'p2upd' AS id, memory_used()-memory_used0 AS diff;
person_by_nickname.delete('p2');
SELECT 'p2del' AS id, memory_used()-memory_used0 AS diff;
END;
SELECT memory_used()-memory_used0 AS diff1;
END;
/
CALL p1;
id diff
p0 176
id diff
p1 376
id diff
p2 2624
id diff
p2upd 2624
id diff
p2del 376
diff1
0
DROP PROCEDURE p1;
DROP FUNCTION memory_used;

View File

@@ -0,0 +1,58 @@
--source include/have_64bit.inc
--echo #
--echo # MDEV-34319 DECLARE TYPE .. TABLE OF .. INDEX BY in stored routines
--echo #
set sql_mode=oracle;
SET NAMES utf8mb4;
--echo #
--echo # Checking memory_used increment when inserting into an assoc array.
--echo # Checking that when the routine execution leaves
--echo # a DECLARE..BEGIN..END block with an assoc array declared,
--echo # the memory used by the assoc array is freed.
--echo #
DELIMITER /;
CREATE FUNCTION memory_used() RETURN BIGINT AS
BEGIN
RETURN (SELECT variable_value FROM INFORMATION_SCHEMA.SESSION_STATUS
WHERE variable_name='memory_used');
END;
/
CREATE PROCEDURE p1 AS
memory_used0 BIGINT:= memory_used();
BEGIN
DECLARE
TYPE person_t IS RECORD
(
first_name VARCHAR(2048),
last_name VARCHAR(2048)
);
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('p0'):= person_t('','');
SELECT 'p0' AS id, memory_used()-memory_used0 AS diff;
person_by_nickname('p1'):= person_t('first_name', 'last_name');
SELECT 'p1' AS id, memory_used()-memory_used0 AS diff;
person_by_nickname('p2'):= person_t(CONCAT('first_name', REPEAT('a',1024)),
CONCAT('last_name', REPEAT('a',1024)));
SELECT 'p2' AS id, memory_used()-memory_used0 AS diff;
person_by_nickname('p2'):= person_t('first_name', 'last_name');
SELECT 'p2upd' AS id, memory_used()-memory_used0 AS diff;
person_by_nickname.delete('p2');
SELECT 'p2del' AS id, memory_used()-memory_used0 AS diff;
END;
SELECT memory_used()-memory_used0 AS diff1;
END;
/
DELIMITER ;/
CALL p1;
DROP PROCEDURE p1;
DROP FUNCTION memory_used;

View File

@@ -0,0 +1,188 @@
#
# MDEV-34319 DECLARE TYPE .. TABLE OF .. INDEX BY in stored routines
#
set sql_mode=oracle;
SET NAMES utf8mb4;
#
# Passing an expression of a wrong type as a key: POINT
#
CREATE PROCEDURE p1 AS
TYPE marks_t IS TABLE OF VARCHAR(20) INDEX BY INT;
marks marks_t:= marks_t(1 => 'x43', 2 => 'x99');
BEGIN
SELECT marks(POINT(1,1));
END;
$$
CALL p1;
ERROR HY000: Illegal parameter data type point for operation '<subscript expression>'
DROP PROCEDURE p1;
CREATE PROCEDURE p1 AS
TYPE marks_t IS TABLE OF NUMBER INDEX BY INTEGER;
marks marks_t;
BEGIN
SELECT 1 INTO marks(POINT(1,1));
END;
$$
CALL p1;
ERROR HY000: Illegal parameter data type point for operation '<subscript expression>'
DROP PROCEDURE p1;
CREATE PROCEDURE p1 AS
TYPE marks_t IS TABLE OF NUMBER INDEX BY INTEGER;
marks marks_t;
BEGIN
marks(POINT(1,1)) := 1;
END;
$$
CALL p1;
ERROR HY000: Illegal parameter data type point for operation '<subscript expression>'
DROP PROCEDURE p1;
#
# Passing an expression of a wrong type as a key: ROW
#
CREATE PROCEDURE p1 AS
TYPE marks_t IS TABLE OF VARCHAR(20) INDEX BY INT;
marks marks_t:= marks_t(1 => 'x43', 2 => 'x99');
BEGIN
SELECT marks(ROW(1,1));
END;
$$
CALL p1;
ERROR HY000: Illegal parameter data type row for operation '<subscript expression>'
DROP PROCEDURE p1;
CREATE PROCEDURE p1 AS
TYPE marks_t IS TABLE OF NUMBER INDEX BY INTEGER;
marks marks_t;
BEGIN
SELECT 1 INTO marks(ROW(1,1));
END;
$$
CALL p1;
ERROR HY000: Illegal parameter data type row for operation '<subscript expression>'
DROP PROCEDURE p1;
CREATE PROCEDURE p1 AS
TYPE marks_t IS TABLE OF NUMBER INDEX BY INTEGER;
marks marks_t;
BEGIN
marks(ROW(1,1)) := 1;
END;
$$
CALL p1;
ERROR HY000: Illegal parameter data type row for operation '<subscript expression>'
DROP PROCEDURE p1;
#
# Passing an expression of a wrong type as a key: anchored POINT
#
CREATE TABLE t1 (a POINT(1,1));
CREATE PROCEDURE p1 AS
TYPE marks_t IS TABLE OF VARCHAR(20) INDEX BY INT;
marks marks_t:= marks_t(1 => 'x43', 2 => 'x99');
idx t1.a%TYPE;
BEGIN
SELECT marks(idx);
END;
$$
CALL p1;
ERROR HY000: Illegal parameter data type point for operation '<subscript expression>'
DROP PROCEDURE p1;
DROP TABLE t1;
CREATE TABLE t1 (a POINT(1,1));
CREATE PROCEDURE p1 AS
TYPE marks_t IS TABLE OF VARCHAR(20) INDEX BY INT;
marks marks_t:= marks_t(1 => 'x43', 2 => 'x99');
idx t1.a%TYPE;
BEGIN
SELECT 1 INTO marks(idx);
END;
$$
CALL p1;
ERROR HY000: Illegal parameter data type point for operation '<subscript expression>'
DROP PROCEDURE p1;
DROP TABLE t1;
CREATE TABLE t1 (a POINT(1,1));
CREATE PROCEDURE p1 AS
TYPE marks_t IS TABLE OF VARCHAR(20) INDEX BY INT;
marks marks_t:= marks_t(1 => 'x43', 2 => 'x99');
idx t1.a%TYPE;
BEGIN
marks(idx):= 1;
END;
$$
CALL p1;
ERROR HY000: Illegal parameter data type point for operation '<subscript expression>'
DROP PROCEDURE p1;
DROP TABLE t1;
#
# Passing an expression of a wrong type as a key: anchored ROW
#
CREATE TABLE t1 (a INT, b INT);
CREATE PROCEDURE p1 AS
TYPE marks_t IS TABLE OF VARCHAR(20) INDEX BY INT;
marks marks_t:= marks_t(1 => 'x43', 2 => 'x99');
idx t1%ROWTYPE;
BEGIN
SELECT marks(idx);
END;
$$
CALL p1;
ERROR HY000: Illegal parameter data type row for operation '<subscript expression>'
DROP PROCEDURE p1;
DROP TABLE t1;
CREATE TABLE t1 (a INT, b INT);
CREATE PROCEDURE p1 AS
TYPE marks_t IS TABLE OF VARCHAR(20) INDEX BY INT;
marks marks_t:= marks_t(1 => 'x43', 2 => 'x99');
idx t1%ROWTYPE;
BEGIN
SELECT 1 INTO marks(idx);
END;
$$
CALL p1;
ERROR HY000: Illegal parameter data type row for operation '<subscript expression>'
DROP PROCEDURE p1;
DROP TABLE t1;
CREATE TABLE t1 (a INT, b INT);
CREATE PROCEDURE p1 AS
TYPE marks_t IS TABLE OF VARCHAR(20) INDEX BY INT;
marks marks_t:= marks_t(1 => 'x43', 2 => 'x99');
idx t1%ROWTYPE;
BEGIN
marks(idx):= 1;
END;
$$
CALL p1;
ERROR HY000: Illegal parameter data type row for operation '<subscript expression>'
DROP PROCEDURE p1;
DROP TABLE t1;
#
# Passing a stored function as a key
#
CREATE FUNCTION f1 RETURN INT AS
BEGIN
RETURN 1;
END;
$$
CREATE PROCEDURE p1 AS
TYPE marks_t IS TABLE OF VARCHAR(20) INDEX BY INT;
marks marks_t:= marks_t(1 => 'x43', 2 => 'x99');
BEGIN
SELECT marks(f1());
END;
$$
ERROR HY000: '"f1"()' is not allowed in this context
CREATE PROCEDURE p1 AS
TYPE marks_t IS TABLE OF NUMBER INDEX BY INTEGER;
marks marks_t;
BEGIN
SELECT 1 INTO marks(f1());
END;
$$
ERROR HY000: '"f1"()' is not allowed in this context
CREATE PROCEDURE p1 AS
TYPE marks_t IS TABLE OF NUMBER INDEX BY INTEGER;
marks marks_t;
BEGIN
marks(f1()) := 1;
END;
$$
ERROR HY000: '"f1"()' is not allowed in this context
DROP FUNCTION f1;

View File

@@ -0,0 +1,248 @@
--echo #
--echo # MDEV-34319 DECLARE TYPE .. TABLE OF .. INDEX BY in stored routines
--echo #
set sql_mode=oracle;
SET NAMES utf8mb4;
--echo #
--echo # Passing an expression of a wrong type as a key: POINT
--echo #
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE marks_t IS TABLE OF VARCHAR(20) INDEX BY INT;
marks marks_t:= marks_t(1 => 'x43', 2 => 'x99');
BEGIN
SELECT marks(POINT(1,1));
END;
$$
DELIMITER ;$$
--error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION
CALL p1;
DROP PROCEDURE p1;
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE marks_t IS TABLE OF NUMBER INDEX BY INTEGER;
marks marks_t;
BEGIN
SELECT 1 INTO marks(POINT(1,1));
END;
$$
DELIMITER ;$$
--error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION
CALL p1;
DROP PROCEDURE p1;
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE marks_t IS TABLE OF NUMBER INDEX BY INTEGER;
marks marks_t;
BEGIN
marks(POINT(1,1)) := 1;
END;
$$
DELIMITER ;$$
--error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION
CALL p1;
DROP PROCEDURE p1;
--echo #
--echo # Passing an expression of a wrong type as a key: ROW
--echo #
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE marks_t IS TABLE OF VARCHAR(20) INDEX BY INT;
marks marks_t:= marks_t(1 => 'x43', 2 => 'x99');
BEGIN
SELECT marks(ROW(1,1));
END;
$$
DELIMITER ;$$
--error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION
CALL p1;
DROP PROCEDURE p1;
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE marks_t IS TABLE OF NUMBER INDEX BY INTEGER;
marks marks_t;
BEGIN
SELECT 1 INTO marks(ROW(1,1));
END;
$$
DELIMITER ;$$
--error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION
CALL p1;
DROP PROCEDURE p1;
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE marks_t IS TABLE OF NUMBER INDEX BY INTEGER;
marks marks_t;
BEGIN
marks(ROW(1,1)) := 1;
END;
$$
DELIMITER ;$$
--error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION
CALL p1;
DROP PROCEDURE p1;
--echo #
--echo # Passing an expression of a wrong type as a key: anchored POINT
--echo #
CREATE TABLE t1 (a POINT(1,1));
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE marks_t IS TABLE OF VARCHAR(20) INDEX BY INT;
marks marks_t:= marks_t(1 => 'x43', 2 => 'x99');
idx t1.a%TYPE;
BEGIN
SELECT marks(idx);
END;
$$
DELIMITER ;$$
--error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION
CALL p1;
DROP PROCEDURE p1;
DROP TABLE t1;
CREATE TABLE t1 (a POINT(1,1));
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE marks_t IS TABLE OF VARCHAR(20) INDEX BY INT;
marks marks_t:= marks_t(1 => 'x43', 2 => 'x99');
idx t1.a%TYPE;
BEGIN
SELECT 1 INTO marks(idx);
END;
$$
DELIMITER ;$$
--error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION
CALL p1;
DROP PROCEDURE p1;
DROP TABLE t1;
CREATE TABLE t1 (a POINT(1,1));
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE marks_t IS TABLE OF VARCHAR(20) INDEX BY INT;
marks marks_t:= marks_t(1 => 'x43', 2 => 'x99');
idx t1.a%TYPE;
BEGIN
marks(idx):= 1;
END;
$$
DELIMITER ;$$
--error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION
CALL p1;
DROP PROCEDURE p1;
DROP TABLE t1;
--echo #
--echo # Passing an expression of a wrong type as a key: anchored ROW
--echo #
CREATE TABLE t1 (a INT, b INT);
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE marks_t IS TABLE OF VARCHAR(20) INDEX BY INT;
marks marks_t:= marks_t(1 => 'x43', 2 => 'x99');
idx t1%ROWTYPE;
BEGIN
SELECT marks(idx);
END;
$$
DELIMITER ;$$
--error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION
CALL p1;
DROP PROCEDURE p1;
DROP TABLE t1;
CREATE TABLE t1 (a INT, b INT);
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE marks_t IS TABLE OF VARCHAR(20) INDEX BY INT;
marks marks_t:= marks_t(1 => 'x43', 2 => 'x99');
idx t1%ROWTYPE;
BEGIN
SELECT 1 INTO marks(idx);
END;
$$
DELIMITER ;$$
--error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION
CALL p1;
DROP PROCEDURE p1;
DROP TABLE t1;
CREATE TABLE t1 (a INT, b INT);
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE marks_t IS TABLE OF VARCHAR(20) INDEX BY INT;
marks marks_t:= marks_t(1 => 'x43', 2 => 'x99');
idx t1%ROWTYPE;
BEGIN
marks(idx):= 1;
END;
$$
DELIMITER ;$$
--error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION
CALL p1;
DROP PROCEDURE p1;
DROP TABLE t1;
--echo #
--echo # Passing a stored function as a key
--echo #
DELIMITER $$;
CREATE FUNCTION f1 RETURN INT AS
BEGIN
RETURN 1;
END;
$$
DELIMITER ;$$
DELIMITER $$;
--error ER_NOT_ALLOWED_IN_THIS_CONTEXT
CREATE PROCEDURE p1 AS
TYPE marks_t IS TABLE OF VARCHAR(20) INDEX BY INT;
marks marks_t:= marks_t(1 => 'x43', 2 => 'x99');
BEGIN
SELECT marks(f1());
END;
$$
DELIMITER ;$$
DELIMITER $$;
--error ER_NOT_ALLOWED_IN_THIS_CONTEXT
CREATE PROCEDURE p1 AS
TYPE marks_t IS TABLE OF NUMBER INDEX BY INTEGER;
marks marks_t;
BEGIN
SELECT 1 INTO marks(f1());
END;
$$
DELIMITER ;$$
DELIMITER $$;
--error ER_NOT_ALLOWED_IN_THIS_CONTEXT
CREATE PROCEDURE p1 AS
TYPE marks_t IS TABLE OF NUMBER INDEX BY INTEGER;
marks marks_t;
BEGIN
marks(f1()) := 1;
END;
$$
DELIMITER ;$$
DROP FUNCTION f1;

View File

@@ -1,4 +1,5 @@
SET sql_mode=ORACLE; SET sql_mode=ORACLE;
SET NAMES utf8mb4;
# #
# MDEV-34319 DECLARE TYPE .. TABLE OF .. INDEX BY in stored routines # MDEV-34319 DECLARE TYPE .. TABLE OF .. INDEX BY in stored routines
# #
@@ -105,3 +106,100 @@ Pos Instruction
18 destruct associative_array assoc_of_record_0@0 18 destruct associative_array assoc_of_record_0@0
19 stmt 0 "SELECT '<block#0' AS comment" 19 stmt 0 "SELECT '<block#0' AS comment"
DROP PROCEDURE p1; DROP PROCEDURE p1;
#
# Call an associative array procedure method directly
#
CREATE PROCEDURE p1 AS
TYPE marks_t IS TABLE OF NUMBER INDEX BY INTEGER;
m1 marks_t:= marks_t(1 => 61, 2 => 62, 3 => 63);
BEGIN
SELECT m1.count;
m1.delete(2);
SELECT m1.count, m1(1), m1(3);
m1.delete;
SELECT m1.count;
END;
$$
SHOW PROCEDURE CODE p1;
Pos Instruction
0 set m1@0 marks_t('1'=>61,'2'=>62,'3'=>63)
1 stmt 0 "SELECT m1.count"
2 stmt 74 "m1.delete(2)"
3 stmt 0 "SELECT m1.count, m1(1), m1(3)"
4 stmt 74 "m1.delete"
5 stmt 0 "SELECT m1.count"
6 destruct associative_array m1@0
DROP PROCEDURE p1;
#
# Item_method_func::print()
#
CREATE PROCEDURE p1 AS
TYPE salary IS TABLE OF NUMBER INDEX BY VARCHAR2(20);
salary_list salary;
name VARCHAR2(20);
BEGIN
WHILE name IS NOT NULL
LOOP
name:= salary_list.NEXT(name);
END LOOP;
END;
$$
SHOW PROCEDURE CODE p1;
Pos Instruction
0 set salary_list@0 NULL
1 set name@1 NULL
2 jump_if_not 5(5) name@1 is not null
3 set name@1 salary_list@0.next(name@1)
4 jump 2
5 destruct associative_array salary_list@0
CALL p1;
DROP PROCEDURE p1;
#
# Item_splocal_assoc_array_element::print()
#
CREATE PROCEDURE p1 AS
TYPE salary IS TABLE OF NUMBER INDEX BY VARCHAR2(20);
salary_list salary:= salary('Rajnisj'=> 62000);
v0 NUMBER;
BEGIN
v0:= salary_list('Rajnisj');
SELECT salary_list('Rajnisj') AS c0, v0;
END;
$$
SHOW PROCEDURE CODE p1;
Pos Instruction
0 set salary_list@0 salary('Rajnisj'=>62000)
1 set v0@1 NULL
2 set v0@1 salary_list@0['Rajnisj']
3 stmt 0 "SELECT salary_list('Rajnisj') AS c0, v0"
4 destruct associative_array salary_list@0
CALL p1;
c0 v0
62000 62000
DROP PROCEDURE p1;
#
# Item_splocal_assoc_array_element_field::print()
#
CREATE PROCEDURE p1 AS
TYPE person_t IS RECORD
(
first_name VARCHAR(64),
last_name VARCHAR(64)
);
TYPE list_t IS TABLE OF person_t INDEX BY INT;
list list_t := list_t(1=>person_t('Agnetha', 'Faltskog'));
BEGIN
list(1).last_name:= REPLACE(list(1).last_name,'Fa','Fä');
SELECT list(1).first_name AS first_name, list(1).last_name AS last_name;
END;
$$
SHOW PROCEDURE CODE p1;
Pos Instruction
0 set list@0 list_t('1'=>('Agnetha','Faltskog'))
1 set list@0[1].last_name replace(list@0[1].last_name,'Fa','Fä')
2 stmt 0 "SELECT list(1).first_name AS first_na..."
3 destruct associative_array list@0
CALL p1;
first_name last_name
Agnetha Fältskog
DROP PROCEDURE p1;

View File

@@ -1,6 +1,7 @@
-- source include/have_debug.inc -- source include/have_debug.inc
SET sql_mode=ORACLE; SET sql_mode=ORACLE;
SET NAMES utf8mb4;
--echo # --echo #
--echo # MDEV-34319 DECLARE TYPE .. TABLE OF .. INDEX BY in stored routines --echo # MDEV-34319 DECLARE TYPE .. TABLE OF .. INDEX BY in stored routines
@@ -87,3 +88,90 @@ $$
DELIMITER ;$$ DELIMITER ;$$
SHOW PROCEDURE CODE p1; SHOW PROCEDURE CODE p1;
DROP PROCEDURE p1; DROP PROCEDURE p1;
--echo #
--echo # Call an associative array procedure method directly
--echo #
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE marks_t IS TABLE OF NUMBER INDEX BY INTEGER;
m1 marks_t:= marks_t(1 => 61, 2 => 62, 3 => 63);
BEGIN
SELECT m1.count;
m1.delete(2);
SELECT m1.count, m1(1), m1(3);
m1.delete;
SELECT m1.count;
END;
$$
DELIMITER ;$$
SHOW PROCEDURE CODE p1;
DROP PROCEDURE p1;
--echo #
--echo # Item_method_func::print()
--echo #
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE salary IS TABLE OF NUMBER INDEX BY VARCHAR2(20);
salary_list salary;
name VARCHAR2(20);
BEGIN
WHILE name IS NOT NULL
LOOP
name:= salary_list.NEXT(name);
END LOOP;
END;
$$
DELIMITER ;$$
SHOW PROCEDURE CODE p1;
CALL p1;
DROP PROCEDURE p1;
--echo #
--echo # Item_splocal_assoc_array_element::print()
--echo #
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE salary IS TABLE OF NUMBER INDEX BY VARCHAR2(20);
salary_list salary:= salary('Rajnisj'=> 62000);
v0 NUMBER;
BEGIN
v0:= salary_list('Rajnisj');
SELECT salary_list('Rajnisj') AS c0, v0;
END;
$$
DELIMITER ;$$
SHOW PROCEDURE CODE p1;
CALL p1;
DROP PROCEDURE p1;
--echo #
--echo # Item_splocal_assoc_array_element_field::print()
--echo #
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE person_t IS RECORD
(
first_name VARCHAR(64),
last_name VARCHAR(64)
);
TYPE list_t IS TABLE OF person_t INDEX BY INT;
list list_t := list_t(1=>person_t('Agnetha', 'Faltskog'));
BEGIN
list(1).last_name:= REPLACE(list(1).last_name,'Fa','Fä');
SELECT list(1).first_name AS first_name, list(1).last_name AS last_name;
END;
$$
DELIMITER ;$$
SHOW PROCEDURE CODE p1;
CALL p1;
DROP PROCEDURE p1;

View File

@@ -0,0 +1,382 @@
SET sql_mode=ORACLE;
SET NAMES utf8mb4;
#
# MDEV-34319 DECLARE TYPE .. TABLE OF .. INDEX BY in stored routines
#
#
# Using a key expression with a different character set
# than the one specified in the INDEX BY clause.
#
CREATE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF INT INDEX BY VARCHAR(10) COLLATE latin1_swedish_ci;
assoc assoc_t:= assoc_t('á' => 10);
BEGIN
assoc('ó'):=20;
SELECT assoc('A') AS c1; -- should find 10
SELECT assoc('Á') AS c1; -- should find 10
SELECT assoc('O') AS c1; -- should find 20
SELECT assoc('Ó') AS c1; -- should find 20
END;
$$
CALL p1;
c1
10
c1
10
c1
20
c1
20
DROP PROCEDURE p1;
#
# Check that methods (exists, delete, next)
# work when the passed key has a different character set
# than the key character set specified in INDEX BY
#
CREATE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF INT INDEX BY VARCHAR(10) COLLATE latin1_swedish_ci;
assoc_latin1 assoc_t:= assoc_t('ß'=>0xDF,'á'=>0xE1,'é'=>0xE9,'ó'=>0xF3,'ú'=>0xFA);
key_utf8mb4 VARCHAR(10) COLLATE utf8mb4_uca1400_ai_ci;
BEGIN
key_utf8mb4:='ó';
SELECT assoc_latin1.exists(key_utf8mb4);
assoc_latin1.delete(key_utf8mb4);
SELECT assoc_latin1.exists(key_utf8mb4);
CREATE TEMPORARY TABLE t1 (id INT AUTO_INCREMENT PRIMARY KEY,
k VARCHAR(10) COLLATE utf8mb4_uca1400_ai_ci,
val INT);
key_utf8mb4:= assoc_latin1.first;
WHILE key_utf8mb4 IS NOT NULL
LOOP
INSERT INTO t1 (k, val) VALUES (key_utf8mb4, assoc_latin1(key_utf8mb4));
key_utf8mb4:= assoc_latin1.NEXT(key_utf8mb4);
END LOOP;
SELECT id, k, HEX(val) AS val FROM t1;
DROP TEMPORARY TABLE t1;
END;
$$
CALL p1;
assoc_latin1.exists(key_utf8mb4)
1
assoc_latin1.exists(key_utf8mb4)
0
id k val
1 á E1
2 é E9
3 ú FA
4 ß DF
DROP PROCEDURE p1;
#
# Assigning arrays with different key character sets and collations
# Dumping using a key variable of different characters set and collations
#
CREATE PROCEDURE p1 AS
TYPE assoc_latin1_t IS TABLE OF INT INDEX BY VARCHAR(10) COLLATE latin1_swedish_ci;
TYPE assoc_utf8mb4_t IS TABLE OF INT INDEX BY VARCHAR(10) COLLATE utf8mb4_uca1400_ai_ci;
assoc_latin1 assoc_latin1_t;
assoc_utf8mb4 assoc_utf8mb4_t;
key_latin1 VARCHAR(10) COLLATE latin1_swedish_ci;
key_utf8mb4 VARCHAR(10) COLLATE utf8mb4_uca1400_ai_ci;
BEGIN
SELECT '# Populating assoc_latin1' AS ``;
assoc_latin1('ß'):=0xDF;
assoc_latin1('á'):=0xE1;
assoc_latin1('é'):=0xE9;
assoc_latin1('ó'):=0xF3;
assoc_latin1('ú'):=0xFA;
SELECT '# Copying to assoc_utf8mb4 from assoc_latin1' AS ``;
assoc_utf8mb4:=assoc_latin1;
CREATE TEMPORARY TABLE t1 (id INT AUTO_INCREMENT PRIMARY KEY,
k VARCHAR(10) COLLATE utf8mb4_uca1400_ai_ci,
val INT);
SELECT '# Dumping assoc_latin1 using key_latin1. The expected order: á é ó ú ß' AS ``;
key_latin1:= assoc_latin1.first;
WHILE key_latin1 IS NOT NULL
LOOP
INSERT INTO t1 (k, val) VALUES (key_latin1, assoc_latin1(key_latin1));
key_latin1:= assoc_latin1.NEXT(key_latin1);
END LOOP;
SELECT id, k, HEX(val) AS val FROM t1;
TRUNCATE TABLE t1;
SELECT '# Dumping assoc_latin1 using key_utf8mb4. The expected order: á é ó ú ß' AS ``;
key_utf8mb4:= assoc_latin1.first;
WHILE key_utf8mb4 IS NOT NULL
LOOP
INSERT INTO t1 (k, val) VALUES (key_utf8mb4, assoc_latin1(key_utf8mb4));
key_utf8mb4:= assoc_latin1.NEXT(key_utf8mb4);
END LOOP;
SELECT id, k, HEX(val) AS val FROM t1;
TRUNCATE TABLE t1;
SELECT '# Dumping assoc_utf8mb4 using key_utf8mb4. The expected order: á é ó ß ú' AS ``;
key_utf8mb4:= assoc_utf8mb4.first;
WHILE key_utf8mb4 IS NOT NULL
LOOP
INSERT INTO t1 (k,val) VALUES (key_utf8mb4, assoc_utf8mb4(key_utf8mb4));
key_utf8mb4:= assoc_utf8mb4.NEXT(key_utf8mb4);
END LOOP;
SELECT id, k, HEX(val) AS val FROM t1;
TRUNCATE TABLE t1;
SELECT '# Dumping assoc_utf8mb4 using key_latin1. The expected order: á é ó ß ú' AS ``;
key_latin1:= assoc_utf8mb4.first;
WHILE key_latin1 IS NOT NULL
LOOP
INSERT INTO t1 (k,val) VALUES (key_latin1, assoc_utf8mb4(key_latin1));
key_latin1:= assoc_utf8mb4.NEXT(key_latin1);
END LOOP;
SELECT id, k, HEX(val) AS val FROM t1;
TRUNCATE TABLE t1;
DROP TEMPORARY TABLE t1;
END;
$$
CALL p1;
# Populating assoc_latin1
# Copying to assoc_utf8mb4 from assoc_latin1
# Dumping assoc_latin1 using key_latin1. The expected order: á é ó ú ß
id k val
1 á E1
2 é E9
3 ó F3
4 ú FA
5 ß DF
# Dumping assoc_latin1 using key_utf8mb4. The expected order: á é ó ú ß
id k val
1 á E1
2 é E9
3 ó F3
4 ú FA
5 ß DF
# Dumping assoc_utf8mb4 using key_utf8mb4. The expected order: á é ó ß ú
id k val
1 á E1
2 é E9
3 ó F3
4 ß DF
5 ú FA
# Dumping assoc_utf8mb4 using key_latin1. The expected order: á é ó ß ú
id k val
1 á E1
2 é E9
3 ó F3
4 ß DF
5 ú FA
DROP PROCEDURE p1;
#
# Assigning arrays with different key character sets and collations
# Some values can cause a collision during the assignment, e.g.:
# 'ü' is not equal to 'y' in utf8mb4_uca1400_as_ci
# 'ü' is equal to 'y' in latin1_swedish_ci
# 'y' disappears after the assignment, because it sorts after 'ü' in utf8mb4_uca1400_as_ci
#
CREATE PROCEDURE p1 AS
TYPE assoc_latin1_t IS TABLE OF INT INDEX BY VARCHAR(10) COLLATE latin1_swedish_ci;
TYPE assoc_utf8mb4_t IS TABLE OF INT INDEX BY VARCHAR(10) COLLATE utf8mb4_uca1400_as_ci;
assoc_latin1 assoc_latin1_t;
assoc_utf8mb4 assoc_utf8mb4_t:= assoc_utf8mb4_t('å'=>0xC5,'ö'=>0xF6,'ü'=>0xFC,
'y'=>0x79,'z'=>0x7A);
key_utf8mb4 VARCHAR(10) COLLATE utf8mb4_uca1400_ai_ci;
BEGIN
SELECT '# Copying to assoc_latin1 from assoc_utf8mb4' AS ``;
assoc_latin1:= assoc_utf8mb4;
SHOW WARNINGS;
SELECT assoc_utf8mb4.count, assoc_latin1.count;
CREATE TEMPORARY TABLE t1 (id INT AUTO_INCREMENT PRIMARY KEY,
k VARCHAR(10) COLLATE utf8mb4_uca1400_ai_ci,
val INT);
SELECT '# Dumping assoc_utf8mb4 using key_utf8mb4' AS ``;
key_utf8mb4:= assoc_utf8mb4.first;
WHILE key_utf8mb4 IS NOT NULL
LOOP
INSERT INTO t1 (k, val) VALUES (key_utf8mb4, assoc_utf8mb4(key_utf8mb4));
key_utf8mb4:= assoc_utf8mb4.NEXT(key_utf8mb4);
END LOOP;
SELECT id, k, HEX(val) AS val FROM t1;
TRUNCATE TABLE t1;
SELECT '# Dumping assoc_latin1 using key_utf8mb4' AS ``;
key_utf8mb4:= assoc_latin1.first;
WHILE key_utf8mb4 IS NOT NULL
LOOP
INSERT INTO t1 (k, val) VALUES (key_utf8mb4, assoc_latin1(key_utf8mb4));
key_utf8mb4:= assoc_latin1.NEXT(key_utf8mb4);
END LOOP;
SELECT id, k, HEX(val) AS val FROM t1;
DROP TEMPORARY TABLE t1;
END;
$$
CALL p1;
# Copying to assoc_latin1 from assoc_utf8mb4
Level Code Message
Warning 1859 Duplicate entry for key 'y'
assoc_utf8mb4.count assoc_latin1.count
5 4
# Dumping assoc_utf8mb4 using key_utf8mb4
id k val
1 å C5
2 ö F6
3 ü FC
4 y 79
5 z 7A
# Dumping assoc_latin1 using key_utf8mb4
id k val
1 ü FC
2 z 7A
3 å C5
4 ö F6
DROP PROCEDURE p1;
#
# Copying to a shorter key data type
# Oracle does not allow this: expression is of wrong type
# MariaDB allows but returns an error if some key does not fit
#
CREATE PROCEDURE p1 AS
TYPE assoc_latin1_t IS TABLE OF INT INDEX BY VARCHAR(1) COLLATE latin1_swedish_ci;
TYPE assoc_utf8mb4_t IS TABLE OF INT INDEX BY VARCHAR(10) COLLATE utf8mb4_uca1400_as_ci;
assoc_latin1 assoc_latin1_t;
assoc_utf8mb4 assoc_utf8mb4_t:= assoc_utf8mb4_t('å'=>0xC5,'öö'=>0xF6,'ü'=>0xFC,
'y'=>0x79,'zz'=>0x7A);
key_utf8mb4 VARCHAR(10) COLLATE utf8mb4_uca1400_ai_ci;
BEGIN
SELECT '# Copying to assoc_latin1 from assoc_utf8mb4' AS ``;
assoc_latin1:= assoc_utf8mb4;
END;
$$
CALL p1;
# Copying to assoc_latin1 from assoc_utf8mb4
ERROR HY000: String 'öö' is too long for INDEX BY (should be no longer than 1)
DROP PROCEDURE p1;
#
# Using 2-byte characters in the key
#
CREATE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF INT INDEX BY VARCHAR(10) COLLATE uca1400_ai_ci;
assoc assoc_t:= assoc_t('áááááááááá' => 10);
BEGIN
SELECT assoc('AAAAAAAAAA'); -- should find
END;
$$
CALL p1;
assoc('AAAAAAAAAA')
10
DROP PROCEDURE p1;
#
# Using 3-byte characters in the key
#
CREATE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF INT INDEX BY VARCHAR(10) COLLATE uca1400_ai_ci;
assoc assoc_t:= assoc_t('ḁḁḁḁḁḁḁḁḁḁ' => 10);
BEGIN
SELECT assoc('AAAAAAAAAA'); -- should find
END;
$$
CALL p1;
assoc('AAAAAAAAAA')
10
DROP PROCEDURE p1;
#
# Using 4-byte characters in the key
#
CREATE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF INT INDEX BY VARCHAR(10) COLLATE uca1400_ai_ci;
assoc assoc_t;
BEGIN
assoc('🅰🅰🅰🅰🅰🅰🅰🅰🅰🅰'):=10;
SELECT assoc('AAAAAAAAAA'); -- should find
END;
$$
CALL p1;
assoc('AAAAAAAAAA')
10
DROP PROCEDURE p1;
#
# Passing a too long key
#
CREATE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF INT INDEX BY VARCHAR(10) COLLATE uca1400_ai_ci;
assoc assoc_t:= assoc_t('ááááááááááá' => 10);
BEGIN
NULL;
END;
$$
CALL p1;
ERROR HY000: String 'ááááááááááá' is too long for INDEX BY (should be no longer than 10)
DROP PROCEDURE p1;
#
# Using a key which cannot be converted to the key character set
# The non-convertable part is inside the VARCHAR length
#
CREATE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF INT INDEX BY VARCHAR(10) COLLATE latin1_swedish_ci;
assoc assoc_t:= assoc_t('áááááááááя' => 10); -- Nine á + я
BEGIN
NULL;
END;
$$
CALL p1;
ERROR HY000: Cannot convert 'utf8mb3' character 0xD18F to 'latin1'
DROP PROCEDURE p1;
#
# Using a key which cannot be converted to the key data type
# The non-convertable part is outside the VARCHAR length
#
CREATE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF INT INDEX BY VARCHAR(10) COLLATE latin1_swedish_ci;
assoc assoc_t:= assoc_t('ááááááááááя' => 10); -- Ten á + я
BEGIN
NULL;
END;
$$
CALL p1;
ERROR HY000: String 'ááááááááááя' is too long for INDEX BY (should be no longer than 10)
DROP PROCEDURE p1;
#
# A binary key re-interpretting as utf8mb4
#
CREATE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF INT INDEX BY VARCHAR(10) COLLATE uca1400_ai_ci;
assoc assoc_t;
BEGIN
assoc(0x61616161616161616161):=10;
SELECT assoc('AAAAAAAAAA') AS c1, assoc(0x41414141414141414141) AS c2;
END;
$$
CALL p1;
c1 c2
10 10
DROP PROCEDURE p1;
#
# A binary key re-interpretting as utf8mb4
# with a bad utf8mb4 sequence inside the VARCHAR length
#
CREATE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF INT INDEX BY VARCHAR(10) COLLATE uca1400_ai_ci;
assoc assoc_t;
BEGIN
assoc(0x616161616161616161FF):=10; -- Nine 0x61 + 0xFF
END;
$$
CALL p1;
ERROR HY000: Invalid utf8mb4 character string: '\xFF'
DROP PROCEDURE p1;
#
# A binary key re-interpretting as utf8mb4
# with a bad utf8mb4 sequence outside the VARCHAR length
#
CREATE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF INT INDEX BY VARCHAR(10) COLLATE uca1400_ai_ci;
assoc assoc_t;
BEGIN
assoc(0x61616161616161616161FF):=10; -- Ten 0x61 + 0xFF
END;
$$
CALL p1;
ERROR HY000: String 'aaaaaaaaaa\xFF' is too long for INDEX BY (should be no longer than 10)
DROP PROCEDURE p1;

View File

@@ -0,0 +1,377 @@
SET sql_mode=ORACLE;
SET NAMES utf8mb4;
--echo #
--echo # MDEV-34319 DECLARE TYPE .. TABLE OF .. INDEX BY in stored routines
--echo #
--echo #
--echo # Using a key expression with a different character set
--echo # than the one specified in the INDEX BY clause.
--echo #
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF INT INDEX BY VARCHAR(10) COLLATE latin1_swedish_ci;
assoc assoc_t:= assoc_t('á' => 10);
BEGIN
assoc('ó'):=20;
SELECT assoc('A') AS c1; -- should find 10
SELECT assoc('Á') AS c1; -- should find 10
SELECT assoc('O') AS c1; -- should find 20
SELECT assoc('Ó') AS c1; -- should find 20
END;
$$
DELIMITER ;$$
CALL p1;
DROP PROCEDURE p1;
--echo #
--echo # Check that methods (exists, delete, next)
--echo # work when the passed key has a different character set
--echo # than the key character set specified in INDEX BY
--echo #
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF INT INDEX BY VARCHAR(10) COLLATE latin1_swedish_ci;
assoc_latin1 assoc_t:= assoc_t('ß'=>0xDF,'á'=>0xE1,'é'=>0xE9,'ó'=>0xF3,'ú'=>0xFA);
key_utf8mb4 VARCHAR(10) COLLATE utf8mb4_uca1400_ai_ci;
BEGIN
key_utf8mb4:='ó';
SELECT assoc_latin1.exists(key_utf8mb4);
assoc_latin1.delete(key_utf8mb4);
SELECT assoc_latin1.exists(key_utf8mb4);
CREATE TEMPORARY TABLE t1 (id INT AUTO_INCREMENT PRIMARY KEY,
k VARCHAR(10) COLLATE utf8mb4_uca1400_ai_ci,
val INT);
key_utf8mb4:= assoc_latin1.first;
WHILE key_utf8mb4 IS NOT NULL
LOOP
INSERT INTO t1 (k, val) VALUES (key_utf8mb4, assoc_latin1(key_utf8mb4));
key_utf8mb4:= assoc_latin1.NEXT(key_utf8mb4);
END LOOP;
SELECT id, k, HEX(val) AS val FROM t1;
DROP TEMPORARY TABLE t1;
END;
$$
DELIMITER ;$$
CALL p1;
DROP PROCEDURE p1;
--echo #
--echo # Assigning arrays with different key character sets and collations
--echo # Dumping using a key variable of different characters set and collations
--echo #
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE assoc_latin1_t IS TABLE OF INT INDEX BY VARCHAR(10) COLLATE latin1_swedish_ci;
TYPE assoc_utf8mb4_t IS TABLE OF INT INDEX BY VARCHAR(10) COLLATE utf8mb4_uca1400_ai_ci;
assoc_latin1 assoc_latin1_t;
assoc_utf8mb4 assoc_utf8mb4_t;
key_latin1 VARCHAR(10) COLLATE latin1_swedish_ci;
key_utf8mb4 VARCHAR(10) COLLATE utf8mb4_uca1400_ai_ci;
BEGIN
SELECT '# Populating assoc_latin1' AS ``;
assoc_latin1('ß'):=0xDF;
assoc_latin1('á'):=0xE1;
assoc_latin1('é'):=0xE9;
assoc_latin1('ó'):=0xF3;
assoc_latin1('ú'):=0xFA;
SELECT '# Copying to assoc_utf8mb4 from assoc_latin1' AS ``;
assoc_utf8mb4:=assoc_latin1;
CREATE TEMPORARY TABLE t1 (id INT AUTO_INCREMENT PRIMARY KEY,
k VARCHAR(10) COLLATE utf8mb4_uca1400_ai_ci,
val INT);
SELECT '# Dumping assoc_latin1 using key_latin1. The expected order: á é ó ú ß' AS ``;
key_latin1:= assoc_latin1.first;
WHILE key_latin1 IS NOT NULL
LOOP
INSERT INTO t1 (k, val) VALUES (key_latin1, assoc_latin1(key_latin1));
key_latin1:= assoc_latin1.NEXT(key_latin1);
END LOOP;
SELECT id, k, HEX(val) AS val FROM t1;
TRUNCATE TABLE t1;
SELECT '# Dumping assoc_latin1 using key_utf8mb4. The expected order: á é ó ú ß' AS ``;
key_utf8mb4:= assoc_latin1.first;
WHILE key_utf8mb4 IS NOT NULL
LOOP
INSERT INTO t1 (k, val) VALUES (key_utf8mb4, assoc_latin1(key_utf8mb4));
key_utf8mb4:= assoc_latin1.NEXT(key_utf8mb4);
END LOOP;
SELECT id, k, HEX(val) AS val FROM t1;
TRUNCATE TABLE t1;
SELECT '# Dumping assoc_utf8mb4 using key_utf8mb4. The expected order: á é ó ß ú' AS ``;
key_utf8mb4:= assoc_utf8mb4.first;
WHILE key_utf8mb4 IS NOT NULL
LOOP
INSERT INTO t1 (k,val) VALUES (key_utf8mb4, assoc_utf8mb4(key_utf8mb4));
key_utf8mb4:= assoc_utf8mb4.NEXT(key_utf8mb4);
END LOOP;
SELECT id, k, HEX(val) AS val FROM t1;
TRUNCATE TABLE t1;
SELECT '# Dumping assoc_utf8mb4 using key_latin1. The expected order: á é ó ß ú' AS ``;
key_latin1:= assoc_utf8mb4.first;
WHILE key_latin1 IS NOT NULL
LOOP
INSERT INTO t1 (k,val) VALUES (key_latin1, assoc_utf8mb4(key_latin1));
key_latin1:= assoc_utf8mb4.NEXT(key_latin1);
END LOOP;
SELECT id, k, HEX(val) AS val FROM t1;
TRUNCATE TABLE t1;
DROP TEMPORARY TABLE t1;
END;
$$
DELIMITER ;$$
CALL p1;
DROP PROCEDURE p1;
--echo #
--echo # Assigning arrays with different key character sets and collations
--echo # Some values can cause a collision during the assignment, e.g.:
--echo # 'ü' is not equal to 'y' in utf8mb4_uca1400_as_ci
--echo # 'ü' is equal to 'y' in latin1_swedish_ci
--echo # 'y' disappears after the assignment, because it sorts after 'ü' in utf8mb4_uca1400_as_ci
--echo #
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE assoc_latin1_t IS TABLE OF INT INDEX BY VARCHAR(10) COLLATE latin1_swedish_ci;
TYPE assoc_utf8mb4_t IS TABLE OF INT INDEX BY VARCHAR(10) COLLATE utf8mb4_uca1400_as_ci;
assoc_latin1 assoc_latin1_t;
assoc_utf8mb4 assoc_utf8mb4_t:= assoc_utf8mb4_t('å'=>0xC5,'ö'=>0xF6,'ü'=>0xFC,
'y'=>0x79,'z'=>0x7A);
key_utf8mb4 VARCHAR(10) COLLATE utf8mb4_uca1400_ai_ci;
BEGIN
SELECT '# Copying to assoc_latin1 from assoc_utf8mb4' AS ``;
assoc_latin1:= assoc_utf8mb4;
SHOW WARNINGS;
SELECT assoc_utf8mb4.count, assoc_latin1.count;
CREATE TEMPORARY TABLE t1 (id INT AUTO_INCREMENT PRIMARY KEY,
k VARCHAR(10) COLLATE utf8mb4_uca1400_ai_ci,
val INT);
SELECT '# Dumping assoc_utf8mb4 using key_utf8mb4' AS ``;
key_utf8mb4:= assoc_utf8mb4.first;
WHILE key_utf8mb4 IS NOT NULL
LOOP
INSERT INTO t1 (k, val) VALUES (key_utf8mb4, assoc_utf8mb4(key_utf8mb4));
key_utf8mb4:= assoc_utf8mb4.NEXT(key_utf8mb4);
END LOOP;
SELECT id, k, HEX(val) AS val FROM t1;
TRUNCATE TABLE t1;
SELECT '# Dumping assoc_latin1 using key_utf8mb4' AS ``;
key_utf8mb4:= assoc_latin1.first;
WHILE key_utf8mb4 IS NOT NULL
LOOP
INSERT INTO t1 (k, val) VALUES (key_utf8mb4, assoc_latin1(key_utf8mb4));
key_utf8mb4:= assoc_latin1.NEXT(key_utf8mb4);
END LOOP;
SELECT id, k, HEX(val) AS val FROM t1;
DROP TEMPORARY TABLE t1;
END;
$$
DELIMITER ;$$
CALL p1;
DROP PROCEDURE p1;
--echo #
--echo # Copying to a shorter key data type
--echo # Oracle does not allow this: expression is of wrong type
--echo # MariaDB allows but returns an error if some key does not fit
--echo #
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE assoc_latin1_t IS TABLE OF INT INDEX BY VARCHAR(1) COLLATE latin1_swedish_ci;
TYPE assoc_utf8mb4_t IS TABLE OF INT INDEX BY VARCHAR(10) COLLATE utf8mb4_uca1400_as_ci;
assoc_latin1 assoc_latin1_t;
assoc_utf8mb4 assoc_utf8mb4_t:= assoc_utf8mb4_t('å'=>0xC5,'öö'=>0xF6,'ü'=>0xFC,
'y'=>0x79,'zz'=>0x7A);
key_utf8mb4 VARCHAR(10) COLLATE utf8mb4_uca1400_ai_ci;
BEGIN
SELECT '# Copying to assoc_latin1 from assoc_utf8mb4' AS ``;
assoc_latin1:= assoc_utf8mb4;
END;
$$
DELIMITER ;$$
--error ER_WRONG_STRING_LENGTH
CALL p1;
DROP PROCEDURE p1;
--echo #
--echo # Using 2-byte characters in the key
--echo #
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF INT INDEX BY VARCHAR(10) COLLATE uca1400_ai_ci;
assoc assoc_t:= assoc_t('áááááááááá' => 10);
BEGIN
SELECT assoc('AAAAAAAAAA'); -- should find
END;
$$
DELIMITER ;$$
CALL p1;
DROP PROCEDURE p1;
--echo #
--echo # Using 3-byte characters in the key
--echo #
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF INT INDEX BY VARCHAR(10) COLLATE uca1400_ai_ci;
assoc assoc_t:= assoc_t('ḁḁḁḁḁḁḁḁḁḁ' => 10);
BEGIN
SELECT assoc('AAAAAAAAAA'); -- should find
END;
$$
DELIMITER ;$$
CALL p1;
DROP PROCEDURE p1;
--echo #
--echo # Using 4-byte characters in the key
--echo #
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF INT INDEX BY VARCHAR(10) COLLATE uca1400_ai_ci;
assoc assoc_t;
BEGIN
assoc('🅰🅰🅰🅰🅰🅰🅰🅰🅰🅰'):=10;
SELECT assoc('AAAAAAAAAA'); -- should find
END;
$$
DELIMITER ;$$
CALL p1;
DROP PROCEDURE p1;
--echo #
--echo # Passing a too long key
--echo #
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF INT INDEX BY VARCHAR(10) COLLATE uca1400_ai_ci;
assoc assoc_t:= assoc_t('ááááááááááá' => 10);
BEGIN
NULL;
END;
$$
DELIMITER ;$$
--error ER_WRONG_STRING_LENGTH
CALL p1;
DROP PROCEDURE p1;
--echo #
--echo # Using a key which cannot be converted to the key character set
--echo # The non-convertable part is inside the VARCHAR length
--echo #
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF INT INDEX BY VARCHAR(10) COLLATE latin1_swedish_ci;
assoc assoc_t:= assoc_t('áááááááááя' => 10); -- Nine á + я
BEGIN
NULL;
END;
$$
DELIMITER ;$$
--error ER_CANNOT_CONVERT_CHARACTER
CALL p1;
DROP PROCEDURE p1;
--echo #
--echo # Using a key which cannot be converted to the key data type
--echo # The non-convertable part is outside the VARCHAR length
--echo #
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF INT INDEX BY VARCHAR(10) COLLATE latin1_swedish_ci;
assoc assoc_t:= assoc_t('ááááááááááя' => 10); -- Ten á + я
BEGIN
NULL;
END;
$$
DELIMITER ;$$
--error ER_WRONG_STRING_LENGTH
CALL p1;
DROP PROCEDURE p1;
--echo #
--echo # A binary key re-interpretting as utf8mb4
--echo #
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF INT INDEX BY VARCHAR(10) COLLATE uca1400_ai_ci;
assoc assoc_t;
BEGIN
assoc(0x61616161616161616161):=10;
SELECT assoc('AAAAAAAAAA') AS c1, assoc(0x41414141414141414141) AS c2;
END;
$$
DELIMITER ;$$
CALL p1;
DROP PROCEDURE p1;
--echo #
--echo # A binary key re-interpretting as utf8mb4
--echo # with a bad utf8mb4 sequence inside the VARCHAR length
--echo #
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF INT INDEX BY VARCHAR(10) COLLATE uca1400_ai_ci;
assoc assoc_t;
BEGIN
assoc(0x616161616161616161FF):=10; -- Nine 0x61 + 0xFF
END;
$$
DELIMITER ;$$
--error ER_INVALID_CHARACTER_STRING
CALL p1;
DROP PROCEDURE p1;
--echo #
--echo # A binary key re-interpretting as utf8mb4
--echo # with a bad utf8mb4 sequence outside the VARCHAR length
--echo #
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF INT INDEX BY VARCHAR(10) COLLATE uca1400_ai_ci;
assoc assoc_t;
BEGIN
assoc(0x61616161616161616161FF):=10; -- Ten 0x61 + 0xFF
END;
$$
DELIMITER ;$$
--error ER_WRONG_STRING_LENGTH
CALL p1;
DROP PROCEDURE p1;

View File

@@ -0,0 +1,11 @@
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF VARCHAR(10) INDEX BY VARCHAR(20);
assoc assoc_t;
BEGIN
NULL;
END;
$$
DELIMITER ;$$
CALL p1;
DROP PROCEDURE p1;

View File

@@ -0,0 +1,16 @@
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE person_t IS RECORD
(
field_name VARCHAR(100),
last_name VARCHAR(100)
);
TYPE assoc_t IS TABLE OF person_t INDEX BY VARCHAR(20);
assoc assoc_t;
BEGIN
NULL;
END;
$$
DELIMITER ;$$
CALL p1;
DROP PROCEDURE p1;

View File

@@ -0,0 +1,89 @@
SET sql_mode=ORACLE;
SET NAMES utf8mb4;
#
# MDEV-34319 DECLARE TYPE .. TABLE OF .. INDEX BY in stored routines
#
SET debug_dbug='d,assoc_array';
CREATE DATABASE test1 CHARACTER SET utf8mb4 COLLATE uca1400_ai_ci;
USE test1;
CREATE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF VARCHAR(10) INDEX BY VARCHAR(20);
assoc assoc_t;
BEGIN
NULL;
END;
$$
CALL p1;
Warnings:
Note 1003 create_fields: key: len= 80 cs=utf8mb4_uca1400_ai_ci
Note 1003 create_fields: val: len= 40 cs=utf8mb4_uca1400_ai_ci
Note 1003 create_fields: key: len= 80 cs=utf8mb4_uca1400_ai_ci
Note 1003 create_fields: val: len= 40 cs=utf8mb4_uca1400_ai_ci
DROP PROCEDURE p1;
CREATE PROCEDURE p1 AS
TYPE person_t IS RECORD
(
field_name VARCHAR(100),
last_name VARCHAR(100)
);
TYPE assoc_t IS TABLE OF person_t INDEX BY VARCHAR(20);
assoc assoc_t;
BEGIN
NULL;
END;
$$
CALL p1;
Warnings:
Note 1003 create_fields: key: len= 80 cs=utf8mb4_uca1400_ai_ci
Note 1003 create_fields: val: len= 0 cs=binary
Note 1003 create_fields: [0]: len= 400 cs=utf8mb4_uca1400_ai_ci
Note 1003 create_fields: [1]: len= 400 cs=utf8mb4_uca1400_ai_ci
Note 1003 create_fields: key: len= 80 cs=utf8mb4_uca1400_ai_ci
Note 1003 create_fields: val: len= 0 cs=binary
Note 1003 create_fields: [0]: len= 400 cs=utf8mb4_uca1400_ai_ci
Note 1003 create_fields: [1]: len= 400 cs=utf8mb4_uca1400_ai_ci
DROP PROCEDURE p1;
DROP DATABASE test1;
USE test;
CREATE DATABASE test1 CHARACTER SET latin1;
USE test1;
CREATE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF VARCHAR(10) INDEX BY VARCHAR(20);
assoc assoc_t;
BEGIN
NULL;
END;
$$
CALL p1;
Warnings:
Note 1003 create_fields: key: len= 20 cs=latin1_swedish_ci
Note 1003 create_fields: val: len= 10 cs=latin1_swedish_ci
Note 1003 create_fields: key: len= 20 cs=latin1_swedish_ci
Note 1003 create_fields: val: len= 10 cs=latin1_swedish_ci
DROP PROCEDURE p1;
CREATE PROCEDURE p1 AS
TYPE person_t IS RECORD
(
field_name VARCHAR(100),
last_name VARCHAR(100)
);
TYPE assoc_t IS TABLE OF person_t INDEX BY VARCHAR(20);
assoc assoc_t;
BEGIN
NULL;
END;
$$
CALL p1;
Warnings:
Note 1003 create_fields: key: len= 20 cs=latin1_swedish_ci
Note 1003 create_fields: val: len= 0 cs=binary
Note 1003 create_fields: [0]: len= 100 cs=latin1_swedish_ci
Note 1003 create_fields: [1]: len= 100 cs=latin1_swedish_ci
Note 1003 create_fields: key: len= 20 cs=latin1_swedish_ci
Note 1003 create_fields: val: len= 0 cs=binary
Note 1003 create_fields: [0]: len= 100 cs=latin1_swedish_ci
Note 1003 create_fields: [1]: len= 100 cs=latin1_swedish_ci
DROP PROCEDURE p1;
DROP DATABASE test1;
USE test;
SET debug_dbug=DEFAULT;

View File

@@ -0,0 +1,26 @@
-- source include/have_debug.inc
SET sql_mode=ORACLE;
SET NAMES utf8mb4;
--echo #
--echo # MDEV-34319 DECLARE TYPE .. TABLE OF .. INDEX BY in stored routines
--echo #
SET debug_dbug='d,assoc_array';
CREATE DATABASE test1 CHARACTER SET utf8mb4 COLLATE uca1400_ai_ci;
USE test1;
--source sp-assoc-array-debug-p00.inc
--source sp-assoc-array-debug-p01.inc
DROP DATABASE test1;
USE test;
CREATE DATABASE test1 CHARACTER SET latin1;
USE test1;
--source sp-assoc-array-debug-p00.inc
--source sp-assoc-array-debug-p01.inc
DROP DATABASE test1;
USE test;
SET debug_dbug=DEFAULT;

View File

@@ -0,0 +1,45 @@
SET sql_mode=oracle;
SET NAMES utf8mb4;
#
# MDEV-34319 DECLARE TYPE .. TABLE OF .. INDEX BY in stored routines
#
#
# Create a relatively big array, then search in it
#
CREATE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF VARCHAR(32) INDEX BY INT;
assoc assoc_t;
BEGIN
FOR i IN 0..1024*128
LOOP
assoc(i):= CONCAT('value', i);
END LOOP;
CREATE TEMPORARY TABLE t1 (a INT, b VARCHAR(32));
FOR i IN 0..16
LOOP
INSERT INTO t1 VALUES (i*10, assoc(i*10));
END LOOP;
SELECT * FROM t1;
DROP TEMPORARY TABLE t1;
END;
$$
CALL p1;
a b
0 value0
10 value10
20 value20
30 value30
40 value40
50 value50
60 value60
70 value70
80 value80
90 value90
100 value100
110 value110
120 value120
130 value130
140 value140
150 value150
160 value160
DROP PROCEDURE p1;

View File

@@ -0,0 +1,32 @@
SET sql_mode=oracle;
SET NAMES utf8mb4;
--echo #
--echo # MDEV-34319 DECLARE TYPE .. TABLE OF .. INDEX BY in stored routines
--echo #
--echo #
--echo # Create a relatively big array, then search in it
--echo #
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF VARCHAR(32) INDEX BY INT;
assoc assoc_t;
BEGIN
FOR i IN 0..1024*128
LOOP
assoc(i):= CONCAT('value', i);
END LOOP;
CREATE TEMPORARY TABLE t1 (a INT, b VARCHAR(32));
FOR i IN 0..16
LOOP
INSERT INTO t1 VALUES (i*10, assoc(i*10));
END LOOP;
SELECT * FROM t1;
DROP TEMPORARY TABLE t1;
END;
$$
DELIMITER ;$$
CALL p1;
DROP PROCEDURE p1;

View File

@@ -0,0 +1,28 @@
--echo #
--echo # Using a package body variable as a key: a wrong data type: POINT
--echo #
DELIMITER $$;
CREATE PACKAGE pkg AS
PROCEDURE p1;
END;
$$
CREATE PACKAGE BODY pkg AS
v POINT := POINT(1,1);
PROCEDURE p1 AS
TYPE marks_t IS TABLE OF VARCHAR(20) INDEX BY INT;
marks marks_t:= marks_t(1 => 'x43', 2 => 'x99');
BEGIN
SELECT marks(v);
marks(v):= 'x43a';
END;
END;
$$
DELIMITER ;$$
if (`SELECT @code`)
{
EXECUTE IMMEDIATE 'SHOW PROCEDURE CODE pkg.p1';
}
--error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION
CALL pkg.p1;
DROP PACKAGE pkg;

View File

@@ -0,0 +1,37 @@
--echo #
--echo # Using a package body variable as a key
--echo #
DELIMITER $$;
CREATE PACKAGE pkg AS
PROCEDURE p1;
END;
$$
CREATE PACKAGE BODY pkg AS
v1 INT := 1;
v2 INT := 2;
PROCEDURE p1 AS
TYPE marks_t IS TABLE OF VARCHAR(20) INDEX BY INT;
marks marks_t:= marks_t(1 => 'x01', 2 => 'x02');
BEGIN
SELECT marks(v1);
marks(v1):= 'x01a';
SELECT marks(v1);
SELECT 'x01b' INTO marks(v1);
SELECT marks(v1);
SELECT marks(v2);
marks(v2):= 'x02a';
SELECT marks(v2);
SELECT 'x02b' INTO marks(v2);
SELECT marks(v2);
END;
END;
$$
DELIMITER ;$$
if (`SELECT @code`)
{
EXECUTE IMMEDIATE 'SHOW PROCEDURE CODE pkg.p1';
}
CALL pkg.p1;
DROP PACKAGE pkg;

View File

@@ -0,0 +1,88 @@
SET @code=1;
SET sql_mode=ORACLE;
SET names utf8mb4;
#
# MDEV-34319 DECLARE TYPE .. TABLE OF .. INDEX BY in stored routines
#
#
# Using a package body variable as a key: a wrong data type: POINT
#
CREATE PACKAGE pkg AS
PROCEDURE p1;
END;
$$
CREATE PACKAGE BODY pkg AS
v POINT := POINT(1,1);
PROCEDURE p1 AS
TYPE marks_t IS TABLE OF VARCHAR(20) INDEX BY INT;
marks marks_t:= marks_t(1 => 'x43', 2 => 'x99');
BEGIN
SELECT marks(v);
marks(v):= 'x43a';
END;
END;
$$
EXECUTE IMMEDIATE 'SHOW PROCEDURE CODE pkg.p1';
Pos Instruction
0 set marks@0 marks_t('1'=>'x43','2'=>'x99')
1 stmt 0 "SELECT marks(v)"
2 set marks@0[PACKAGE_BODY.v@0] 'x43a'
3 destruct associative_array marks@0
CALL pkg.p1;
ERROR HY000: Illegal parameter data type point for operation '<subscript expression>'
DROP PACKAGE pkg;
#
# Using a package body variable as a key
#
CREATE PACKAGE pkg AS
PROCEDURE p1;
END;
$$
CREATE PACKAGE BODY pkg AS
v1 INT := 1;
v2 INT := 2;
PROCEDURE p1 AS
TYPE marks_t IS TABLE OF VARCHAR(20) INDEX BY INT;
marks marks_t:= marks_t(1 => 'x01', 2 => 'x02');
BEGIN
SELECT marks(v1);
marks(v1):= 'x01a';
SELECT marks(v1);
SELECT 'x01b' INTO marks(v1);
SELECT marks(v1);
SELECT marks(v2);
marks(v2):= 'x02a';
SELECT marks(v2);
SELECT 'x02b' INTO marks(v2);
SELECT marks(v2);
END;
END;
$$
EXECUTE IMMEDIATE 'SHOW PROCEDURE CODE pkg.p1';
Pos Instruction
0 set marks@0 marks_t('1'=>'x01','2'=>'x02')
1 stmt 0 "SELECT marks(v1)"
2 set marks@0[PACKAGE_BODY.v1@0] 'x01a'
3 stmt 0 "SELECT marks(v1)"
4 stmt 0 "SELECT 'x01b' INTO marks(v1)"
5 stmt 0 "SELECT marks(v1)"
6 stmt 0 "SELECT marks(v2)"
7 set marks@0[PACKAGE_BODY.v2@1] 'x02a'
8 stmt 0 "SELECT marks(v2)"
9 stmt 0 "SELECT 'x02b' INTO marks(v2)"
10 stmt 0 "SELECT marks(v2)"
11 destruct associative_array marks@0
CALL pkg.p1;
marks(v1)
x01
marks(v1)
x01a
marks(v1)
x01b
marks(v2)
x02
marks(v2)
x02a
marks(v2)
x02b
DROP PACKAGE pkg;

View File

@@ -0,0 +1,12 @@
--source include/have_debug.inc
SET @code=1;
SET sql_mode=ORACLE;
SET names utf8mb4;
--echo #
--echo # MDEV-34319 DECLARE TYPE .. TABLE OF .. INDEX BY in stored routines
--echo #
--source sp-assoc-array-package-00.inc
--source sp-assoc-array-package-01.inc

View File

@@ -0,0 +1,67 @@
SET sql_mode=oracle;
SET NAMES utf8mb4;
#
# MDEV-34319 DECLARE TYPE .. TABLE OF .. INDEX BY in stored routines
#
#
# Using a package body variable as a key: a wrong data type: POINT
#
CREATE PACKAGE pkg AS
PROCEDURE p1;
END;
$$
CREATE PACKAGE BODY pkg AS
v POINT := POINT(1,1);
PROCEDURE p1 AS
TYPE marks_t IS TABLE OF VARCHAR(20) INDEX BY INT;
marks marks_t:= marks_t(1 => 'x43', 2 => 'x99');
BEGIN
SELECT marks(v);
marks(v):= 'x43a';
END;
END;
$$
CALL pkg.p1;
ERROR HY000: Illegal parameter data type point for operation '<subscript expression>'
DROP PACKAGE pkg;
#
# Using a package body variable as a key
#
CREATE PACKAGE pkg AS
PROCEDURE p1;
END;
$$
CREATE PACKAGE BODY pkg AS
v1 INT := 1;
v2 INT := 2;
PROCEDURE p1 AS
TYPE marks_t IS TABLE OF VARCHAR(20) INDEX BY INT;
marks marks_t:= marks_t(1 => 'x01', 2 => 'x02');
BEGIN
SELECT marks(v1);
marks(v1):= 'x01a';
SELECT marks(v1);
SELECT 'x01b' INTO marks(v1);
SELECT marks(v1);
SELECT marks(v2);
marks(v2):= 'x02a';
SELECT marks(v2);
SELECT 'x02b' INTO marks(v2);
SELECT marks(v2);
END;
END;
$$
CALL pkg.p1;
marks(v1)
x01
marks(v1)
x01a
marks(v1)
x01b
marks(v2)
x02
marks(v2)
x02a
marks(v2)
x02b
DROP PACKAGE pkg;

View File

@@ -0,0 +1,9 @@
SET sql_mode=oracle;
SET NAMES utf8mb4;
--echo #
--echo # MDEV-34319 DECLARE TYPE .. TABLE OF .. INDEX BY in stored routines
--echo #
--source sp-assoc-array-package-00.inc
--source sp-assoc-array-package-01.inc

View File

@@ -0,0 +1,140 @@
SET sql_mode=ORACLE;
SET NAMES utf8mb4;
#
# MDEV-34319 DECLARE TYPE .. TABLE OF .. INDEX BY in stored routines
#
#
# Disallow VARCHAR->TEXT propagation for the INDEX BY field
# Even for non-strict sql_mode.
#
SET @sql_mode_old=@@sql_mode;
SET sql_mode=REPLACE(@@sql_mode,'STRING_ALL_TABLES','');
CREATE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF INT INDEX BY VARCHAR(100000) COLLATE latin1_bin;
assoc assoc_t:= assoc_t('a'=>1,'b'=>2);
BEGIN
NULL;
END;
$$
CALL p1;
ERROR 42000: Column length too big for column '' (max = 65532); use BLOB or TEXT instead
DROP PROCEDURE p1;
SET sql_mode=@sql_mode_old;
#
# Check that TEXT works in an array of scalars
#
CREATE OR REPLACE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF TEXT INDEX BY INT;
assoc assoc_t:= assoc_t(1=>'val1',2=>'val2');
BEGIN
SELECT assoc(1) AS a1, assoc(2) AS a2;
CREATE TABLE t1 AS SELECT assoc(1) AS a1, assoc(2) AS a2;
SHOW CREATE TABLE t1;
SELECT * FROM t1;
DROP TABLE t1;
END;
$$
CALL p1;
a1 a2
val1 val2
Table Create Table
t1 CREATE TABLE "t1" (
"a1" text DEFAULT NULL,
"a2" text DEFAULT NULL
)
a1 a2
val1 val2
DROP PROCEDURE p1;
#
# VARCHAR->TEXT propagation is ok for the data scalar value
#
SET @sql_mode_old=@@sql_mode;
SET sql_mode=REPLACE(@@sql_mode,'STRING_ALL_TABLES','');
CREATE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF VARCHAR(100000) INDEX BY VARCHAR(20);
assoc assoc_t := assoc_t(1=>'val1',2=>'val2');
BEGIN
SELECT assoc(1) AS a1, assoc(2) AS a2;
CREATE TABLE t1 AS SELECT assoc(1) AS a1, assoc(2) AS a2;
SELECT * FROM t1;
SHOW CREATE TABLE t1;
DROP TABLE t1;
END;
$$
Warnings:
Note 1246 Converting column 'assoc_t' from VARCHAR to TEXT
CALL p1;
a1 a2
val1 val2
a1 a2
val1 val2
Table Create Table
t1 CREATE TABLE "t1" (
"a1" mediumtext DEFAULT NULL,
"a2" mediumtext DEFAULT NULL
)
Warnings:
Note 1246 Converting column 'assoc_t' from VARCHAR to TEXT
DROP PROCEDURE p1;
SET sql_mode=@sql_mode_old;
#
# Check that TEXT record fields work in an array of records
#
CREATE OR REPLACE PROCEDURE p1 AS
TYPE rec_t IS RECORD (a TEXT, b LONGTEXT);
TYPE assoc_t IS TABLE OF rec_t INDEX BY INT;
assoc assoc_t:= assoc_t(1=>rec_t('a1','b1'),2=>rec_t('a2','b2'));
BEGIN
CREATE TABLE t1 AS
SELECT assoc(1).a AS a1a, assoc(1).b AS a1b, assoc(2).a AS a2a, assoc(2).b AS a2b;
SHOW CREATE TABLE t1;
SELECT * FROM t1;
DROP TABLE t1;
END;
$$
CALL p1;
Table Create Table
t1 CREATE TABLE "t1" (
"a1a" text DEFAULT NULL,
"a1b" longtext DEFAULT NULL,
"a2a" text DEFAULT NULL,
"a2b" longtext DEFAULT NULL
)
a1a a1b a2a a2b
a1 b1 a2 b2
DROP PROCEDURE p1;
#
# VARCHAR->TEXT propagation is ok for the data fields
#
SET @sql_mode_old=@@sql_mode;
SET sql_mode=REPLACE(@@sql_mode,'STRING_ALL_TABLES','');
CREATE PROCEDURE p1 AS
TYPE rec_t IS RECORD (a VARCHAR(100000), b VARCHAR(100000000));
TYPE assoc_t IS TABLE OF rec_t INDEX BY VARCHAR(20) COLLATE latin1_bin;
assoc assoc_t := assoc_t(1=>rec_t('a1','b1'),2=>rec_t('a2','b2'));
BEGIN
SELECT assoc(1).a, assoc(1).b, assoc(2).a, assoc(2).b;
CREATE TABLE t1 AS
SELECT assoc(1).a AS a1a, assoc(1).b AS a1b, assoc(2).a AS a2a, assoc(2).b AS a2b;
SHOW CREATE TABLE t1;
DROP TABLE t1;
END;
$$
Warnings:
Note 1246 Converting column 'a' from VARCHAR to TEXT
Note 1246 Converting column 'b' from VARCHAR to TEXT
CALL p1;
assoc(1).a assoc(1).b assoc(2).a assoc(2).b
a1 b1 a2 b2
Table Create Table
t1 CREATE TABLE "t1" (
"a1a" mediumtext DEFAULT NULL,
"a1b" longtext DEFAULT NULL,
"a2a" mediumtext DEFAULT NULL,
"a2b" longtext DEFAULT NULL
)
Warnings:
Note 1246 Converting column 'a' from VARCHAR to TEXT
Note 1246 Converting column 'b' from VARCHAR to TEXT
DROP PROCEDURE p1;
SET sql_mode=@sql_mode_old;

View File

@@ -0,0 +1,119 @@
SET sql_mode=ORACLE;
SET NAMES utf8mb4;
--echo #
--echo # MDEV-34319 DECLARE TYPE .. TABLE OF .. INDEX BY in stored routines
--echo #
--echo #
--echo # Disallow VARCHAR->TEXT propagation for the INDEX BY field
--echo # Even for non-strict sql_mode.
--echo #
SET @sql_mode_old=@@sql_mode;
SET sql_mode=REPLACE(@@sql_mode,'STRING_ALL_TABLES','');
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF INT INDEX BY VARCHAR(100000) COLLATE latin1_bin;
assoc assoc_t:= assoc_t('a'=>1,'b'=>2);
BEGIN
NULL;
END;
$$
DELIMITER ;$$
--error ER_TOO_BIG_FIELDLENGTH
CALL p1;
DROP PROCEDURE p1;
SET sql_mode=@sql_mode_old;
--echo #
--echo # Check that TEXT works in an array of scalars
--echo #
DELIMITER $$;
CREATE OR REPLACE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF TEXT INDEX BY INT;
assoc assoc_t:= assoc_t(1=>'val1',2=>'val2');
BEGIN
SELECT assoc(1) AS a1, assoc(2) AS a2;
CREATE TABLE t1 AS SELECT assoc(1) AS a1, assoc(2) AS a2;
SHOW CREATE TABLE t1;
SELECT * FROM t1;
DROP TABLE t1;
END;
$$
DELIMITER ;$$
CALL p1;
DROP PROCEDURE p1;
--echo #
--echo # VARCHAR->TEXT propagation is ok for the data scalar value
--echo #
SET @sql_mode_old=@@sql_mode;
SET sql_mode=REPLACE(@@sql_mode,'STRING_ALL_TABLES','');
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF VARCHAR(100000) INDEX BY VARCHAR(20);
assoc assoc_t := assoc_t(1=>'val1',2=>'val2');
BEGIN
SELECT assoc(1) AS a1, assoc(2) AS a2;
CREATE TABLE t1 AS SELECT assoc(1) AS a1, assoc(2) AS a2;
SELECT * FROM t1;
SHOW CREATE TABLE t1;
DROP TABLE t1;
END;
$$
DELIMITER ;$$
CALL p1;
DROP PROCEDURE p1;
SET sql_mode=@sql_mode_old;
--echo #
--echo # Check that TEXT record fields work in an array of records
--echo #
DELIMITER $$;
CREATE OR REPLACE PROCEDURE p1 AS
TYPE rec_t IS RECORD (a TEXT, b LONGTEXT);
TYPE assoc_t IS TABLE OF rec_t INDEX BY INT;
assoc assoc_t:= assoc_t(1=>rec_t('a1','b1'),2=>rec_t('a2','b2'));
BEGIN
CREATE TABLE t1 AS
SELECT assoc(1).a AS a1a, assoc(1).b AS a1b, assoc(2).a AS a2a, assoc(2).b AS a2b;
SHOW CREATE TABLE t1;
SELECT * FROM t1;
DROP TABLE t1;
END;
$$
DELIMITER ;$$
CALL p1;
DROP PROCEDURE p1;
--echo #
--echo # VARCHAR->TEXT propagation is ok for the data fields
--echo #
SET @sql_mode_old=@@sql_mode;
SET sql_mode=REPLACE(@@sql_mode,'STRING_ALL_TABLES','');
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE rec_t IS RECORD (a VARCHAR(100000), b VARCHAR(100000000));
TYPE assoc_t IS TABLE OF rec_t INDEX BY VARCHAR(20) COLLATE latin1_bin;
assoc assoc_t := assoc_t(1=>rec_t('a1','b1'),2=>rec_t('a2','b2'));
BEGIN
SELECT assoc(1).a, assoc(1).b, assoc(2).a, assoc(2).b;
CREATE TABLE t1 AS
SELECT assoc(1).a AS a1a, assoc(1).b AS a1b, assoc(2).a AS a2a, assoc(2).b AS a2b;
SHOW CREATE TABLE t1;
DROP TABLE t1;
END;
$$
DELIMITER ;$$
CALL p1;
DROP PROCEDURE p1;
SET sql_mode=@sql_mode_old;

View File

@@ -0,0 +1,63 @@
set sql_mode=oracle;
SET NAMES utf8mb4;
#
# MDEV-34319 DECLARE TYPE .. TABLE OF .. INDEX BY in stored routines
#
#
# Using assoc array scalar elements in a UNION
#
CREATE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF INT INDEX BY INT;
assoc assoc_t:= assoc_t(1=>10,2=>20,3=>30);
BEGIN
SELECT assoc(1) AS c1 UNION SELECT assoc(2) UNION SELECT assoc(3);
END;
$$
CALL p1;
c1
10
20
30
DROP PROCEDURE p1;
CREATE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF TEXT INDEX BY INT;
assoc assoc_t:= assoc_t(1=>'10',2=>'20',3=>'30');
BEGIN
SELECT assoc(1) AS c1 UNION SELECT assoc(2) UNION SELECT assoc(3);
END;
$$
CALL p1;
c1
10
20
30
DROP PROCEDURE p1;
#
# Using assoc array record elements in a UNION
#
CREATE PROCEDURE p1 AS
TYPE person_t IS RECORD
(
first_name VARCHAR(64),
last_name VARCHAR(64)
);
TYPE assoc_t IS TABLE OF person_t INDEX BY INT;
assoc assoc_t:= assoc_t(
1=>person_t('Agnetha','Fältskog'),
2=>person_t('Benny','Andersson'),
3=>person_t('Björn', 'Ulvaeus'),
4=>person_t('Anni-Frid','Lyngstad'));
BEGIN
SELECT assoc(1).first_name AS first_name, assoc(1).last_name AS last_name
UNION SELECT assoc(2).first_name, assoc(2).last_name
UNION SELECT assoc(3).first_name, assoc(3).last_name
UNION SELECT assoc(4).first_name, assoc(4).last_name;
END;
$$
CALL p1;
first_name last_name
Agnetha Fältskog
Benny Andersson
Björn Ulvaeus
Anni-Frid Lyngstad
DROP PROCEDURE p1;

View File

@@ -0,0 +1,63 @@
set sql_mode=oracle;
SET NAMES utf8mb4;
--echo #
--echo # MDEV-34319 DECLARE TYPE .. TABLE OF .. INDEX BY in stored routines
--echo #
--echo #
--echo # Using assoc array scalar elements in a UNION
--echo #
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF INT INDEX BY INT;
assoc assoc_t:= assoc_t(1=>10,2=>20,3=>30);
BEGIN
SELECT assoc(1) AS c1 UNION SELECT assoc(2) UNION SELECT assoc(3);
END;
$$
DELIMITER ;$$
CALL p1;
DROP PROCEDURE p1;
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF TEXT INDEX BY INT;
assoc assoc_t:= assoc_t(1=>'10',2=>'20',3=>'30');
BEGIN
SELECT assoc(1) AS c1 UNION SELECT assoc(2) UNION SELECT assoc(3);
END;
$$
DELIMITER ;$$
CALL p1;
DROP PROCEDURE p1;
--echo #
--echo # Using assoc array record elements in a UNION
--echo #
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE person_t IS RECORD
(
first_name VARCHAR(64),
last_name VARCHAR(64)
);
TYPE assoc_t IS TABLE OF person_t INDEX BY INT;
assoc assoc_t:= assoc_t(
1=>person_t('Agnetha','Fältskog'),
2=>person_t('Benny','Andersson'),
3=>person_t('Björn', 'Ulvaeus'),
4=>person_t('Anni-Frid','Lyngstad'));
BEGIN
SELECT assoc(1).first_name AS first_name, assoc(1).last_name AS last_name
UNION SELECT assoc(2).first_name, assoc(2).last_name
UNION SELECT assoc(3).first_name, assoc(3).last_name
UNION SELECT assoc(4).first_name, assoc(4).last_name;
END;
$$
DELIMITER ;$$
CALL p1;
DROP PROCEDURE p1;

View File

@@ -43,6 +43,45 @@ END;
$$ $$
ERROR HY000: 'associative_array' is not allowed in this context ERROR HY000: 'associative_array' is not allowed in this context
# #
# Bad key data type
#
CREATE OR REPLACE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF VARCHAR2(20) INDEX BY DOUBLE;
assoc assoc_t;
BEGIN
NULL;
END;
$$
ERROR HY000: Illegal parameter data type double for operation '<array index data type>'
CREATE OR REPLACE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF VARCHAR2(20) INDEX BY POINT;
assoc assoc_t;
BEGIN
NULL;
END;
$$
ERROR HY000: Illegal parameter data type point for operation '<array index data type>'
CREATE OR REPLACE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF VARCHAR2(20) INDEX BY DATE;
assoc assoc_t;
BEGIN
NULL;
END;
$$
ERROR HY000: Illegal parameter data type datetime for operation '<array index data type>'
CREATE OR REPLACE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF VARCHAR2(20) INDEX BY ROW(a INT, b INT);
assoc assoc_t;
BEGIN
NULL;
END;
$$
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'ROW(a INT, b INT);
assoc assoc_t;
BEGIN
NULL;
END' at line 2
#
# RECORD element type # RECORD element type
# #
DECLARE DECLARE
@@ -207,6 +246,48 @@ BEGIN
NULL; NULL;
END' at line 3 END' at line 3
# #
# Wrong use: assoc_array_var;
#
CREATE PROCEDURE p1 AS
TYPE assoc0_t IS TABLE OF INT INDEX BY INT;
assoc0 assoc0_t;
BEGIN
assoc0;
END;
$$
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'assoc0;
END' at line 5
CREATE PROCEDURE p1 AS
TYPE assoc0_t IS TABLE OF INT INDEX BY INT;
assoc0 assoc0_t;
BEGIN
`assoc0`;
END;
$$
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '`assoc0`;
END' at line 5
#
# Wrong use: assoc_array_var(1);
#
CREATE PROCEDURE p1 AS
TYPE assoc0_t IS TABLE OF INT INDEX BY INT;
assoc0 assoc0_t;
BEGIN
assoc0(1);
END;
$$
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'assoc0(1);
END' at line 5
CREATE PROCEDURE p1 AS
TYPE assoc0_t IS TABLE OF INT INDEX BY INT;
assoc0 assoc0_t;
BEGIN
`assoc0`(1);
END;
$$
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '`assoc0`(1);
END' at line 5
#
# Wrong use: assoc_array_var.x(1) # Wrong use: assoc_array_var.x(1)
# #
CREATE PROCEDURE p1 AS CREATE PROCEDURE p1 AS
@@ -354,9 +435,9 @@ END LOOP;
END; END;
$$ $$
Warnings: Warnings:
Warning 1366 Incorrect double value: 'Klaus' for column ``.``.`` at row 0 Warning 1366 Incorrect double value: 'Klaus' for column ``.``.`marks` at row 0
Warning 1366 Incorrect double value: 'Lee' for column ``.``.`` at row 0 Warning 1366 Incorrect double value: 'Lee' for column ``.``.`marks` at row 0
Warning 1366 Incorrect double value: 'Arun' for column ``.``.`` at row 0 Warning 1366 Incorrect double value: 'Arun' for column ``.``.`marks` at row 0
# #
# Anchored ROWTYPE for array element # Anchored ROWTYPE for array element
# #
@@ -662,6 +743,45 @@ $$
marks.EXISTS(4) marks.EXISTS(4)
0 0
# #
# Make sure DELETE does not work as a method-function
#
DECLARE
TYPE marks_t IS TABLE OF NUMBER INDEX BY INTEGER;
marks marks_t;
BEGIN
marks(0) := 62;
SELECT marks.DELETE;
END;
$$
ERROR 42S22: Unknown column 'marks' in 'DELETE'
DECLARE
TYPE marks_t IS TABLE OF NUMBER INDEX BY INTEGER;
marks marks_t;
BEGIN
marks(0) := 62;
DO marks.DELETE;
END;
$$
ERROR 42S22: Unknown column 'marks' in 'DELETE'
DECLARE
TYPE marks_t IS TABLE OF NUMBER INDEX BY INTEGER;
marks marks_t;
BEGIN
marks(0) := 62;
SELECT marks.DELETE(0);
END;
$$
ERROR 42S22: Unknown column 'marks' in 'DELETE'
DECLARE
TYPE marks_t IS TABLE OF NUMBER INDEX BY INTEGER;
marks marks_t;
BEGIN
marks(0) := 62;
DO marks.DELETE(0);
END;
$$
ERROR 42S22: Unknown column 'marks' in 'DELETE'
#
# DELETE # DELETE
# #
DECLARE DECLARE
@@ -671,18 +791,14 @@ BEGIN
marks(0) := 62; marks(0) := 62;
marks(1) := 78; marks(1) := 78;
marks(2) := 99; marks(2) := 99;
SELECT marks.DELETE(1); marks.DELETE(1);
SELECT marks.EXISTS(0), marks.EXISTS(1), marks.EXISTS(2); SELECT marks.EXISTS(0), marks.EXISTS(1), marks.EXISTS(2);
SELECT marks.DELETE; marks.DELETE;
SELECT marks.EXISTS(0), marks.EXISTS(1), marks.EXISTS(2); SELECT marks.EXISTS(0), marks.EXISTS(1), marks.EXISTS(2);
END; END;
$$ $$
marks.DELETE(1)
0
marks.EXISTS(0) marks.EXISTS(1) marks.EXISTS(2) marks.EXISTS(0) marks.EXISTS(1) marks.EXISTS(2)
1 0 1 1 0 1
marks.DELETE
0
marks.EXISTS(0) marks.EXISTS(1) marks.EXISTS(2) marks.EXISTS(0) marks.EXISTS(1) marks.EXISTS(2)
0 0 0 0 0 0
# #
@@ -695,7 +811,7 @@ BEGIN
marks(0) := 62; marks(0) := 62;
marks(1) := 78; marks(1) := 78;
marks(2) := 99; marks(2) := 99;
SELECT marks.DELETE(1, 2); marks.DELETE(1, 2);
END; END;
$$ $$
ERROR 42000: Incorrect number of arguments for DELETE ; expected 1, got 2 ERROR 42000: Incorrect number of arguments for DELETE ; expected 1, got 2
@@ -707,21 +823,40 @@ TYPE marks_t IS TABLE OF NUMBER INDEX BY INTEGER;
marks marks_t; marks marks_t;
BEGIN BEGIN
marks(0) := 62; marks(0) := 62;
SELECT marks.DELETE(NULL); marks.DELETE(NULL);
END; END;
$$ $$
marks.DELETE(NULL)
0
# #
# DELETE on scalar # DELETE on scalar
# #
DECLARE DECLARE
marks INTEGER; marks INTEGER;
BEGIN BEGIN
SELECT marks.DELETE(1); marks.DELETE(1);
END; END;
$$ $$
ERROR 42000: FUNCTION marks.DELETE does not exist. Check the 'Function Name Parsing and Resolution' section in the Reference Manual ERROR 42000: PROCEDURE marks.DELETE does not exist
#
# Make sure COUNT does not work as a method-procedure
#
DECLARE
TYPE marks_t IS TABLE OF NUMBER INDEX BY INTEGER;
marks marks_t;
BEGIN
marks(0) := 62;
marks.COUNT;
END;
$$
ERROR 42S22: Unknown column 'marks' in 'COUNT'
DECLARE
TYPE marks_t IS TABLE OF NUMBER INDEX BY INTEGER;
marks marks_t;
BEGIN
marks(0) := 62;
marks.COUNT();
END;
$$
ERROR 42S22: Unknown column 'marks' in 'COUNT'
# #
# COUNT # COUNT
# #
@@ -735,7 +870,7 @@ marks(2) := 99;
SELECT marks.COUNT; SELECT marks.COUNT;
marks(3) := 44; marks(3) := 44;
SELECT marks.COUNT; SELECT marks.COUNT;
SELECT marks.DELETE(1); marks.DELETE(1);
SELECT marks.COUNT; SELECT marks.COUNT;
END; END;
$$ $$
@@ -743,8 +878,6 @@ marks.COUNT
3 3
marks.COUNT marks.COUNT
4 4
marks.DELETE(1)
0
marks.COUNT marks.COUNT
3 3
# #
@@ -767,7 +900,7 @@ BEGIN
marks('Clementine Montgomery') := 99; marks('Clementine Montgomery') := 99;
END; END;
$$ $$
ERROR 42000: Specified key was too long; max key length is 10 bytes ERROR HY000: String 'Clementine Montgomery' is too long for INDEX BY (should be no longer than 10)
# #
# VARCHAR2 key fix # VARCHAR2 key fix
# #
@@ -2069,7 +2202,7 @@ BEGIN
NULL; NULL;
END; END;
$$ $$
ERROR 42000: This version of MariaDB doesn't yet support 'nested associative arrays' ERROR HY000: Illegal parameter data type associative_array for operation '<array element data type>'
# #
# Ensure that nested assoc array types cannot be used for record field # Ensure that nested assoc array types cannot be used for record field
# #
@@ -2138,7 +2271,7 @@ person_t('Michael', 'Widenius') IN (person_by_nickname('Monty'))
id select_type table type possible_keys key key_len ref rows filtered Extra id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used 1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings: Warnings:
Note 1003 select ('Michael','Widenius') = person_by_nickname['Monty']@0['Monty'] AS "person_t('Michael', 'Widenius') IN (person_by_nickname('Monty'))" Note 1003 select ('Michael','Widenius') = person_by_nickname@0['Monty'] AS "person_t('Michael', 'Widenius') IN (person_by_nickname('Monty'))"
# #
# EXPLAIN SELECT for field of element of array # EXPLAIN SELECT for field of element of array
# #
@@ -2158,7 +2291,7 @@ $$
id select_type table type possible_keys key key_len ref rows filtered Extra id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used 1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings: Warnings:
Note 1003 select person_by_nickname['Monty'].first_name@0['Monty'].0 AS "person_by_nickname('Monty').first_name" Note 1003 select person_by_nickname@0['Monty'].first_name AS "person_by_nickname('Monty').first_name"
# #
# EXPLAIN SELECT for IS NULL with array argument # EXPLAIN SELECT for IS NULL with array argument
# #
@@ -2233,17 +2366,25 @@ m1 IS NULL m1 IS NOT NULL m2 is NULL m2 IS NOT NULL
m1 IS NULL m1 IS NOT NULL m2 is NULL m2 IS NOT NULL m1 IS NULL m1 IS NOT NULL m2 is NULL m2 IS NOT NULL
0 1 0 1 0 1 0 1
# #
# Call associative array method directly # Call an associative array procedure method directly
# #
DECLARE DECLARE
TYPE marks_t IS TABLE OF NUMBER INDEX BY INTEGER; TYPE marks_t IS TABLE OF NUMBER INDEX BY INTEGER;
m1 marks_t:= marks_t(1 => 62); m1 marks_t:= marks_t(1 => 61, 2 => 62, 3 => 63);
BEGIN BEGIN
SELECT m1.count;
m1.delete(2);
SELECT m1.count, m1(1), m1(3);
m1.delete; m1.delete;
SELECT m1.count;
END; END;
$$ $$
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'm1.delete; m1.count
END' at line 5 3
m1.count m1(1) m1(3)
2 61 63
m1.count
0
# #
# Create an associative array type sharing the same name as a built-in function # Create an associative array type sharing the same name as a built-in function
# TODO: This should be allowed # TODO: This should be allowed
@@ -2591,3 +2732,134 @@ END;
$$ $$
ERROR HY000: 'count("t1"."a")' is not allowed in this context ERROR HY000: 'count("t1"."a")' is not allowed in this context
DROP TABLE t1; DROP TABLE t1;
#
# Non-constant expressions are not allowed as a key: a scalar value
#
CREATE PROCEDURE p1 AS
TYPE marks_t IS TABLE OF NUMBER INDEX BY INTEGER;
marks marks_t;
BEGIN
marks(1) := 78;
SELECT marks(rand());
END;
$$
CALL p1;
ERROR HY000: 'rand()' is not allowed in this context
DROP PROCEDURE p1;
CREATE PROCEDURE p1 AS
TYPE marks_t IS TABLE OF NUMBER INDEX BY INTEGER;
marks marks_t;
BEGIN
marks(1) := 78;
SELECT marks(78+rand());
END;
$$
CALL p1;
ERROR HY000: '78 + rand()' is not allowed in this context
DROP PROCEDURE p1;
CREATE PROCEDURE p1 AS
TYPE marks_t IS TABLE OF NUMBER INDEX BY INTEGER;
marks marks_t;
BEGIN
marks(rand()) := 78;
END;
$$
CALL p1;
ERROR HY000: 'rand()' is not allowed in this context
DROP PROCEDURE p1;
CREATE PROCEDURE p1 AS
TYPE marks_t IS TABLE OF NUMBER INDEX BY INTEGER;
marks marks_t;
BEGIN
marks(1) := 78;
SELECT 79 INTO marks(rand());
END;
$$
CALL p1;
ERROR HY000: 'rand()' is not allowed in this context
DROP PROCEDURE p1;
#
# Non-constant expressions are not allowed as a key: a row value field
#
CREATE PROCEDURE p1 AS
TYPE person_t IS RECORD
(
first_name VARCHAR(64),
last_name VARCHAR(64)
);
TYPE assoc_t IS TABLE OF person_t INDEX BY INTEGER;
assoc assoc_t:= assoc_t (1 => person_t('Iqbal','Hassan'));
BEGIN
SELECT assoc(rand()).first_name;
END;
$$
CALL p1;
ERROR HY000: 'rand()' is not allowed in this context
DROP PROCEDURE p1;
CREATE PROCEDURE p1 AS
TYPE person_t IS RECORD
(
first_name VARCHAR(64),
last_name VARCHAR(64)
);
TYPE assoc_t IS TABLE OF person_t INDEX BY INTEGER;
assoc assoc_t:= assoc_t(1 => person_t('Iqbal','Hassan'));
BEGIN
assoc(rand()).first_name:= 'John';
END;
$$
CALL p1;
ERROR HY000: 'rand()' is not allowed in this context
DROP PROCEDURE p1;
CREATE PROCEDURE p1 AS
TYPE person_t IS RECORD
(
first_name VARCHAR(64),
last_name VARCHAR(64)
);
TYPE assoc_t IS TABLE OF person_t INDEX BY INTEGER;
assoc assoc_t:= assoc_t(1 => person_t('Iqbal','Hassan'));
BEGIN
SELECT 'John' INTO assoc(rand()).first_name;
END;
$$
CALL p1;
ERROR HY000: 'rand()' is not allowed in this context
DROP PROCEDURE p1;
#
# The below script with a scalar element produces a warning.
# Make sure the variable name is correct in the warning.
#
CREATE PROCEDURE p1 AS
TYPE assoc0_t IS TABLE OF INT INDEX BY INT;
assoc0 assoc0_t;
BEGIN
assoc0(1):= 10e10;
SELECT assoc0(1) AS c1;
END;
$$
CALL p1;
c1
2147483647
Warnings:
Warning 1264 Out of range value for column 'assoc0' at row 0
DROP PROCEDURE p1;
#
# The below script with a record element produces a warning.
# Make sure the variable name is correct in the warning.
#
CREATE PROCEDURE p1 AS
TYPE rec_t IS RECORD (i INT);
TYPE assoc0_t IS TABLE OF rec_t INDEX BY INT;
assoc0 assoc0_t := assoc0_t(1 => rec_t(0));
BEGIN
assoc0(1).i := 10e10;
SELECT assoc0(1).i AS c1;
END;
$$
CALL p1;
c1
2147483647
Warnings:
Warning 1264 Out of range value for column 'i' at row 0
DROP PROCEDURE p1;

View File

@@ -66,6 +66,58 @@ $$
DELIMITER ;$$ DELIMITER ;$$
--echo #
--echo # Bad key data type
--echo #
DELIMITER $$;
--error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION
CREATE OR REPLACE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF VARCHAR2(20) INDEX BY DOUBLE;
assoc assoc_t;
BEGIN
NULL;
END;
$$
DELIMITER ;$$
DELIMITER $$;
--error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION
CREATE OR REPLACE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF VARCHAR2(20) INDEX BY POINT;
assoc assoc_t;
BEGIN
NULL;
END;
$$
DELIMITER ;$$
DELIMITER $$;
--error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION
CREATE OR REPLACE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF VARCHAR2(20) INDEX BY DATE;
assoc assoc_t;
BEGIN
NULL;
END;
$$
DELIMITER ;$$
DELIMITER $$;
--error ER_PARSE_ERROR
CREATE OR REPLACE PROCEDURE p1 AS
TYPE assoc_t IS TABLE OF VARCHAR2(20) INDEX BY ROW(a INT, b INT);
assoc assoc_t;
BEGIN
NULL;
END;
$$
DELIMITER ;$$
--echo # --echo #
--echo # RECORD element type --echo # RECORD element type
--echo # --echo #
@@ -224,6 +276,62 @@ $$
DELIMITER ;$$ DELIMITER ;$$
--echo #
--echo # Wrong use: assoc_array_var;
--echo #
DELIMITER $$;
--error ER_PARSE_ERROR
CREATE PROCEDURE p1 AS
TYPE assoc0_t IS TABLE OF INT INDEX BY INT;
assoc0 assoc0_t;
BEGIN
assoc0;
END;
$$
DELIMITER ;$$
DELIMITER $$;
--error ER_PARSE_ERROR
CREATE PROCEDURE p1 AS
TYPE assoc0_t IS TABLE OF INT INDEX BY INT;
assoc0 assoc0_t;
BEGIN
`assoc0`;
END;
$$
DELIMITER ;$$
--echo #
--echo # Wrong use: assoc_array_var(1);
--echo #
DELIMITER $$;
--error ER_PARSE_ERROR
CREATE PROCEDURE p1 AS
TYPE assoc0_t IS TABLE OF INT INDEX BY INT;
assoc0 assoc0_t;
BEGIN
assoc0(1);
END;
$$
DELIMITER ;$$
DELIMITER $$;
--error ER_PARSE_ERROR
CREATE PROCEDURE p1 AS
TYPE assoc0_t IS TABLE OF INT INDEX BY INT;
assoc0 assoc0_t;
BEGIN
`assoc0`(1);
END;
$$
DELIMITER ;$$
--echo # --echo #
--echo # Wrong use: assoc_array_var.x(1) --echo # Wrong use: assoc_array_var.x(1)
--echo # --echo #
@@ -718,6 +826,60 @@ END;
$$ $$
DELIMITER ;$$ DELIMITER ;$$
--echo #
--echo # Make sure DELETE does not work as a method-function
--echo #
DELIMITER $$;
--error ER_BAD_FIELD_ERROR
DECLARE
TYPE marks_t IS TABLE OF NUMBER INDEX BY INTEGER;
marks marks_t;
BEGIN
marks(0) := 62;
SELECT marks.DELETE;
END;
$$
DELIMITER ;$$
DELIMITER $$;
--error ER_BAD_FIELD_ERROR
DECLARE
TYPE marks_t IS TABLE OF NUMBER INDEX BY INTEGER;
marks marks_t;
BEGIN
marks(0) := 62;
DO marks.DELETE;
END;
$$
DELIMITER ;$$
DELIMITER $$;
--error ER_BAD_FIELD_ERROR
DECLARE
TYPE marks_t IS TABLE OF NUMBER INDEX BY INTEGER;
marks marks_t;
BEGIN
marks(0) := 62;
SELECT marks.DELETE(0);
END;
$$
DELIMITER ;$$
DELIMITER $$;
--error ER_BAD_FIELD_ERROR
DECLARE
TYPE marks_t IS TABLE OF NUMBER INDEX BY INTEGER;
marks marks_t;
BEGIN
marks(0) := 62;
DO marks.DELETE(0);
END;
$$
DELIMITER ;$$
--echo # --echo #
--echo # DELETE --echo # DELETE
--echo # --echo #
@@ -729,10 +891,10 @@ BEGIN
marks(0) := 62; marks(0) := 62;
marks(1) := 78; marks(1) := 78;
marks(2) := 99; marks(2) := 99;
SELECT marks.DELETE(1); marks.DELETE(1);
SELECT marks.EXISTS(0), marks.EXISTS(1), marks.EXISTS(2); SELECT marks.EXISTS(0), marks.EXISTS(1), marks.EXISTS(2);
SELECT marks.DELETE; marks.DELETE;
SELECT marks.EXISTS(0), marks.EXISTS(1), marks.EXISTS(2); SELECT marks.EXISTS(0), marks.EXISTS(1), marks.EXISTS(2);
END; END;
$$ $$
@@ -750,7 +912,7 @@ BEGIN
marks(0) := 62; marks(0) := 62;
marks(1) := 78; marks(1) := 78;
marks(2) := 99; marks(2) := 99;
SELECT marks.DELETE(1, 2); marks.DELETE(1, 2);
END; END;
$$ $$
DELIMITER ;$$ DELIMITER ;$$
@@ -764,7 +926,7 @@ DECLARE
marks marks_t; marks marks_t;
BEGIN BEGIN
marks(0) := 62; marks(0) := 62;
SELECT marks.DELETE(NULL); marks.DELETE(NULL);
END; END;
$$ $$
DELIMITER ;$$ DELIMITER ;$$
@@ -773,15 +935,46 @@ DELIMITER ;$$
--echo # DELETE on scalar --echo # DELETE on scalar
--echo # --echo #
DELIMITER $$; DELIMITER $$;
--error ER_FUNC_INEXISTENT_NAME_COLLISION --error ER_SP_DOES_NOT_EXIST
DECLARE DECLARE
marks INTEGER; marks INTEGER;
BEGIN BEGIN
SELECT marks.DELETE(1); marks.DELETE(1);
END; END;
$$ $$
DELIMITER ;$$ DELIMITER ;$$
--echo #
--echo # Make sure COUNT does not work as a method-procedure
--echo #
DELIMITER $$;
--error ER_BAD_FIELD_ERROR
DECLARE
TYPE marks_t IS TABLE OF NUMBER INDEX BY INTEGER;
marks marks_t;
BEGIN
marks(0) := 62;
marks.COUNT;
END;
$$
DELIMITER ;$$
DELIMITER $$;
--error ER_BAD_FIELD_ERROR
DECLARE
TYPE marks_t IS TABLE OF NUMBER INDEX BY INTEGER;
marks marks_t;
BEGIN
marks(0) := 62;
marks.COUNT();
END;
$$
DELIMITER ;$$
--echo # --echo #
--echo # COUNT --echo # COUNT
--echo # --echo #
@@ -798,7 +991,7 @@ BEGIN
marks(3) := 44; marks(3) := 44;
SELECT marks.COUNT; SELECT marks.COUNT;
SELECT marks.DELETE(1); marks.DELETE(1);
SELECT marks.COUNT; SELECT marks.COUNT;
END; END;
$$ $$
@@ -822,7 +1015,7 @@ DELIMITER ;$$
--echo # VARCHAR2 key length error --echo # VARCHAR2 key length error
--echo # --echo #
DELIMITER $$; DELIMITER $$;
--error ER_TOO_LONG_KEY --error ER_WRONG_STRING_LENGTH
DECLARE DECLARE
TYPE marks_t IS TABLE OF NUMBER INDEX BY VARCHAR2(10); TYPE marks_t IS TABLE OF NUMBER INDEX BY VARCHAR2(10);
marks marks_t; marks marks_t;
@@ -2380,7 +2573,7 @@ SET sql_mode=ORACLE;
--echo # Ensure that nested assoc array types are properly parsed (without crash, etc) --echo # Ensure that nested assoc array types are properly parsed (without crash, etc)
--echo # --echo #
DELIMITER $$; DELIMITER $$;
--error ER_NOT_SUPPORTED_YET --error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION
DECLARE DECLARE
TYPE marks_t IS TABLE OF NUMBER INDEX BY INTEGER; TYPE marks_t IS TABLE OF NUMBER INDEX BY INTEGER;
TYPE classes_t IS TABLE OF marks_t INDEX BY INTEGER; TYPE classes_t IS TABLE OF marks_t INDEX BY INTEGER;
@@ -2546,15 +2739,18 @@ DELIMITER ;$$
--echo # --echo #
--echo # Call associative array method directly --echo # Call an associative array procedure method directly
--echo # --echo #
DELIMITER $$; DELIMITER $$;
--error ER_PARSE_ERROR
DECLARE DECLARE
TYPE marks_t IS TABLE OF NUMBER INDEX BY INTEGER; TYPE marks_t IS TABLE OF NUMBER INDEX BY INTEGER;
m1 marks_t:= marks_t(1 => 62); m1 marks_t:= marks_t(1 => 61, 2 => 62, 3 => 63);
BEGIN BEGIN
SELECT m1.count;
m1.delete(2);
SELECT m1.count, m1(1), m1(3);
m1.delete; m1.delete;
SELECT m1.count;
END; END;
$$ $$
DELIMITER ;$$ DELIMITER ;$$
@@ -2946,3 +3142,163 @@ $$
DELIMITER ;$$ DELIMITER ;$$
DROP TABLE t1; DROP TABLE t1;
--echo #
--echo # Non-constant expressions are not allowed as a key: a scalar value
--echo #
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE marks_t IS TABLE OF NUMBER INDEX BY INTEGER;
marks marks_t;
BEGIN
marks(1) := 78;
SELECT marks(rand());
END;
$$
DELIMITER ;$$
--error ER_NOT_ALLOWED_IN_THIS_CONTEXT
CALL p1;
DROP PROCEDURE p1;
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE marks_t IS TABLE OF NUMBER INDEX BY INTEGER;
marks marks_t;
BEGIN
marks(1) := 78;
SELECT marks(78+rand());
END;
$$
DELIMITER ;$$
--error ER_NOT_ALLOWED_IN_THIS_CONTEXT
CALL p1;
DROP PROCEDURE p1;
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE marks_t IS TABLE OF NUMBER INDEX BY INTEGER;
marks marks_t;
BEGIN
marks(rand()) := 78;
END;
$$
DELIMITER ;$$
--error ER_NOT_ALLOWED_IN_THIS_CONTEXT
CALL p1;
DROP PROCEDURE p1;
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE marks_t IS TABLE OF NUMBER INDEX BY INTEGER;
marks marks_t;
BEGIN
marks(1) := 78;
SELECT 79 INTO marks(rand());
END;
$$
DELIMITER ;$$
--error ER_NOT_ALLOWED_IN_THIS_CONTEXT
CALL p1;
DROP PROCEDURE p1;
--echo #
--echo # Non-constant expressions are not allowed as a key: a row value field
--echo #
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE person_t IS RECORD
(
first_name VARCHAR(64),
last_name VARCHAR(64)
);
TYPE assoc_t IS TABLE OF person_t INDEX BY INTEGER;
assoc assoc_t:= assoc_t (1 => person_t('Iqbal','Hassan'));
BEGIN
SELECT assoc(rand()).first_name;
END;
$$
DELIMITER ;$$
--error ER_NOT_ALLOWED_IN_THIS_CONTEXT
CALL p1;
DROP PROCEDURE p1;
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE person_t IS RECORD
(
first_name VARCHAR(64),
last_name VARCHAR(64)
);
TYPE assoc_t IS TABLE OF person_t INDEX BY INTEGER;
assoc assoc_t:= assoc_t(1 => person_t('Iqbal','Hassan'));
BEGIN
assoc(rand()).first_name:= 'John';
END;
$$
DELIMITER ;$$
--error ER_NOT_ALLOWED_IN_THIS_CONTEXT
CALL p1;
DROP PROCEDURE p1;
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE person_t IS RECORD
(
first_name VARCHAR(64),
last_name VARCHAR(64)
);
TYPE assoc_t IS TABLE OF person_t INDEX BY INTEGER;
assoc assoc_t:= assoc_t(1 => person_t('Iqbal','Hassan'));
BEGIN
SELECT 'John' INTO assoc(rand()).first_name;
END;
$$
DELIMITER ;$$
--error ER_NOT_ALLOWED_IN_THIS_CONTEXT
CALL p1;
DROP PROCEDURE p1;
--echo #
--echo # The below script with a scalar element produces a warning.
--echo # Make sure the variable name is correct in the warning.
--echo #
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE assoc0_t IS TABLE OF INT INDEX BY INT;
assoc0 assoc0_t;
BEGIN
assoc0(1):= 10e10;
SELECT assoc0(1) AS c1;
END;
$$
DELIMITER ;$$
CALL p1;
DROP PROCEDURE p1;
--echo #
--echo # The below script with a record element produces a warning.
--echo # Make sure the variable name is correct in the warning.
--echo #
DELIMITER $$;
CREATE PROCEDURE p1 AS
TYPE rec_t IS RECORD (i INT);
TYPE assoc0_t IS TABLE OF rec_t INDEX BY INT;
assoc0 assoc0_t := assoc0_t(1 => rec_t(0));
BEGIN
assoc0(1).i := 10e10;
SELECT assoc0(1).i AS c1;
END;
$$
DELIMITER ;$$
CALL p1;
DROP PROCEDURE p1;

File diff suppressed because it is too large Load Diff

View File

@@ -26,6 +26,7 @@
class Item_field_packable; class Item_field_packable;
class Assoc_array_data;
/* /*
@@ -35,7 +36,11 @@ class Type_handler_assoc_array: public Type_handler_composite
{ {
public: public:
static const Type_handler_assoc_array *singleton(); static const Type_handler_assoc_array *singleton();
static bool check_key_expression_type(Item *key);
static bool check_functor_args(THD *thd, List<Item> *args,
const char *opname);
static bool check_subscript_expression(const Type_handler *formal_th,
Item *key);
public: public:
bool has_methods() const override { return true; } bool has_methods() const override { return true; }
bool has_functors() const override { return true; } bool has_functors() const override { return true; }
@@ -59,22 +64,7 @@ public:
Column_definition *def, Column_definition *def,
const Lex_field_type_st &attr, const Lex_field_type_st &attr,
column_definition_type_t type) column_definition_type_t type)
const override 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, bool sp_variable_declarations_finalize(THD *thd,
LEX *lex, int nvars, LEX *lex, int nvars,
@@ -194,18 +184,36 @@ public:
Item *item, Item *item,
const LEX_CSTRING &expr_str) const LEX_CSTRING &expr_str)
const override; const override;
protected:
Item *create_item_method_func(THD *thd,
const Lex_ident_sys &ca,
const Lex_ident_sys &cb,
List<Item> *args,
const Lex_ident_cli_st &query_fragment)
const;
Item *create_item_method_proc(THD *thd,
const Lex_ident_sys &ca,
const Lex_ident_sys &cb,
List<Item> *args,
const Lex_ident_cli_st &query_fragment)
const;
public:
virtual virtual
Item *create_item_method(THD *thd, Item *create_item_method(THD *thd,
object_method_type_t type,
const Lex_ident_sys &ca, const Lex_ident_sys &ca,
const Lex_ident_sys &cb, const Lex_ident_sys &cb,
List<Item> *args, List<Item> *args,
const Lex_ident_cli_st &query_fragment) const Lex_ident_cli_st &query_fragment)
const override; const override
{
return type == object_method_type_t::PROCEDURE ?
create_item_method_proc(thd, ca, cb, args, query_fragment) :
create_item_method_func(thd, ca, cb, args, query_fragment);
}
bool key_to_lex_cstring(THD *thd, LEX_CSTRING key_to_lex_cstring(THD *thd, const sp_rcontext_addr &var,
Item **key, Item **key, String *buffer) const override;
const LEX_CSTRING& name,
LEX_CSTRING& out_key) const override;
bool get_item_index(THD *thd, bool get_item_index(THD *thd,
const Item_field *item, const Item_field *item,
@@ -242,10 +250,14 @@ protected:
Item_field_packable *m_item_pack; Item_field_packable *m_item_pack;
Item *m_item; Item *m_item;
/* // A helper method
Modified copy of the key definition Assoc_array_data *assoc_tree_search(String *key) const
*/ {
Spvar_definition m_key_def; return reinterpret_cast<Assoc_array_data *>(tree_search((TREE*) &m_tree,
key,
m_key_field));
}
public: public:
Field_assoc_array(uchar *ptr_arg, Field_assoc_array(uchar *ptr_arg,
const LEX_CSTRING *field_name_arg); const LEX_CSTRING *field_name_arg);
@@ -263,6 +275,11 @@ public:
return m_def; return m_def;
} }
const Spvar_definition *get_key_def() const
{
return m_def->head();
}
Item_field *make_item_field_spvar(THD *thd, Item_field *make_item_field_spvar(THD *thd,
const Spvar_definition &def) override; const Spvar_definition &def) override;
@@ -292,7 +309,7 @@ public:
Field *get_key_field() const { return m_key_field; } Field *get_key_field() const { return m_key_field; }
protected: protected:
bool copy_and_convert_key(const String *key, String &key_copy) const; bool copy_and_convert_key(const String &key, String *key_copy) const;
bool unpack_key(const Binary_string &key, String *key_dst) const; bool unpack_key(const Binary_string &key, String *key_dst) const;
bool create_fields(THD *thd); bool create_fields(THD *thd);
@@ -303,7 +320,7 @@ protected:
bool init_element_base(THD *thd); bool init_element_base(THD *thd);
bool create_element_buffer(THD *thd, Binary_string *buffer); bool create_element_buffer(THD *thd, Binary_string *buffer);
bool insert_element(String &&key, Binary_string &&element); bool insert_element(THD *thd, Assoc_array_data *data, bool warn_on_dup_key);
bool get_next_or_prior_key(const String *curr_key, bool get_next_or_prior_key(const String *curr_key,
String *new_key, String *new_key,
bool is_next); bool is_next);
@@ -403,7 +420,17 @@ class Item_splocal_assoc_array_base :public Item_composite_base
{ {
protected: protected:
Item *m_key; Item *m_key;
/*
In expressions:
- assoc_array(key_expr)
- assoc_array(key_expr).field
execute "fix_fields_if_needed" for the key_expr and check if it's
compatible with the "INDEX_BY key_type" clause of the assoc array
definition.
@param thd - Current thd
@param array_addr - The run-time address of the assoc array variable.
*/
bool fix_key(THD *thd, const sp_rcontext_addr &array_addr);
public: public:
Item_splocal_assoc_array_base(Item *key); Item_splocal_assoc_array_base(Item *key);
}; };

View File

@@ -2,6 +2,17 @@
# MDEV-34319 DECLARE TYPE type_name IS RECORD (..) with scalar members in stored routines # MDEV-34319 DECLARE TYPE type_name IS RECORD (..) with scalar members in stored routines
# #
# #
# UUID is not allowed as an assoc array key type
#
SET sql_mode=ORACLE;
DECLARE
TYPE assoc_t IS TABLE OF INTEGER INDEX BY UUID;
BEGIN
NULL;
END;
$$
ERROR HY000: Illegal parameter data type uuid for operation '<array index data type>'
#
# Demonstrate UDT field type for associative array element # Demonstrate UDT field type for associative array element
# #
SET sql_mode=ORACLE; SET sql_mode=ORACLE;

View File

@@ -2,6 +2,21 @@
--echo # MDEV-34319 DECLARE TYPE type_name IS RECORD (..) with scalar members in stored routines --echo # MDEV-34319 DECLARE TYPE type_name IS RECORD (..) with scalar members in stored routines
--echo # --echo #
--echo #
--echo # UUID is not allowed as an assoc array key type
--echo #
SET sql_mode=ORACLE;
DELIMITER $$;
--error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION
DECLARE
TYPE assoc_t IS TABLE OF INTEGER INDEX BY UUID;
BEGIN
NULL;
END;
$$
DELIMITER ;$$
--echo # --echo #
--echo # Demonstrate UDT field type for associative array element --echo # Demonstrate UDT field type for associative array element

View File

@@ -3264,6 +3264,11 @@ protected:
sp_rcontext *get_rcontext(sp_rcontext *local_ctx) const; sp_rcontext *get_rcontext(sp_rcontext *local_ctx) const;
Item_field *get_variable(sp_rcontext *ctx) const; Item_field *get_variable(sp_rcontext *ctx) const;
sp_rcontext_addr rcontext_addr() const
{
return sp_rcontext_addr(m_rcontext_handler, m_var_idx);
}
public: public:
Item_splocal(THD *thd, const Sp_rcontext_handler *rh, Item_splocal(THD *thd, const Sp_rcontext_handler *rh,
const LEX_CSTRING *sp_var_name, uint sp_var_idx, const LEX_CSTRING *sp_var_name, uint sp_var_idx,

View File

@@ -1319,13 +1319,15 @@ sp_instr_set_row_field::print(String *str)
int int
sp_instr_set_composite_field_by_name::exec_core(THD *thd, uint *nextp) sp_instr_set_composite_field_by_name::exec_core(THD *thd, uint *nextp)
{ {
StringBuffer<64> buffer;
if (m_key) if (m_key)
{ {
auto var= get_rcontext(thd)->get_variable(m_offset); auto var= get_rcontext(thd)->get_variable(m_offset);
auto handler= var->type_handler()->to_composite(); auto handler= var->type_handler()->to_composite();
DBUG_ASSERT(handler); DBUG_ASSERT(handler);
if (handler->key_to_lex_cstring(thd, &m_key, var->name, m_field_name)) m_field_name= handler->key_to_lex_cstring(thd, *this, &m_key, &buffer);
if (!m_field_name.str)
return true; return true;
} }
@@ -1391,8 +1393,10 @@ sp_instr_set_composite_field_by_key::exec_core(THD *thd, uint *nextp)
auto handler= var->type_handler()->to_composite(); auto handler= var->type_handler()->to_composite();
DBUG_ASSERT(handler); DBUG_ASSERT(handler);
LEX_CSTRING key; StringBuffer<64> buffer;
if (handler->key_to_lex_cstring(thd, &m_key, var->name, key)) const LEX_CSTRING key= handler->key_to_lex_cstring(thd, *this, &m_key,
&buffer);
if (!key.str)
return true; return true;
int res= get_rcontext(thd)->set_variable_composite_field_by_key(thd, int res= get_rcontext(thd)->set_variable_composite_field_by_key(thd,

View File

@@ -432,19 +432,6 @@ sp_condition_value *sp_pcontext::find_condition(const LEX_CSTRING *name,
} }
bool sp_type_def_list::type_defs_add_record(THD *thd,
const Lex_ident_column &name,
Row_definition_list *field)
{
auto p= new (thd->mem_root) sp_type_def_record(name, field);
if (p == nullptr)
return true;
return m_type_defs.append(p);
}
sp_type_def *sp_pcontext::find_type_def(const LEX_CSTRING &name, sp_type_def *sp_pcontext::find_type_def(const LEX_CSTRING &name,
bool current_scope_only) const bool current_scope_only) const
{ {
@@ -458,21 +445,6 @@ 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_condition_value *
sp_pcontext::find_declared_or_predefined_condition(THD *thd, sp_pcontext::find_declared_or_predefined_condition(THD *thd,
const LEX_CSTRING *name) const LEX_CSTRING *name)

View File

@@ -739,37 +739,14 @@ public:
sp_type_def *find_type_def(const LEX_CSTRING &name, sp_type_def *find_type_def(const LEX_CSTRING &name,
bool current_scope_only) const; bool current_scope_only) const;
///////////////////////////////////////////////////////////////////////// bool type_defs_add(THD *thd, sp_type_def *def)
// Record.
/////////////////////////////////////////////////////////////////////////
bool type_defs_declare_record(THD *thd,
const Lex_ident_column &name,
Row_definition_list *field)
{ {
if (unlikely(find_type_def(name, true))) if (unlikely(find_type_def(def->get_name(), true)))
{ {
my_error(ER_SP_DUP_DECL, MYF(0), name.str); my_error(ER_SP_DUP_DECL, MYF(0), def->get_name().str);
return true; return true;
} }
return type_defs_add_record(thd, name, field); return sp_type_def_list::type_defs_add(def);
}
/////////////////////////////////////////////////////////////////////////
// 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: private:

View File

@@ -108,14 +108,10 @@ public:
} }
return nullptr; return nullptr;
} }
bool type_defs_add(sp_type_def *def)
bool type_defs_add_record(THD *thd, const Lex_ident_column &name, {
Row_definition_list *field); return m_type_defs.append(def);
bool type_defs_add_composite2(THD *thd, }
const Lex_ident_column &name,
const Type_handler *th,
Spvar_definition *key,
Spvar_definition *value);
}; };

View File

@@ -8954,7 +8954,7 @@ bool Qualified_column_ident::append_to(THD *thd, String *str) const
Qualified_ident::Qualified_ident(THD *thd, const Lex_ident_cli_st &a) Qualified_ident::Qualified_ident(THD *thd, const Lex_ident_cli_st &a)
:m_cli_pos(a.pos()), :m_pos(Lex_ident_cli(a.pos(), a.end() - a.pos())),
m_spvar(nullptr), m_spvar(nullptr),
m_defined_parts(1) m_defined_parts(1)
{ {
@@ -8964,32 +8964,34 @@ Qualified_ident::Qualified_ident(THD *thd, const Lex_ident_cli_st &a)
Qualified_ident::Qualified_ident(THD *thd, const Lex_ident_cli_st &a, Qualified_ident::Qualified_ident(THD *thd, const Lex_ident_cli_st &a,
const Lex_ident_sys_st &b) const Lex_ident_cli_st &b)
:m_cli_pos(a.pos()), :m_pos(Lex_ident_cli(a.pos(), b.end() - a.pos())),
m_spvar(nullptr), m_spvar(nullptr),
m_defined_parts(2) m_defined_parts(2)
{ {
DBUG_ASSERT(a.pos() < b.end());
m_parts[0]= Lex_ident_sys(thd, &a); m_parts[0]= Lex_ident_sys(thd, &a);
m_parts[1]= b; m_parts[1]= Lex_ident_sys(thd, &b);
m_parts[2]= Lex_ident_sys(); m_parts[2]= Lex_ident_sys();
} }
Qualified_ident::Qualified_ident(THD *thd, const Lex_ident_cli_st &a, Qualified_ident::Qualified_ident(THD *thd, const Lex_ident_cli_st &a,
const Lex_ident_sys_st &b, const Lex_ident_cli_st &b,
const Lex_ident_sys_st &c) const Lex_ident_cli_st &c)
:m_cli_pos(a.pos()), :m_pos(Lex_ident_cli(a.pos(), c.end() - a.pos())),
m_spvar(nullptr), m_spvar(nullptr),
m_defined_parts(3) m_defined_parts(3)
{ {
DBUG_ASSERT(a.pos() < c.end());
m_parts[0]= Lex_ident_sys(thd, &a); m_parts[0]= Lex_ident_sys(thd, &a);
m_parts[1]= b; m_parts[1]= Lex_ident_sys(thd, &b);
m_parts[2]= c; m_parts[2]= Lex_ident_sys(thd, &c);
} }
Qualified_ident::Qualified_ident(const Lex_ident_sys_st &a) Qualified_ident::Qualified_ident(const Lex_ident_sys_st &a)
:m_cli_pos(nullptr), :m_pos(Lex_ident_cli((const char *) nullptr, 0)),
m_spvar(nullptr), m_spvar(nullptr),
m_defined_parts(1) m_defined_parts(1)
{ {

View File

@@ -7407,28 +7407,29 @@ public:
class Qualified_ident: public Sql_alloc class Qualified_ident: public Sql_alloc
{ {
protected: protected:
const char *m_cli_pos; Lex_ident_cli_st m_pos;
Lex_ident_sys_st m_parts[3]; Lex_ident_sys m_parts[3];
sp_variable *m_spvar; sp_variable *m_spvar;
uint m_defined_parts; uint m_defined_parts;
public: public:
Qualified_ident(THD *thd, const Lex_ident_cli_st &a); Qualified_ident(THD *thd, const Lex_ident_cli_st &a);
Qualified_ident(THD *thd, Qualified_ident(THD *thd,
const Lex_ident_cli_st &a, const Lex_ident_cli_st &a,
const Lex_ident_sys_st &b); const Lex_ident_cli_st &b);
Qualified_ident(THD *thd, Qualified_ident(THD *thd,
const Lex_ident_cli_st &a, const Lex_ident_cli_st &a,
const Lex_ident_sys_st &b, const Lex_ident_cli_st &b,
const Lex_ident_sys_st &c); const Lex_ident_cli_st &c);
Qualified_ident(const Lex_ident_sys_st &a); Qualified_ident(const Lex_ident_sys_st &a);
/* /*
Returns the position of the first character of the identifier in Returns the client query fragment starting at the first character
the client string. of the leftmost identifier and ending at after the last character
of the rightmist identifier.
*/ */
const char *pos() const const Lex_ident_cli_st &pos() const
{ {
return m_cli_pos; return m_pos;
} }
sp_variable *spvar() const sp_variable *spvar() const
@@ -7436,7 +7437,7 @@ public:
return m_spvar; return m_spvar;
} }
const Lex_ident_sys_st &part(uint i) const const Lex_ident_sys &part(uint i) const
{ {
DBUG_ASSERT(i < array_elements(m_parts)); DBUG_ASSERT(i < array_elements(m_parts));
return m_parts[i]; return m_parts[i];
@@ -7447,9 +7448,23 @@ public:
return m_defined_parts; return m_defined_parts;
} }
void set_cli_pos(const char *pos) /*
When initializing m_parts[n] in the constructor, a EOM could happen.
This method checks that all defined parts were initialized without error.
*/
bool is_sane() const
{ {
m_cli_pos= pos; for (uint i= 0; i < m_defined_parts; i++)
{
if (m_parts[i].is_null())
return false; // E.g. EOM happened in the constructor
}
return true;
}
void set_pos(const Lex_ident_cli_st &pos)
{
m_pos= pos;
} }
void set_spvar(sp_variable *spvar) void set_spvar(sp_variable *spvar)
{ {

View File

@@ -8966,8 +8966,10 @@ Item *LEX::create_item_ident(THD *thd,
const Lex_ident_cli query_fragment(start, end - start); const Lex_ident_cli query_fragment(start, end - start);
if (sys_a.is_null() || sys_b.is_null()) if (sys_a.is_null() || sys_b.is_null())
return nullptr; // EOM return nullptr; // EOM
return spv->type_handler()->create_item_method(thd, sys_a, sys_b, return spv->type_handler()->
NULL, query_fragment); create_item_method(thd,
Type_handler::object_method_type_t::FUNCTION,
sys_a, sys_b, NULL, query_fragment);
} }
} }
@@ -9291,7 +9293,7 @@ bool LEX::set_variable(const Qualified_ident *ident,
{ {
if (unlikely(ident->part(2).length)) if (unlikely(ident->part(2).length))
{ {
thd->parse_error(ER_SYNTAX_ERROR, ident->pos()); thd->parse_error(ER_SYNTAX_ERROR, ident->pos().str);
return true; return true;
} }
@@ -10124,6 +10126,51 @@ bool LEX::call_statement_start_or_lvalue_assign(THD *thd,
} }
bool LEX::direct_call(THD *thd, const Qualified_ident *ident,
List<Item> *args)
{
DBUG_ASSERT(ident);
if (!ident->spvar())
return false; // A procedure call
/*
ident->part(0) is a known SP variable.
Search for a procedure method of the variable, e.g.:
assoc_array_var.delete('key');
*/
Item *item;
if (!ident->spvar()->type_handler()->has_methods() ||
ident->part(1).is_null() ||
!ident->part(2).is_null())
{
/*
E.g.:
spvar_int.method(); -- The SP variable data type does not have methods
spvar.step1.step2(); -- A 3-step method call of an SP variable
(we don't have 3-step methods yet)
*/
thd->parse_error(ER_SYNTAX_ERROR, ident->pos().str);
return true;
}
if (!(item= ident->spvar()->type_handler()->
create_item_method(thd,
Type_handler::object_method_type_t::PROCEDURE,
ident->part(0), ident->part(1),
args, ident->pos())))
{
DBUG_ASSERT(thd->is_error());
return true;
}
sql_command= SQLCOM_DO;
DBUG_ASSERT(insert_list == nullptr);
if (!(insert_list= List<Item>::make(thd->mem_root, item)))
return true;
return false;
}
bool LEX::assoc_assign_start(THD *thd, Qualified_ident *ident) bool LEX::assoc_assign_start(THD *thd, Qualified_ident *ident)
{ {
if (unlikely(ident->spvar() == NULL)) if (unlikely(ident->spvar() == NULL))
@@ -10134,7 +10181,7 @@ bool LEX::assoc_assign_start(THD *thd, Qualified_ident *ident)
LEX *lex= this; LEX *lex= this;
lex->set_stmt_init(); lex->set_stmt_init();
if (sp_create_assignment_lex(thd, ident->pos())) if (sp_create_assignment_lex(thd, ident->pos().str))
return true; return true;
return false; return false;
@@ -10523,9 +10570,10 @@ Item *LEX::make_item_func_or_method_call(THD *thd,
(spv= spcont->find_variable(&sys_a, false)) && (spv= spcont->find_variable(&sys_a, false)) &&
spv->type_handler()->has_methods()) spv->type_handler()->has_methods())
{ {
if (Item *item= spv->type_handler()->create_item_method(thd, if (Item *item= spv->type_handler()->
sys_a, sys_b, args, create_item_method(thd,
query_fragment)) Type_handler::object_method_type_t::FUNCTION,
sys_a, sys_b, args, query_fragment))
{ {
item->set_name(thd, query_fragment, thd->charset()); item->set_name(thd, query_fragment, thd->charset());
return item; return item;
@@ -12766,6 +12814,60 @@ Lex_field_type_st::set_handler_length_flags(const Type_handler *handler,
} }
bool LEX::declare_type_record(THD *thd,
const Lex_ident_sys_st &type_name,
Row_definition_list *fields)
{
sp_type_def *tdef=
new (thd->mem_root) sp_type_def_record(Lex_ident_column(type_name), fields);
if (unlikely(!tdef || spcont->type_defs_add(thd, tdef)))
return true;
return false;
}
bool LEX::declare_type_assoc_array(THD *thd,
const Lex_ident_sys_st &type_name,
Spvar_definition *key,
Spvar_definition *value)
{
const auto aa= "associative_array"_Lex_ident_plugin;
const Type_handler *th= Type_handler::handler_by_name_or_error(thd, aa);
if (unlikely(!th))
return true;
sp_type_def *tdef=
new (thd->mem_root) sp_type_def_composite2(Lex_ident_column(type_name),
th, key, value);
if (unlikely(!tdef || spcont->type_defs_add(thd, tdef)))
return true;
Column_definition def;
def.set_handler(th);
def.set_attr_const_void_ptr(0, tdef);
Lex_field_type_st ltype;
ltype.set(th);
return def.type_handler()->
Column_definition_set_attributes(thd, &def, ltype,
COLUMN_DEFINITION_ROUTINE_LOCAL);
}
bool LEX::set_field_type_udt_or_typedef(Lex_field_type_st *type,
const LEX_CSTRING &name,
const Lex_length_and_dec_st &attr)
{
bool is_typedef= false;
if (unlikely(set_field_type_typedef(type, name, &is_typedef)))
return true;
if (is_typedef)
return false;
return set_field_type_udt(type, name, attr);
}
bool LEX::set_field_type_udt(Lex_field_type_st *type, bool LEX::set_field_type_udt(Lex_field_type_st *type,
const LEX_CSTRING &name, const LEX_CSTRING &name,
const Lex_length_and_dec_st &attr) const Lex_length_and_dec_st &attr)
@@ -12789,34 +12891,21 @@ bool LEX::set_cast_type_udt(Lex_cast_type_st *type,
} }
bool LEX::set_field_type_composite(Lex_field_type_st *type, bool LEX::set_field_type_typedef(Lex_field_type_st *type,
const LEX_CSTRING &name, const LEX_CSTRING &name,
bool with_collection, bool *is_typedef)
bool *is_composite)
{ {
DBUG_ASSERT(type); DBUG_ASSERT(type);
DBUG_ASSERT(is_composite); DBUG_ASSERT(is_typedef);
sp_type_def *composite= NULL; *is_typedef= false;
*is_composite= false;
if (spcont) if (spcont)
{ {
if ((composite= spcont->find_type_def(name, false))) if (const sp_type_def *composite= spcont->find_type_def(name, false))
{ {
if (with_collection || type->set(composite->type_handler(), NULL);
likely(composite->type_handler() == &type_handler_row)) last_field->set_attr_const_void_ptr(0, composite);
{ *is_typedef= true;
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;
} }
} }

View File

@@ -3918,6 +3918,15 @@ public:
bool call_statement_start(THD *thd, const Qualified_ident *ident); bool call_statement_start(THD *thd, const Qualified_ident *ident);
bool call_statement_start_or_lvalue_assign(THD *thd, bool call_statement_start_or_lvalue_assign(THD *thd,
Qualified_ident *ident); Qualified_ident *ident);
/*
Create instructions for a direct call (without the CALL keyword):
sp1; - a schema procedure call
db1.sp1; - a schema procedure call
pkg1.sp1; - a package procedure call
db1.pkg1.sp1; - a package procedure call
assoc_array.delete - an SP variable procedure method call
*/
bool direct_call(THD *thd, const Qualified_ident *ident, List<Item> *args);
bool assoc_assign_start(THD *thd, Qualified_ident *ident); bool assoc_assign_start(THD *thd, Qualified_ident *ident);
sp_variable *find_variable(const LEX_CSTRING *name, sp_variable *find_variable(const LEX_CSTRING *name,
@@ -4988,10 +4997,19 @@ public:
bool set_cast_type_udt(Lex_cast_type_st *type, bool set_cast_type_udt(Lex_cast_type_st *type,
const LEX_CSTRING &name); const LEX_CSTRING &name);
bool set_field_type_composite(Lex_field_type_st *type, bool declare_type_record(THD *thd,
const LEX_CSTRING &name, const Lex_ident_sys_st &type_name,
bool with_collection, Row_definition_list *fields);
bool *is_composite); bool declare_type_assoc_array(THD *thd,
const Lex_ident_sys_st &type_name,
Spvar_definition *key,
Spvar_definition *value);
bool set_field_type_typedef(Lex_field_type_st *type,
const LEX_CSTRING &name,
bool *is_typedef);
bool set_field_type_udt_or_typedef(Lex_field_type_st *type,
const LEX_CSTRING &name,
const Lex_length_and_dec_st &attr);
bool map_data_type(const Lex_ident_sys_st &schema, bool map_data_type(const Lex_ident_sys_st &schema,
Lex_field_type_st *type) const; Lex_field_type_st *type) const;

View File

@@ -293,6 +293,7 @@ public:
inline list_node* last_node() { return *last; } inline list_node* last_node() { return *last; }
inline list_node* first_node() { return first;} inline list_node* first_node() { return first;}
inline void *head() { return first->info; } inline void *head() { return first->info; }
inline const void *head() const { return first->info; }
inline void **head_ref() { return first != &end_of_list ? &first->info : 0; } inline void **head_ref() { return first != &end_of_list ? &first->info : 0; }
inline bool is_empty() const { return first == &end_of_list ; } inline bool is_empty() const { return first == &end_of_list ; }
inline list_node *last_ref() { return &end_of_list; } inline list_node *last_ref() { return &end_of_list; }
@@ -503,6 +504,7 @@ public:
inline bool push_front(const T *a, MEM_ROOT *mem_root) inline bool push_front(const T *a, MEM_ROOT *mem_root)
{ return base_list::push_front((void*) a, mem_root); } { return base_list::push_front((void*) a, mem_root); }
inline T* head() {return (T*) base_list::head(); } inline T* head() {return (T*) base_list::head(); }
inline const T* head() const {return (const T*) base_list::head(); }
inline T** head_ref() {return (T**) base_list::head_ref(); } inline T** head_ref() {return (T**) base_list::head_ref(); }
inline T* pop() {return (T*) base_list::pop(); } inline T* pop() {return (T*) base_list::pop(); }
inline void append(List<T> *list) { base_list::append(list); } inline void append(List<T> *list) { base_list::append(list); }

View File

@@ -489,6 +489,10 @@ public:
if (!alloced) if (!alloced)
thread_specific= 1; thread_specific= 1;
} }
bool get_thread_specific() const
{
return thread_specific;
}
bool is_alloced() const { return alloced; } bool is_alloced() const { return alloced; }
inline uint32 alloced_length() const { return Alloced_length;} inline uint32 alloced_length() const { return Alloced_length;}
inline uint32 extra_allocation() const { return extra_alloc;} inline uint32 extra_allocation() const { return extra_alloc;}

View File

@@ -5297,7 +5297,7 @@ raise_bad_data_type_for_functor(const Qualified_ident &ident,
DBUG_ASSERT(ident.defined_parts() > 0 && ident.defined_parts() <= 3); DBUG_ASSERT(ident.defined_parts() > 0 && ident.defined_parts() <= 3);
char param[MYSQL_ERRMSG_SIZE]; char param[MYSQL_ERRMSG_SIZE];
uint used= 0; size_t used= 0;
for (uint i= 0; i < ident.defined_parts() && used < sizeof(param); i++) for (uint i= 0; i < ident.defined_parts() && used < sizeof(param); i++)
{ {
used+= my_snprintf(param + used, used+= my_snprintf(param + used,

View File

@@ -3924,6 +3924,13 @@ protected:
enum_field_types type) enum_field_types type)
const; const;
public: public:
enum class object_method_type_t
{
FUNCTION,
PROCEDURE
};
static const Type_handler *handler_by_name(THD *thd, const LEX_CSTRING &name); static const Type_handler *handler_by_name(THD *thd, const LEX_CSTRING &name);
static const Type_handler *handler_by_name_or_error(THD *thd, static const Type_handler *handler_by_name_or_error(THD *thd,
const LEX_CSTRING &name); const LEX_CSTRING &name);
@@ -4690,7 +4697,7 @@ public:
DBUG_ASSERT(0); // Should have checked has_functors(). DBUG_ASSERT(0); // Should have checked has_functors().
return nullptr; return nullptr;
} }
virtual Item *create_item_method(THD *thd, virtual Item *create_item_method(THD *thd, object_method_type_t type,
const Lex_ident_sys &ca, const Lex_ident_sys &ca,
const Lex_ident_sys &cb, const Lex_ident_sys &cb,
List<Item> *args, List<Item> *args,

View File

@@ -392,11 +392,34 @@ public:
bool Item_func_div_fix_length_and_dec(Item_func_div *) 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; 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, Get a key value from an expression and convert it into the internal form
LEX_CSTRING& out_key) const suitable for the variable.
@param thd - Current thd
@param var - The variable aaddress
@param key - The expression
@param buffer - The string buffer, e.g. used for val_str().
@retutns - An null LEX_CSTRING {0,0} in case if key->val_str()
returned NULL, or if could not convert the value to
the internal form
- A non-null LEX_CSTRING, usually pointing to "buffer",
with an internal key representation
In case of an VARCHAR-key asssoc array, the key value is converted
to the character set explicitlye or implicitly specified in the
INDEX BY clause.
In case of an integer-key assoc array, the key value is checked to
be inside the allowed range according to the integer type specified
in the INDEX BY clause.
*/
virtual LEX_CSTRING key_to_lex_cstring(THD *thd,
const sp_rcontext_addr &var,
Item **key,
String *buffer) const
{ {
return false; return Lex_cstring();
} }
/* /*

View File

@@ -1437,7 +1437,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%type <json_on_response> json_on_response %type <json_on_response> json_on_response
%type <Lex_field_type> field_type field_type_all field_type_all_builtin %type <Lex_field_type> field_type field_type_all field_type_all_builtin
field_type_all_with_composites field_type_all_with_typedefs
qualified_field_type qualified_field_type
field_type_numeric field_type_numeric
field_type_string field_type_string
@@ -1448,8 +1448,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%ifdef ORACLE %ifdef ORACLE
%type <spvar_definition> assoc_array_table_types %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 %endif
%type <ident_cli> %type <ident_cli>
@@ -3633,7 +3631,7 @@ sp_decl_idents_init_vars:
sp_decl_variable_list: sp_decl_variable_list:
sp_decl_idents_init_vars sp_decl_idents_init_vars
field_type_all_with_composites field_type_all_with_typedefs
{ {
if (Lex->last_field->set_attributes(thd, $2, if (Lex->last_field->set_attributes(thd, $2,
COLUMN_DEFINITION_ROUTINE_LOCAL)) COLUMN_DEFINITION_ROUTINE_LOCAL))
@@ -6536,45 +6534,15 @@ field_type_all:
} }
; ;
%ifdef ORACLE field_type_all_with_typedefs:
field_type_all_with_record:
field_type_all_builtin field_type_all_builtin
{ {
Lex->map_data_type(Lex_ident_sys(), &($$= $1)); Lex->map_data_type(Lex_ident_sys(), &($$= $1));
} }
| udt_name float_options srid_option | udt_name float_options srid_option
{ {
bool is_composite= false; if (unlikely(Lex->set_field_type_udt_or_typedef(&$$, $1, $2)))
if (unlikely(Lex->set_field_type_composite(&$$, $1, false,
&is_composite)))
MYSQL_YYABORT; MYSQL_YYABORT;
if (!is_composite)
{
if (Lex->set_field_type_udt(&$$, $1, $2))
MYSQL_YYABORT;
}
}
;
%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)
{
if (Lex->set_field_type_udt(&$$, $1, $2))
MYSQL_YYABORT;
}
} }
; ;
@@ -19454,13 +19422,8 @@ direct_call_or_lvalue_assign:
direct_call_statement: direct_call_statement:
direct_call_or_lvalue_assign opt_parenthesized_opt_sp_cparams direct_call_or_lvalue_assign opt_parenthesized_opt_sp_cparams
{ {
if (unlikely($1->spvar())) if (Lex->direct_call(thd, $1, $2) ||
{ Lex->check_cte_dependencies_and_resolve_references())
thd->parse_error(ER_SYNTAX_ERROR, $1->pos());
MYSQL_YYABORT;
}
if (Lex->check_cte_dependencies_and_resolve_references())
MYSQL_YYABORT; MYSQL_YYABORT;
} }
; ;
@@ -19736,19 +19699,22 @@ ident_cli_directly_assignable:
optionally_qualified_directly_assignable: optionally_qualified_directly_assignable:
ident_cli_directly_assignable ident_cli_directly_assignable
{ {
if (unlikely(!($$= new (thd->mem_root) Qualified_ident(thd, $1)))) if (unlikely(!($$= new (thd->mem_root) Qualified_ident(thd, $1)) ||
!$$->is_sane()))
MYSQL_YYABORT; MYSQL_YYABORT;
} }
| ident_cli_directly_assignable '.' ident | ident_cli_directly_assignable '.' ident_cli
{ {
if (unlikely(!($$= new (thd->mem_root) Qualified_ident(thd, $1, if (unlikely(!($$= new (thd->mem_root) Qualified_ident(thd, $1,
$3)))) $3)) ||
!$$->is_sane()))
MYSQL_YYABORT; MYSQL_YYABORT;
} }
| ident_cli_directly_assignable '.' ident '.' ident | ident_cli_directly_assignable '.' ident_cli '.' ident_cli
{ {
if (unlikely(!($$= new (thd->mem_root) Qualified_ident(thd, $1, if (unlikely(!($$= new (thd->mem_root) Qualified_ident(thd, $1,
$3, $5)))) $3, $5)) ||
!$$->is_sane()))
MYSQL_YYABORT; MYSQL_YYABORT;
} }
; ;
@@ -19759,7 +19725,7 @@ set_assign:
{ {
LEX *lex=Lex; LEX *lex=Lex;
lex->set_stmt_init(); lex->set_stmt_init();
if (sp_create_assignment_lex(thd, $1->pos())) if (sp_create_assignment_lex(thd, $1->pos().str))
MYSQL_YYABORT; MYSQL_YYABORT;
} }
set_expr_or_default set_expr_or_default
@@ -20364,7 +20330,7 @@ typed_ident:
; ;
assoc_array_table_types: assoc_array_table_types:
field_type_all_with_record field_type_all_with_typedefs
{ {
if (Lex->last_field->set_attributes(thd, $1, if (Lex->last_field->set_attributes(thd, $1,
COLUMN_DEFINITION_ROUTINE_LOCAL)) COLUMN_DEFINITION_ROUTINE_LOCAL))
@@ -20403,31 +20369,6 @@ assoc_array_table_types:
} }
; ;
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_non_handler:
sp_decl_variable_list sp_decl_variable_list
| ident_directly_assignable CONDITION_SYM FOR_SYM sp_cond | ident_directly_assignable CONDITION_SYM FOR_SYM sp_cond
@@ -20468,27 +20409,22 @@ sp_decl_non_handler:
} }
| typed_ident IS RECORD_SYM rec_type_body | typed_ident IS RECORD_SYM rec_type_body
{ {
if (unlikely(Lex->spcont-> if (unlikely(Lex->declare_type_record(thd, $1, $4)))
type_defs_declare_record(thd, Lex_ident_column($1), $4)))
MYSQL_YYABORT; MYSQL_YYABORT;
$$.vars= $$.conds= $$.hndlrs= $$.curs= 0; $$.vars= $$.conds= $$.hndlrs= $$.curs= 0;
} }
| typed_ident IS TABLE_SYM OF_SYM assoc_array_table_types | typed_ident IS TABLE_SYM OF_SYM assoc_array_table_types
{ {
Lex->init_last_field(new (thd->mem_root) Column_definition(), Lex->init_last_field(new (thd->mem_root) Spvar_definition(),
&empty_clex_str); &empty_clex_str);
} }
assoc_array_index_type INDEX_SYM BY field_type
{ {
const auto aa= "associative_array"_Lex_ident_plugin; if (Lex->last_field->set_attributes(thd, $9,
const Type_handler *th= COLUMN_DEFINITION_ROUTINE_LOCAL))
Type_handler::handler_by_name_or_error(thd, aa); MYSQL_YYABORT;
if (unlikely(!th || auto def= static_cast<Spvar_definition*>(Lex->last_field);
Lex->spcont-> if (unlikely(Lex->declare_type_assoc_array(thd, $1, def, $5)))
type_defs_declare_composite2(thd,
Lex_ident_column($1),
th, $7, $5)))
MYSQL_YYABORT; MYSQL_YYABORT;
$$.vars= $$.conds= $$.hndlrs= $$.curs= 0; $$.vars= $$.conds= $$.hndlrs= $$.curs= 0;
} }