Skip to content

Commit 7c12b77

Browse files
author
Guilhem Bichot
committed
Bug#21833760 CALC_DAYNR: ASSERTION `DELSUM+(INT) Y/4-TEMP >= 0' FAILED
Virtual generated column ("vgcol") c13 depends on vgcol c11 which depends on base columns c8 and c9. A query reads c13. Thus c11, and thus c8, c9, are added to read_set. An index-only scan on c13 is used. It reads only c13 from the engine. update_generated_read_fields() sees that c11 is in read_set, and is not in the index, so it calculates it, based on an uninitialized value of c8, c9; DATE/TIME functions don't support really abnormal values of arguments, and this causes problems. If using INT columns and simpler generation expressions, we still get Valgrind warnings. Fix: if the Optimizer said "index-only scan" (table->keyread=true) it means it knows that the index provides everything; so, if keyread=true don't try to calculate anything. Note that this logic may have to be reconsidered when we fix Bug 21815348, see the @todo in code.
1 parent 2a876f5 commit 7c12b77

File tree

3 files changed

+77
-44
lines changed

3 files changed

+77
-44
lines changed

mysql-test/suite/gcol/r/gcol_bugfixes.result

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,3 +466,29 @@ SELECT * FROM t;
466466
a b
467467
1 1
468468
DROP TABLE t;
469+
#
470+
# Bug#21833760 CALC_DAYNR: ASSERTION `DELSUM+(INT) Y/4-TEMP >= 0' FAILED.
471+
#
472+
CREATE TABLE C(
473+
c1 INT AUTO_INCREMENT,
474+
c8 DATETIME,
475+
c9 TIME,
476+
c11 TIME GENERATED ALWAYS AS(ADDTIME(c8,c9)) VIRTUAL,
477+
c13 TIME GENERATED ALWAYS AS(ADDTIME(c8,c11)) VIRTUAL,
478+
PRIMARY KEY(c1),
479+
UNIQUE KEY(c13)
480+
);
481+
INSERT INTO C (c8,c9) VALUES('1970-01-01',0),('1970-01-01',1);
482+
CREATE VIEW view_C AS SELECT * FROM C;
483+
SELECT /*+ NO_BNL(t1) */ t1.c13 FROM C AS t2 STRAIGHT_JOIN C AS t1 FORCE INDEX(c13);
484+
c13
485+
00:00:00
486+
00:00:01
487+
00:00:00
488+
00:00:01
489+
SELECT DISTINCT t1.c13 FROM C AS t1, view_C AS t2;
490+
c13
491+
00:00:00
492+
00:00:01
493+
DROP TABLE C;
494+
DROP VIEW view_C;

mysql-test/suite/gcol/t/gcol_bugfixes.test

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,3 +461,27 @@ SELECT f();
461461
DROP FUNCTION f;
462462
SELECT * FROM t;
463463
DROP TABLE t;
464+
465+
--echo #
466+
--echo # Bug#21833760 CALC_DAYNR: ASSERTION `DELSUM+(INT) Y/4-TEMP >= 0' FAILED.
467+
--echo #
468+
469+
CREATE TABLE C(
470+
c1 INT AUTO_INCREMENT,
471+
c8 DATETIME,
472+
c9 TIME,
473+
c11 TIME GENERATED ALWAYS AS(ADDTIME(c8,c9)) VIRTUAL,
474+
c13 TIME GENERATED ALWAYS AS(ADDTIME(c8,c11)) VIRTUAL,
475+
PRIMARY KEY(c1),
476+
UNIQUE KEY(c13)
477+
);
478+
479+
INSERT INTO C (c8,c9) VALUES('1970-01-01',0),('1970-01-01',1);
480+
481+
CREATE VIEW view_C AS SELECT * FROM C;
482+
483+
SELECT /*+ NO_BNL(t1) */ t1.c13 FROM C AS t2 STRAIGHT_JOIN C AS t1 FORCE INDEX(c13);
484+
SELECT DISTINCT t1.c13 FROM C AS t1, view_C AS t2;
485+
486+
DROP TABLE C;
487+
DROP VIEW view_C;

sql/table.cc

Lines changed: 27 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -7460,39 +7460,6 @@ bool is_simple_order(ORDER *order)
74607460
return TRUE;
74617461
}
74627462

