mirror of
https://github.com/MariaDB/server.git
synced 2025-11-09 11:41:36 +03:00
This is a joint patch fixing the following problems:
MDEV-12875 Wrong VIEW column data type for COALESCE(int_column)
MDEV-12886 Different default for INT and BIGINT column in a VIEW for a SELECT with ROLLUP
MDEV-12916 Wrong column data type for an INT field of a cursor-anchored ROW variable
All above problem happened because the global function ::create_tmp_field()
called the top-level Item::create_tmp_field(), which made some tranformation
for INT-result data types. For example, INT(11) became BIGINT(11), because 11
is a corner case and it's not known if it fits or does not fit into INT range,
so Item::create_tmp_field() converted it to BIGINT(11) for safety.
The main idea of this patch is to avoid such tranformations.
1. Fixing Item::create_tmp_field() not to have a special case for INT_RESULT.
Item::create_tmp_field() is changed not to have a special case
for INT_RESULT (which earlier made a decision based on Item's max_length).
It now calls tmp_table_field_from_field_type() for INT_RESULT,
therefore preserves the original data type (e.g. INT, YEAR) without
conversion to BIGINT.
This change is valid, because a number of recent fixes
(e.g. in Item_func_int, Item_hybrid_func, Item_int, Item_splocal)
guarantee that item->type_handler() now properly returns
type_handler_long vs type_handler_longlong. So no adjustment by length
is needed any more for Items returning INT_RESULT.
After this change, Item::create_tmp_field() calls
tmp_table_field_from_field_type() for all XXX_RESULT, except REAL_RESULT.
2. Fixing Item::create_tmp_field() not to have a special case for REAL_RESULT.
Note, the reason for a special case for REAL_RESULT is to have a special
constructor for Field_double(), forcing Field_real::not_fixed to be set
to true.
Taking into account that only Item_sum descendants actually need a special
constructor call Field_double(not_fixed=true), not too loose precision
when mixing individual rows to the aggregate result:
- renaming Item::create_tmp_field() to Item_sum::create_tmp_field().
- changing Item::create_tmp_field() just to call
tmp_table_field_from_field_type() for all XXX_RESULT types.
A special case for REAL_RESULT in Item::create_tmp_field() is now gone.
Item::create_tmp_field() is now symmetric for all XXX_RESULT types,
and now just calls tmp_table_field_from_field_type().
3. Fixing Item_func::create_field_for_create_select() not to have
a special case for STRING_RESULT.
After changes #1 and #2, the code in
Item_func::create_field_for_create_select(), testing result_type(),
becomes useless, because: now Item::create_tmp_field() and
tmp_table_field_from_field_type() do exactly the same thing for all
XXX_RESULT types for Item_func descendants:
a. It calls tmp_table_field_from_field_type for STRING_RESULT directly.
b. For other XXX_RESULT, it goes through Item::create_tmp_field(),
which calls the global function ::create_tmp_field(),
which calls item->create_tmp_field() for FUNC_ITEM,
which calls tmp_table_field_from_field_type() again.
So removing the virtual implementation of
Item_func::create_field_for_create_select().
The inherited Item::create_field_for_create_select() now perfectly
does the job, as it also calls tmp_table_field_from_field_type()
for FUNC_ITEM, independently from XXX_RESULT type.
4. Taking into account #1 and #2, as well as some recent changes,
removing virtual implementations:
- Item_hybrid_func::create_tmp_field()
- Item_hybrid_func::create_field_for_create_select()
- Item_int_func::create_tmp_field()
- Item_int_func::create_field_for_create_select()
- Item_temporal_func::create_field_for_create_select()
The derived versions from Item now perfectly work.
5. Moving a piece of code from create_tmp_field_from_item()
to a new function create_tmp_field_from_item_finalize(),
to reuse it in two places (see #6).
6. Changing the code responsible for BIT->INT/BIGIN tranformation
(which is called for the cases when the created table, e.g. HEAP,
does not fully support BIT) not to call create_tmp_field_from_item(),
because the latter now calls tmp_table_field_from_field_type() instead
of create_tmp_field() and thefore cannot do BIT transformation.
So rewriting this code using a sequence of these calls:
- item->type_handler_long_or_longlong()
- handler->make_and_init_table_field()
- create_tmp_field_from_item_finalize()
7. Miscelaneous changes:
- Moving type_handler_long_or_longlong() from "protected" to "public",
as it's now needed in the global function create_tmp_field().
8. The above changes fixed MDEV-12875, MDEV-12886, MDEV-12916.
So adding tests for these bugs.
296 lines
5.3 KiB
Plaintext
296 lines
5.3 KiB
Plaintext
SET sql_mode=ORACLE;
|
|
|
|
--echo #
|
|
--echo # MDEV-10598 sql_mode=ORACLE: Variable declarations can go after cursor declarations
|
|
--echo #
|
|
|
|
--echo #
|
|
--echo # Variable after cursor declaration
|
|
--echo #
|
|
|
|
CREATE TABLE t1 (a INT);
|
|
insert into t1 values (1);
|
|
insert into t1 values (2);
|
|
DELIMITER $$;
|
|
CREATE PROCEDURE p1
|
|
AS
|
|
CURSOR c IS SELECT a FROM t1;
|
|
var1 varchar(10);
|
|
BEGIN
|
|
OPEN c;
|
|
fetch c into var1;
|
|
SELECT c%ROWCOUNT,var1;
|
|
close c;
|
|
END;
|
|
$$
|
|
DELIMITER ;$$
|
|
CALL p1;
|
|
DROP PROCEDURE p1;
|
|
drop table t1;
|
|
|
|
--echo #
|
|
--echo # Variable after condition declaration
|
|
--echo #
|
|
|
|
CREATE TABLE t1 (col1 INT);
|
|
insert into t1 values (1);
|
|
create unique index t1_col1 on t1 (col1);
|
|
DELIMITER $$;
|
|
CREATE PROCEDURE p1
|
|
AS
|
|
dup_key CONDITION FOR SQLSTATE '23000';
|
|
var1 varchar(40);
|
|
CONTINUE HANDLER FOR dup_key
|
|
BEGIN
|
|
var1:='duplicate key in index';
|
|
END;
|
|
BEGIN
|
|
var1:='';
|
|
insert into t1 values (1);
|
|
select var1;
|
|
END;
|
|
$$
|
|
DELIMITER ;$$
|
|
CALL p1;
|
|
DROP PROCEDURE p1;
|
|
drop table t1;
|
|
|
|
--echo #
|
|
--echo # Condition after cursor declaration
|
|
--echo #
|
|
|
|
CREATE TABLE t1 (col1 INT);
|
|
insert into t1 values (1);
|
|
create unique index t1_col1 on t1 (col1);
|
|
DELIMITER $$;
|
|
CREATE PROCEDURE p1
|
|
AS
|
|
var1 varchar(40);
|
|
var2 integer;
|
|
CURSOR c IS SELECT col1 FROM t1;
|
|
dup_key CONDITION FOR SQLSTATE '23000';
|
|
CONTINUE HANDLER FOR dup_key
|
|
BEGIN
|
|
var1:='duplicate key in index';
|
|
END;
|
|
BEGIN
|
|
var1:='';
|
|
insert into t1 values (1);
|
|
SELECT var1;
|
|
END;
|
|
$$
|
|
DELIMITER ;$$
|
|
CALL p1;
|
|
DROP PROCEDURE p1;
|
|
drop table t1;
|
|
|
|
--echo #
|
|
--echo # Cursor after handler declaration
|
|
--echo #
|
|
|
|
CREATE TABLE t1 (col1 INT);
|
|
insert into t1 values (1);
|
|
create unique index t1_col1 on t1 (col1);
|
|
DELIMITER $$;
|
|
--error ER_PARSE_ERROR
|
|
CREATE PROCEDURE p1
|
|
AS
|
|
var1 varchar(40);
|
|
var2 integer;
|
|
dup_key CONDITION FOR SQLSTATE '23000';
|
|
CONTINUE HANDLER FOR dup_key
|
|
BEGIN
|
|
var1:='duplicate key in index';
|
|
END;
|
|
CURSOR c IS SELECT col1 FROM t1;
|
|
BEGIN
|
|
var1:='';
|
|
insert into t1 values (1);
|
|
SELECT var1;
|
|
END;
|
|
$$
|
|
DELIMITER ;$$
|
|
drop table t1;
|
|
|
|
--echo #
|
|
--echo # Condition after handler declaration
|
|
--echo #
|
|
|
|
CREATE TABLE t1 (col1 INT);
|
|
insert into t1 values (1);
|
|
create unique index t1_col1 on t1 (col1);
|
|
DELIMITER $$;
|
|
--error ER_PARSE_ERROR
|
|
CREATE PROCEDURE p1
|
|
AS
|
|
var1 varchar(40);
|
|
var2 integer;
|
|
dup_key CONDITION FOR SQLSTATE '23000';
|
|
CURSOR c IS SELECT col1 FROM t1;
|
|
CONTINUE HANDLER FOR dup_key
|
|
BEGIN
|
|
var1:='duplicate key in index';
|
|
END;
|
|
divide_by_zero CONDITION FOR SQLSTATE '22012';
|
|
BEGIN
|
|
var1:='';
|
|
insert into t1 values (1);
|
|
SELECT var1;
|
|
END;
|
|
$$
|
|
DELIMITER ;$$
|
|
drop table t1;
|
|
|
|
--echo #
|
|
--echo # Variable after handler declaration
|
|
--echo #
|
|
|
|
CREATE TABLE t1 (col1 INT);
|
|
insert into t1 values (1);
|
|
create unique index t1_col1 on t1 (col1);
|
|
DELIMITER $$;
|
|
--error ER_PARSE_ERROR
|
|
CREATE PROCEDURE p1
|
|
AS
|
|
var1 varchar(40);
|
|
var2 integer;
|
|
dup_key CONDITION FOR SQLSTATE '23000';
|
|
CURSOR c IS SELECT col1 FROM t1;
|
|
CONTINUE HANDLER FOR dup_key
|
|
BEGIN
|
|
var1:='duplicate key in index';
|
|
END;
|
|
divide_by_zero CONDITION FOR SQLSTATE '22012';
|
|
BEGIN
|
|
var1:='';
|
|
insert into t1 values (1);
|
|
SELECT var1;
|
|
END;
|
|
$$
|
|
DELIMITER ;$$
|
|
drop table t1;
|
|
|
|
--echo #
|
|
--echo # Variable after cursor (inner block)
|
|
--echo #
|
|
|
|
CREATE TABLE t1 (col1 INT);
|
|
insert into t1 values (1);
|
|
insert into t1 values (2);
|
|
create unique index t1_col1 on t1 (col1);
|
|
DELIMITER $$;
|
|
CREATE PROCEDURE p1
|
|
AS
|
|
CURSOR c IS SELECT col1 FROM t1;
|
|
var1 varchar(40);
|
|
BEGIN
|
|
OPEN c;
|
|
begin
|
|
declare
|
|
CURSOR c IS SELECT col1 FROM t1 where col1=2;
|
|
var2 integer;
|
|
dup_key CONDITION FOR SQLSTATE '23000';
|
|
CONTINUE HANDLER FOR dup_key
|
|
BEGIN
|
|
var1:='duplicate key in index';
|
|
END;
|
|
begin
|
|
OPEN c;
|
|
fetch c into var1;
|
|
SELECT 'inner cursor',var1;
|
|
insert into t1 values (2);
|
|
close c;
|
|
end;
|
|
end;
|
|
SELECT var1;
|
|
fetch c into var1;
|
|
SELECT c%ROWCOUNT,var1;
|
|
begin
|
|
insert into t1 values (2);
|
|
exception when 1062 then
|
|
begin
|
|
SELECT 'dup key caugth';
|
|
end;
|
|
end;
|
|
close c;
|
|
END;
|
|
$$
|
|
DELIMITER ;$$
|
|
CALL p1;
|
|
DROP PROCEDURE p1;
|
|
drop table t1;
|
|
|
|
--echo #
|
|
--echo # Cursor declaration and row type declaration in same block
|
|
--echo #
|
|
|
|
CREATE TABLE t1 (a INT, b VARCHAR(10));
|
|
insert into t1 values(1,'a');
|
|
delimiter $$;
|
|
CREATE PROCEDURE p1()
|
|
AS
|
|
CURSOR cur1 IS SELECT a FROM t1;
|
|
rec1 cur1%ROWTYPE;
|
|
BEGIN
|
|
rec1.a:= 10;
|
|
END;
|
|
$$
|
|
delimiter ;$$
|
|
call p1;
|
|
DROP PROCEDURE p1;
|
|
drop table t1;
|
|
|
|
|
|
--echo #
|
|
--echo # Recursive cursor and cursor%ROWTYPE declarations in the same block
|
|
--echo #
|
|
|
|
delimiter $$;
|
|
CREATE PROCEDURE p1
|
|
AS
|
|
a INT:=10;
|
|
b VARCHAR(10):='b0';
|
|
c DOUBLE:=0.1;
|
|
CURSOR cur1 IS SELECT a, b, c;
|
|
rec1 cur1%ROWTYPE;
|
|
CURSOR cur2 IS SELECT rec1.a + 1 "a", rec1.b||'0' AS b, rec1.c AS c;
|
|
rec2 cur2%ROWTYPE;
|
|
BEGIN
|
|
OPEN cur1;
|
|
FETCH cur1 INTO rec1;
|
|
CLOSE cur1;
|
|
SELECT rec1.a;
|
|
OPEN cur2;
|
|
FETCH cur2 INTO rec2;
|
|
CLOSE cur2;
|
|
SELECT rec2.a;
|
|
CREATE TABLE t2 AS SELECT rec2.a AS a, rec2.b AS b, rec2.c AS c;
|
|
SHOW CREATE TABLE t2;
|
|
DROP TABLE t2;
|
|
END;
|
|
$$
|
|
DELIMITER ;$$
|
|
CALL p1();
|
|
DROP PROCEDURE p1;
|
|
|
|
|
|
--echo #
|
|
--echo # MDEV-12916 Wrong column data type for an INT field of a cursor-anchored ROW variable
|
|
--echo #
|
|
|
|
DELIMITER $$;
|
|
CREATE PROCEDURE p1
|
|
AS
|
|
a INT DEFAULT 10;
|
|
CURSOR cur1 IS SELECT a;
|
|
rec1 cur1%ROWTYPE;
|
|
BEGIN
|
|
CREATE TABLE t1 AS SELECT rec1.a;
|
|
SHOW CREATE TABLE t1;
|
|
DROP TABLE t1;
|
|
END;
|
|
$$
|
|
DELIMITER ;$$
|
|
CALL p1();
|
|
DROP PROCEDURE p1;
|