Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion include/myisam.h
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,6 @@ typedef struct st_columndef /* column information */
uint32 offset; /* Offset to position in row */
uint8 null_bit; /* If column may be 0 */
uint16 null_pos; /* position for null marker */

#ifndef NOT_PACKED_DATABASES
void (*unpack)(struct st_columndef *rec,struct st_mi_bit_buff *buff,
uchar *start,uchar *end);
Expand Down
211 changes: 211 additions & 0 deletions mysql-test/suite/innodb/r/innodb_null_only.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
CREATE TABLE t (
id INT PRIMARY KEY,
b MEDIUMBLOB NULL,
c INT NOT NULL
) ENGINE=InnoDB;
INSERT INTO t VALUES
(1, REPEAT('a', 100000), 1),
(2, NULL, 2),
(3, REPEAT('b', 100000), 3),
(4, NULL, 4);
# Restart with buffer pool dump/load disabled so blob-page-read metrics
# reflect a cold cache, not pages left from table setup.
SET GLOBAL innodb_monitor_disable='module_buffer_page';
SET GLOBAL innodb_monitor_reset_all='module_buffer_page';
SET GLOBAL innodb_monitor_enable='module_buffer_page';
# Reset innodb_metrics only before blob-read checks; use FLUSH STATUS
# to isolate per-query session status like Null_only_columns.
FLUSH STATUS;
SELECT COUNT(*) FROM t WHERE b IS NULL;
COUNT(*)
2
SELECT VARIABLE_VALUE AS null_only_columns
FROM information_schema.session_status
WHERE VARIABLE_NAME='Null_only_columns';
null_only_columns
1
# Sum the three blob-page-read counters to cover uncompressed and
# compressed off-page blob representations in one assertion.
SELECT SUM(COUNT) AS blob_reads
FROM information_schema.innodb_metrics
WHERE NAME IN ('buffer_page_read_blob','buffer_page_read_zblob',
'buffer_page_read_zblob2');
blob_reads
0
FLUSH STATUS;
SELECT COUNT(*) FROM t WHERE b <=> NULL;
COUNT(*)
2
SELECT VARIABLE_VALUE AS null_only_columns
FROM information_schema.session_status
WHERE VARIABLE_NAME='Null_only_columns';
null_only_columns
1
SELECT SUM(COUNT) AS blob_reads
FROM information_schema.innodb_metrics
WHERE NAME IN ('buffer_page_read_blob','buffer_page_read_zblob',
'buffer_page_read_zblob2');
blob_reads
0
SET GLOBAL innodb_monitor_disable='module_buffer_page';
SET GLOBAL innodb_monitor_reset_all='module_buffer_page';
SET GLOBAL innodb_monitor_enable='module_buffer_page';
FLUSH STATUS;
# Positive case: ON-clause shape should still mark the
# column as null-only and avoid off-page blob reads.
SELECT COUNT(*)
FROM t t1 JOIN t t2
ON t1.id= t2.id AND t1.b IS NULL;
COUNT(*)
2
SELECT VARIABLE_VALUE AS null_only_columns
FROM information_schema.session_status
WHERE VARIABLE_NAME='Null_only_columns';
null_only_columns
1
SELECT SUM(COUNT) AS blob_reads
FROM information_schema.innodb_metrics
WHERE NAME IN ('buffer_page_read_blob','buffer_page_read_zblob',
'buffer_page_read_zblob2');
blob_reads
0
SET GLOBAL innodb_monitor_disable='module_buffer_page';
SET GLOBAL innodb_monitor_reset_all='module_buffer_page';
SET GLOBAL innodb_monitor_enable='module_buffer_page';
FLUSH STATUS;
# Positive case: EXISTS shape should still mark the
# column as null-only and avoid off-page blob reads.
SELECT COUNT(*)
FROM t outer_t
WHERE EXISTS (SELECT 1
FROM t inner_t
WHERE inner_t.id= outer_t.id
AND inner_t.b IS NULL);
COUNT(*)
2
SELECT VARIABLE_VALUE AS null_only_columns
FROM information_schema.session_status
WHERE VARIABLE_NAME='Null_only_columns';
null_only_columns
1
SELECT SUM(COUNT) AS blob_reads
FROM information_schema.innodb_metrics
WHERE NAME IN ('buffer_page_read_blob','buffer_page_read_zblob',
'buffer_page_read_zblob2');
blob_reads
0
SET GLOBAL innodb_monitor_disable='module_buffer_page';
SET GLOBAL innodb_monitor_reset_all='module_buffer_page';
SET GLOBAL innodb_monitor_enable='module_buffer_page';
FLUSH STATUS;
# Negative case: CRC32(b) needs the actual blob value, so SQL should
# clear the null-only candidate and InnoDB should read blob pages.
SELECT SUM(CRC32(b)) FROM t WHERE b IS NULL OR c = 3;
SUM(CRC32(b))
4149198040
SELECT VARIABLE_VALUE = 0 AS null_only_used
FROM information_schema.session_status
WHERE VARIABLE_NAME='Null_only_columns';
null_only_used
1
SELECT SUM(COUNT) > 0 AS blob_reads
FROM information_schema.innodb_metrics
WHERE NAME IN ('buffer_page_read_blob','buffer_page_read_zblob',
'buffer_page_read_zblob2');
blob_reads
1
# Restart to force disk reads for the FOR UPDATE guardrail check.
SET GLOBAL innodb_monitor_disable='module_buffer_page';
SET GLOBAL innodb_monitor_reset_all='module_buffer_page';
SET GLOBAL innodb_monitor_enable='module_buffer_page';
FLUSH STATUS;
# FOR UPDATE keeps the SQL-layer candidate, but InnoDB disables the
# optimization under LOCK_X and should still read blob pages.
SELECT c FROM t WHERE b IS NULL FOR UPDATE;
c
2
4
SELECT VARIABLE_VALUE AS null_only_columns
FROM information_schema.session_status
WHERE VARIABLE_NAME='Null_only_columns';
null_only_columns
1
SELECT SUM(COUNT) > 0 AS blob_reads
FROM information_schema.innodb_metrics
WHERE NAME IN ('buffer_page_read_blob','buffer_page_read_zblob',
'buffer_page_read_zblob2');
blob_reads
1
DROP TABLE t;
# ICP check: the same query should explain and execute with index
# condition pushdown, while SQL still marks d IS NULL as null-only.
CREATE TABLE t_icp (
id INT PRIMARY KEY,
c INT NOT NULL,
d VARCHAR(64) NULL,
payload INT NOT NULL,
KEY idx_cd (c, d)
) ENGINE=InnoDB;
INSERT INTO t_icp VALUES
(1, 1, 'x', 10),
(2, 2, NULL, 20),
(3, 3, 'y', 30),
(4, 4, NULL, 40);
SET optimizer_switch='index_condition_pushdown=on';
FLUSH STATUS;
EXPLAIN SELECT payload
FROM t_icp FORCE INDEX (idx_cd)
WHERE c > 0 AND d IS NULL;
id select_type table type possible_keys key key_len ref rows Extra
# SIMPLE t_icp range idx_cd idx_cd # # # Using index condition
SELECT payload
FROM t_icp FORCE INDEX (idx_cd)
WHERE c > 0 AND d IS NULL;
payload
20
40
SELECT VARIABLE_VALUE AS null_only_columns
FROM information_schema.session_status
WHERE VARIABLE_NAME='Null_only_columns';
null_only_columns
1
SELECT VARIABLE_VALUE > 0 AS icp_used
FROM information_schema.session_status
WHERE VARIABLE_NAME='HANDLER_ICP_ATTEMPTS';
icp_used
1
DROP TABLE t_icp;
CREATE TABLE t_vcol (
id INT PRIMARY KEY,
b MEDIUMBLOB NULL,
v VARBINARY(1) AS (SUBSTR(b, 1, 1)) VIRTUAL
) ENGINE=InnoDB;
INSERT INTO t_vcol (id, b) VALUES
(1, REPEAT('a', 100000)),
(2, NULL),
(3, REPEAT('b', 100000)),
(4, NULL);
# Restart to validate virtual-column guardrail with cold-page metrics.
SET GLOBAL innodb_monitor_disable='module_buffer_page';
SET GLOBAL innodb_monitor_reset_all='module_buffer_page';
SET GLOBAL innodb_monitor_enable='module_buffer_page';
FLUSH STATUS;
# Virtual-column guardrail: SQL should not mark v as null-only, so
# the query should still force blob-page reads.
SELECT COUNT(*) FROM t_vcol WHERE v IS NULL;
COUNT(*)
2
SELECT VARIABLE_VALUE AS null_only_columns
FROM information_schema.session_status
WHERE VARIABLE_NAME='Null_only_columns';
null_only_columns
0
SELECT SUM(COUNT) > 0 AS blob_reads
FROM information_schema.innodb_metrics
WHERE NAME IN ('buffer_page_read_blob','buffer_page_read_zblob',
'buffer_page_read_zblob2');
blob_reads
1
DROP TABLE t_vcol;
SET GLOBAL innodb_monitor_disable='module_buffer_page';
SET GLOBAL innodb_monitor_reset_all='module_buffer_page';
Loading