7463-
/**
7464-
Check whether a given virtual generated column is part of a covering index.
7465-
7466-
@note that this ignores the number of key parts used by MySQL: if the index
7467-
is on (other, vgcol) and MySQL chooses to use it but only its first
7468-
key part, this function still returns true. Fortunately, if vgcol is needed
7469-
it is in read_set, and is part of the complete index, then InnoDB will read
7470-
the complete index entry (see build_template_needs_field()) and provide the
7471-
vgcol's value. So the number of key parts used by MySQL is irrelevant here.
7472-
7473-
@param table pointer of the table
7474-
@param index_no the number of index to be checked
7475-
@param vfield the pointer of checked virtual generated column
7476-
7477-
@return true if the column is covered by the given index.
7478-
*/
7479-
static bool
7480-
index_contains_this_virtual_gcol(const TABLE *table, uint index_no,
7481-
const Field *vfield)
7482-
{
7483-
DBUG_ASSERT(index_no != MAX_KEY && table->key_read);
7484-
const KEY *key= table->key_info + index_no;
7485-
7486-
for (KEY_PART_INFO *key_part= key->key_part;
7487-
key_part < key->key_part + key->user_defined_key_parts;
7488-
key_part++)
7489-
{
7490-
if (key_part->field == vfield)
7491-
return true;
7492-
}
7493-
return false;
7494-
}
7495-
74967463

74977464
/**
74987465
Repoint a table's fields from old_rec to new_rec
@@ -7512,21 +7479,42 @@ void repoint_field_to_record(TABLE *table, uchar *old_rec, uchar *new_rec)
75127479

75137480

75147481
/**
7515-
Evaluate each virtual generated column marked in read_set.
7482+
Evaluate necessary virtual generated columns.
75167483
This is used right after reading a row from the storage engine.
75177484
7518-
@note this is not necessary for stored generated fields.
7485+
@note this is not necessary for stored generated columns, as they are
7486+
provided by the storage engine.
75197487
75207488
@param buf[in,out] the buffer to store data
75217489
@param table the TABLE object
7522-
@param active_index the number of key for index scan(MAX_KEY is default)
7490+
@param active_index the number of key for index scan (MAX_KEY is default)
75237491
75247492
@return true if error.
7493+
7494+
@todo see below for potential conflict with Bug#21815348 .
75257495
*/
75267496
bool update_generated_read_fields(uchar *buf, TABLE *table, uint active_index)
75277497
{
75287498
DBUG_ENTER("update_generated_read_fields");
75297499
DBUG_ASSERT(table && table->vfield);
7500+
if (active_index != MAX_KEY && table->key_read)
7501+
{
7502+
/*
7503+
The covering index is providing all necessary columns, including
7504+
generated ones.
7505+
Note that this logic may have to be reconsidered when we fix
7506+
Bug#21815348; indeed, for that bug it could be possible to implement the
7507+
following optimization: if A is an indexed base column, and B is a
7508+
virtual generated column dependent on A, "select B from t" could choose
7509+
an index-only scan over the index of A and calculate values of B on the
7510+
fly. In that case, we would come here, however calculation of B would
7511+
still be needed.
7512+
Currently MySQL doesn't choose an index scan in that case because it
7513+
considers B as independent from A, in its index-scan decision logic.
7514+
*/
7515+
DBUG_RETURN(false);
7516+
}
7517+
75307518
int error= 0;
75317519

75327520
/*
@@ -7541,18 +7529,13 @@ bool update_generated_read_fields(uchar *buf, TABLE *table, uint active_index)
75417529
{
75427530
Field *vfield= *vfield_ptr;
75437531
DBUG_ASSERT(vfield->gcol_info && vfield->gcol_info->expr_item);
7544-
/**
7532+
/*
75457533
Only calculate those virtual generated fields that are marked in the
7546-
read_set bitmap and not filled.
7547-
@todo: Consider replacing index_contains_this_virtual_gcol with
7548-
a test on part_of_key below.
7534+
read_set bitmap.
75497535
*/
75507536
if (!vfield->stored_in_db &&
7551-
bitmap_is_set(table->read_set, vfield->field_index) &&
7552-
!(active_index != MAX_KEY && table->key_read &&
7553-
index_contains_this_virtual_gcol(table, active_index, vfield)))
7537+
bitmap_is_set(table->read_set, vfield->field_index))
75547538
{
7555-
/* Generate the actual value of the generated fields */
75567539
error= vfield->gcol_info->expr_item->save_in_field(vfield, 0);
75577540
DBUG_PRINT("info", ("field '%s' - updated", vfield->field_name));
75587541
if (error && !table->in_use->is_error())

0 commit comments

Comments
 (0)