在WHERE子句中,早于1970年的TIMESTAMP可以工作,但在插入时会被拒绝——不确定是否安全使用
在GridDB中有一个带时间戳列的集合,用来存储事件记录。我的部分输入数据日期早于1970年。尝试插入其中的一行,返回了一个范围错误。查阅后发现,GridDB的 TIMESTAMP从 UTC的 1970-01-01开始,所以这样的拒绝是完全有道理的。
没想到的是,下面这段在没有任何报错的情况下就跑起来了:
SELECT * FROM eventRecords
WHERE event_date > TIMESTAMP('1945-08-15T00:00:00.000Z')
就这么工作了。返回的行都正常。插入时被拒绝的同一个值作为筛选条件却毫不费力地通过了。就那样坐在那里看了一会儿。
继续再往前一段时间试看看会怎样:
SELECT * FROM eventRecords
WHERE event_date > TIMESTAMP('1850-01-01T00:00:00.000Z')
也同样执行成功。返回集合中的所有内容,这也说得通,因为存储的任何数据都不会早于1970。但TQL甚至解析并接受那个字符串而没有抛出任何错误这点,让我完全难以理解。
随后再测试上界,看看是否也会这样。尝试一个超过9999的日期:
SELECT * FROM eventRecords
WHERE event_date < TIMESTAMP('10000-06-01T00:00:00.000Z')
那个确实抛出了错误。因此1970之前的时间戳可以悄悄工作,而9999之后就会出错。都是同一种违规,但跨越边界时结果完全不同。
回到文档,发现有一句话类似的描述:超过可存储范围的值仍然可以用于搜索条件,但在获取行时可能会出错。这句话起了很大作用,但并没有真正告诉我这是有意的设计,还是只是当前恰好生效的未定义行为。
也在考虑跨边界的数值类型比较。我的列是BYTE类型,取值范围是 -128到 127。如果在WHERE条件中对该列进行比较时不小心传入像200这样的值,TQL会悄悄降位、按原样比较,还是直接报错?我做了几次测试,感觉结果不一致,但又不完全确定原因。
并不是要依赖这种破坏性行为。只是想知道在WHERE条件中使用1970年以前的时间戳是否真的安全,还是我可能在日后悄悄出问题的东西。
解决方案
你没有说明这些失败查询返回了哪些错误信息。
我的猜测是这里发生了两件不同的事。
- 插入早于1970/01/01 00:00 UTC的时间戳会失败,因为数据库将时间戳以无符号整型值存储;例如,表示自“UNIX纪元”起的秒数或毫秒数。
- 使用字面量 "10000-06-01T00:00:00.000Z" 的查询失败,因为该字面量不符合ISO 8601的日期时间格式。 ISO 8601要求年份为4 位十进制数字,即0000至 9999。该字面量格式错误。
这是不是“安全使用”的问题?
依照GridDB的文档,答案是否定的。GridDB的文档明确指出时间戳字符串中的年份必须为4 位数字,并且必须 >= 1970(ref)。如果接受1970年之前的年份,那就是查询解析器的一个bug。GridDB的维护者有理由在后续版本中修复它。因此,不应依赖它。
1 - ISO 8601:2000在(弃用的)“截断表示法”中曾支持两位数年份,但这些格式在ISO 8601:2004中被移除。