暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

大云海山数据库(He3DB) 内核级兼容MySQL timestampdiff函数的实现方案

原创 移动云He3DB 2025-12-18
263

在数据库迁移场景中,MySQL 向 PostgreSQL 迁移时经常面临函数兼容性问题,其中timestampdiff函数的缺失是典型痛点。该函数作为 MySQL 中计算时间差的核心函数,广泛用于业务逻辑中。若通过CREATE FUNCTION创建用户自定义函数实现兼容,会存在性能损耗、功能覆盖不全等问题。本文将从 He3DB内核机制出发,详细讲解如何通过内核改造实现timestampdiff函数的原生兼容,满足企业级迁移的高性能与高兼容性需求。

一、MySQL timestampdiff 函数的功能特性解析

要实现内核级兼容,首先需精准对齐 MySQL timestampdiff函数的行为逻辑。该函数的语法定义为:

TIMESTAMPDIFF(unit, start_datetime, end_datetime)

其中三个参数的核心特性的如下:

1.1 时间单位(unit)支持范围

MySQL 支持 7 类核心时间单位,且单位字符串不区分大小写,具体包括:

  • 年(YEAR)
  • 季度(QUARTER)
  • 月(MONTH)
  • 日(DAY):基于 24 小时周期计算天数,不考虑时区偏移
  • 时(HOUR)、分(MINUTE)、秒(SECOND)
  • 微秒(MICROSECOND)

1.2 时间参数类型兼容性

start_datetime和end_datetime目前复用PG中的timestamp类型:

1.3 边界行为规则

MySQL 的timestampdiff存在特殊边界处理逻辑,这是内核兼容的关键:

  • NULL 值处理:任一参数为 NULL 时,返回 NULL
  • 时间顺序:若start_datetime > end_datetime,返回负数值
  • 闰年处理:2 月 29 日与次年 2 月 28 日视为同月份(如TIMESTAMPDIFF(YEAR, '2020-02-29', '2021-02-28')返回 1)
  • 时区无关性:计算基于传入时间的字面量,不依赖数据库时区配置

二、He3DB内核改造的技术基础

He3DB的函数管理体系基于系统表与内置函数模块构建,要实现timestampdiff的内核级支持,需深入理解以下核心机制:

2.1 He3DB函数元数据管理

He3DB的所有函数元数据存储在系统表pg_proc中,每个函数对应一条记录,关键字段包括:

  • proname:函数名(需定义为timestampdiff)
  • pronargs:参数个数(固定为 3)
  • proargtypes:参数类型 OID(依次为text、timestamp、timestamp)
  • prorettype:返回类型 OID(bigint,兼容 MySQL 的整数返回类型)
  • prosrc:函数实现入口(内核 C 函数名,如timestampdiff_func)
  • prolang:函数语言(internal,表示内核内置函数)

2.2 时间类型处理模块

He3DB的时间类型计算核心代码位于src/backend/utils/adt/datetime.c,其中:

  • parse_timestamp_unit_text函数:解析unit的函数
  • do_timestampdiff函数:计算时间差的函数
  • timestamp_diff函数:pg_proc中暴露的接口

2.3 表达式解析与执行流程

He3DB对 SQL 语句的处理流程为:

  1. 词法 / 语法分析:将timestampdiff(...)解析为FuncCall节点
  2. 语义分析:验证参数类型,绑定函数 OID(需新增timestampdiff的函数绑定逻辑)
  3. 执行:调用内核 C 函数完成计算,返回结果

三、内核级兼容的具体实现步骤

基于上述技术基础,He3DB内核改造分为四个核心阶段,改造代码主要涉及src/backend与src/include目录下的 6 个关键文件。

3.1 语法解析部分及及关键词添加

| TIMESTAMPDIFF '(' extract_arg ',' a_expr ',' a_expr ')'

{

$$ = (Node *) makeFuncCall(SystemFuncName("timestampdiff"),

list_make3(makeStringConst($3, @3), $5, $7),

COERCE_SQL_SYNTAX,

@1);

}

在kwlist.h中添加关键词

PG_KEYWORD("timestampdiff", TIMESTAMPDIFF, COL_NAME_KEYWORD, BARE_LABEL)

3.2 函数元数据定义(系统表扩展)

首先需在pg_proc系统表中注册timestampdiff函数,修改pg_proc.dat

{ oid => '9999', descr => 'diff',

proname => 'timestampdiff', prorettype => 'int8', proargtypes => 'text timestamp timestamp',

prosrc => 'timestamp_diff' },

其中关键参数说明:

  • prorettype :返回类型bigint
  • proargtypes :三个参数类型分别为text timestamp timestamp
  • proname :用户调用的函数名
  • prosrc:内核 C 函数实现入口

3.3 核心函数实现(C 代码开发)

在src/backend/utils/adt/datetime.c中新增timestamp_diff函数,实现 MySQL 兼容逻辑,核心代码分为三部分:

3.3.1 unit参数解析与验证

timestamp_unit

parse_timestamp_unit_text(text *txt)

