首批通过分布式安全可靠测评,为关键业务系统打造
函数索引 Session 变量变换导致 SQL 报 -4377
更新时间:2024-01-22 01:56
问题描述
OceanBase 数据库中创建函数索引后,若函数索引表达式的结果依赖于系统变量,修改系统变量后再执行 update/delete 操作时可能报 -4377。
问题现象
创建函数索引后,若函数索引表达式的结果依赖于系统变量,修改系统变量后再执行 update/delete 操作时可能报 -4377。原因是索引表中的数据是按照系统变量的旧值计算的,系统变量更新后,根据索引表达式计算出的值若与索引表中保存的值不同,在检查时报错。OceanBase V4.0 版本之前的分支创建虚拟生成列,以及所有分支创建虚拟生成列上的索引都有同样的问题。
示例如下。
设置当前租户会话使用的时区。
obclient > ALTER SESSION SET time_zone='+08:00'; Query OK, 0 rows affected (0.000 sec)创建表。
obclient > CREATE TABLE t1(c1 timestamp, c2 varchar(10)); Query OK, 0 rows affected (0.032 sec)创建索引键为
SYS_EXTRACT_UTC(c1)的函数索引。obclient > CREATE INDEX i1 on t1(SYS_EXTRACT_UTC(c1)); Query OK, 0 rows affected (0.431 sec)设置当前租户会话日期时间格式。
obclient > ALTER SESSION SET NLS_TIMESTAMP_FORMAT ='YYYY-MM-DD HH24:MI:SS.FF'; Query OK, 1 row affected (0.005 sec)插入数据。
obclient > INSERT INTO t1 VALUES('2020-01-02 01:01:01','abc'); Query OK, 1 row affected (0.005 sec)obclient > INSERT INTO t1 VALUES('2020-01-01 01:01:01','aaa'); Query OK, 1 row affected (0.001 sec)执行查询语句。
obclient > SELECT c1,c2,SYS_EXTRACT_UTC(c1) FROM t1;输出内容如下。
+----------------------------+------+----------------------------+ | C1 | C2 | SYS_EXTRACT_UTC(C1) | +----------------------------+------+----------------------------+ | 2020-01-02 01:01:01.000000 | abc | 2020-01-01 15:01:01.000000 | | 2020-01-01 01:01:01.000000 | aaa | 2019-12-31 15:01:01.000000 | +----------------------------+------+----------------------------+SYS_EXTRACT_UTC系统函数的结果依赖于time_zone,因此修改time_zone后结果变化,但是索引表中的数据是按照 time_zone='+08:00' 计算的。设置修改当前租户会话使用的时区。
ALTER SESSION SET time_zone='+00:00';再次执行步骤 6 中的查询语句。
obclient > SELECT c1,c2,SYS_EXTRACT_UTC(c1) FROM t1;输出内容如下。
+----------------------------+------+----------------------------+ | C1 | C2 | SYS_EXTRACT_UTC(C1) | +----------------------------+------+----------------------------+ | 2020-01-02 01:01:01.000000 | abc | 2020-01-02 01:01:01.000000 | | 2020-01-01 01:01:01.000000 | aaa | 2020-01-01 01:01:01.000000 | +----------------------------+------+----------------------------+执行 update 语句,更新索引表数据时报错。
obclient > UPDATE t1 SET c2 = 'ddd' WHERE c2 = 'aaa'; ORA-00600: internal error code, arguments: -4377, fatal internal error in [check_old_row_legitimacy]执行查询语句,使用索 I1 时结果错误,预期是一行,实际执行结果是空集。
obclient > SELECT c1,c2,SYS_EXTRACT_UTC(c1) FROM t1 WHERE SYS_EXTRACT_UTC(c1) = '2020-01-01 17:01:01.000000'; Empty set (0.01 sec)展示步骤 10 中查询语句的执行计划。
obclinet > EXPLAIN SELECT c1,c2,SYS_EXTRACT_UTC(c1) FROM t1 WHERE SYS_EXTRACT_UTC(c1) = '2020-01-01 17:01:01.000000';输出内容如下。
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Query Plan | +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | ===================================== |ID|OPERATOR |NAME |EST. ROWS|COST| ------------------------------------- |0 |TABLE SCAN|T1(I1)|1 |92 | ===================================== Outputs & filters: ------------------------------------- 0 - output([T1.C1], [T1.C2], [SYS_EXTRACT_UTC(cast(T1.C1, TIMESTAMP_WITH_TIME_ZONE(-1, -1)))]), filter(nil), access([T1.C1], [T1.C2]), partitions(p0) | +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
问题风险及影响
该问题是用户行为引起,与常规 4377 问题不同。在原生 MySQL 数据库中,未对此场景做特殊处理,触发时不报错且执行结果错误。Oracle 数据库也仅通过添加 to_char 等内置表达式处理了部分场景。
在 OceanBase 数据库中,该问题的影响是修改系统变量后,更新表中数据会报 -4377;执行查询语句时,若使用该索引,查询结果错误。
影响的版本
OceanBase 数据库企业版 V2.x、V3.x、V4.x 版本,以及社区版 V3.1.x 版本。
注意
OceanBase 数据库在 V3.2.3 BP10 (oceanbase-3.2.3.3-110000092023091219) 、V3.2.4 BP6 (oceanbase-3.2.4.6-106000052023102710) 、V4.1.0 BP4 (oceanbase-4.1.0.2-104000032023092119) 、V4.2.0 Beta(oceanbase-ce-4.2.0.0-100000152023080109)及之后版本做了修复。但是仅调整报错信息以便于排查,修复后检测到函数索引/生成列在入参未改变,但是结果与旧值不同时,报 5500 错误。触发后,若确认是系统变量改变或系统函数行为不确定导致报错,则与老版本的处理一致,删除索引后重建。
解决办法及规避方式
解决办法
对于创建函数索引或生成列索引后出现该问题的场景,在修改系统变量后,需要删除并重新创建索引。
对于虚拟生成列上无索引,修改系统变量后报 -4377 的场景,可以登录系统租户下手动触发合并,下刷数据,执行如下命令。
obclient > ALTER SYSTEM MINOR FREEZE;OceanBase 数据库在 V3.2.3 BP10 (oceanbase-3.2.3.3-110000092023091219) 、V3.2.4 BP6 (oceanbase-3.2.4.6-106000052023102710) 、V4.1.0 BP4 (oceanbase-4.1.0.2-104000032023092119) 、V4.2.0 Beta (oceanbase-ce-4.2.0.0-100000152023080109) 及之后的版本修复了该问题。但是仅调整报错信息以便于排查,修复后检测到函数索引/生成列在入参未改变,但是结果与旧值不同时,报 5500 错误。触发后,若确认是系统变量改变或系统函数行为不确定导致报错,则与老版本的处理一致,删除索引后重建。
规避方式
避免将行为不确定的系统函数用于函数索引或生成列的定义。