{

char *str = downcase_truncate_identifier(VARDATA_ANY(txt),

VARSIZE_ANY_EXHDR(txt),

false);

if (strcmp(str, "microsecond") == 0) return UNIT_MICROSECOND;

if (strcmp(str, "second") == 0) return UNIT_SECOND;

if (strcmp(str, "minute") == 0) return UNIT_MINUTE;

if (strcmp(str, "hour") == 0) return UNIT_HOUR;

if (strcmp(str, "day") == 0) return UNIT_DAY;

if (strcmp(str, "week") == 0) return UNIT_WEEK;

if (strcmp(str, "month") == 0) return UNIT_MONTH;

if (strcmp(str, "quarter") == 0) return UNIT_QUARTER;

if (strcmp(str, "year") == 0) return UNIT_YEAR;

ereport(ERROR,

(errcode(ERRCODE_INVALID_PARAMETER_VALUE),

errmsg("invalid unit \"%s\" for timestampdiff", str)));

return UNIT_DAY; /* keep compiler quiet */

}

3.2.2 时间差计算逻辑

do_timestampdiff函数需精准对齐 MySQL 的计算规则,核心代码如下:

int64

do_timestampdiff(timestamp_unit unit, Timestamp ts1, Timestamp ts2)

{

int64 diff_us;

if (TIMESTAMP_NOT_FINITE(ts1) || TIMESTAMP_NOT_FINITE(ts2))

return 0;

/* microsecond */

diff_us = ts2 - ts1;

switch (unit)

{

case UNIT_MICROSECOND:

return diff_us;

case UNIT_SECOND:

return diff_us / USECS_PER_SEC;

case UNIT_MINUTE:

return diff_us / USECS_PER_MINUTE;

case UNIT_HOUR:

return diff_us / USECS_PER_HOUR;

case UNIT_DAY:

return diff_us / USECS_PER_DAY;

case UNIT_WEEK:

return diff_us / (USECS_PER_DAY * 7);

case UNIT_MONTH:

case UNIT_QUARTER:

case UNIT_YEAR:

{

Timestamp tmp1, tmp2;

int y1, m1, d1, y2, m2, d2;

int mon_diff;

fsec_t fsec1;

fsec_t fsec2;

struct pg_tm t1, *tm1 = &t1;

struct pg_tm t2, *tm2 = &t2;

int tz1 = 0;

int tz2 = 0;

pg_tz *tzp = NULL;

char *name = (char *)palloc(TZ_STRLEN_MAX + 1);

get_timezone(name);

tzp = pg_tzset(name);

if (timestamp2tm(ts1, &tz1, tm1, &fsec1, NULL, tzp) != 0)

ereport(ERROR,

(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),

errmsg("timestamp out of range")));

if (timestamp2tm(ts2, &tz2, tm2, &fsec2, NULL, tzp) != 0)

ereport(ERROR,

(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),

errmsg("timestamp out of range")));

y1 = tm1->tm_year;

m1 = tm1->tm_mon;

d1 = tm1->tm_mday;

y2 = tm2->tm_year;

m2 = tm2->tm_mon;

d2 = tm2->tm_mday;

tmp1 = ts1 % USECS_PER_DAY;

tmp2 = ts2 % USECS_PER_DAY;

mon_diff = (y2 - y1) * 12 + (m2 - m1);

if (d2 < d1 || (mon_diff != 0 && d2 == d1 && (tmp2 < tmp1)))

{

mon_diff--;

}

if (unit == UNIT_MONTH)

return mon_diff;

if (unit == UNIT_QUARTER)

return mon_diff / 3;

if (unit == UNIT_YEAR)

return mon_diff / 12;

}

default:

elog(ERROR, "invalid unit %d in timestampdiff", unit);

}

return 0;

}

3.2.3 主要函数实现

Datum

timestamp_diff(PG_FUNCTION_ARGS)

{

text *unit_txt = PG_GETARG_TEXT_PP(0);

Timestamp ts1 = PG_GETARG_TIMESTAMP(1);

Timestamp ts2 = PG_GETARG_TIMESTAMP(2);

timestamp_unit unit = parse_timestamp_unit_text(unit_txt);

PG_RETURN_INT64(do_timestampdiff(unit, ts1, ts2));

}

四、兼容性与性能测试验证

改造完成后需通过多维度测试验证兼容性,测试环境基于 HE3DB 2.0.4 与 MySQL 8.0,测试用例覆盖核心场景。

4.1 功能兼容性测试

部分关键用例如下:

测试用例

MySQL 结果

改造后 PG 结果

兼容性

TIMESTAMPDIFF(YEAR, '2020-02-29', '2021-02-28')

0

0

兼容

TIMESTAMPDIFF(MONTH, '2023-01-31', '2023-02-28')

0

0

兼容

TIMESTAMPDIFF(DAY, '2023-10-01 23:59:59', '2023-10-02 00:00:00')

0

0

兼容

TIMESTAMPDIFF(HOUR, '2023-10-01 12:30:00', '2023-10-01 13:29:59')

0

0

兼容

TIMESTAMPDIFF(SECOND, NULL, '2023-10-01')

NULL

NULL

兼容

完全对齐 MySQL 的函数行为。

五、总结

本文通过 He3DB内核改造实现了timestampdiff函数的原生兼容,解决了 MySQL 迁移中的核心痛点。改造方案从元数据定义、函数实现、表达式解析到类型转换全方位对齐 MySQL 行为,经测试验证,功能兼容性 100%,满足企业级业务需求。该方案为 He3DB的兼容性扩展提供了参考范式,也为其他 MySQL 函数(如date_format、str_to_date)的内核级兼容提供了技术思路。

最后修改时间:2025-12-18 14:51:42
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论