Android Lollipop - 改变了SQLite的行为

Mic*_*ier 22 sqlite android backwards-compatibility android-5.0-lollipop

在测试我的Android 5.0兼容应用程序时,我发现了这一点 我的两个SQL查询 在棒棒糖上不再按预期工作了.与旧的Android版本相比,我的两个问题导致Lollipop的结果显着不同.

下面,我将更深入地描述这些问题及其解决方案,以防您遇到类似问题.

我的主要问题很简单:这些非向后兼容的变更是否记录在案?

问题一:MATCH

似乎以下查询在Lollipop上不再起作用:


SELECT title FROM ents JOIN ctt ON ctt.docid = ents.cttId WHERE (ctt MATCH '*ads*');
Run Code Online (Sandbox Code Playgroud)

它不再返回任何结果,在它之前的Lollipop上(当然使用相同的数据库和相同的数据).

例如,如本问题所述,MATCH仅匹配字符串前缀.事实确实如此,搜索词前面的'*'在Android <5.0时被忽略了.

然而,Lollipop的SQLite不喜欢第一个'*',并且不会为此查询返回任何内容.我不得不将查询更改为以下内容以使其再次起作用:


SELECT title FROM ents JOIN ctt ON ctt.docid = ents.cttId WHERE (ctt MATCH 'ads*');
Run Code Online (Sandbox Code Playgroud)

(我使用FTS3进行全文搜索.)

问题二:COLLATE LOCALIZED

简短的故事: GROUPing BY由原始名称引用的别名列与ORDER BY一起使用特定于Android的"COLLATE LOCALIZED"会在Lollipop上引发错误,但适用于以前的版本.WTF!?:-)

很长的故事:

故事开始于一个非常大的自动生成的查询,因此我修改,简化并缩短了导致问题的部分.我知道查询没有多大意义,如下所示,但它证明了问题.


SELECT 
    inner.title AS title, 
    ltrim(inner.title, '*') AS title2
FROM 
    (SELECT types.text AS title FROM types) AS inner
GROUP BY inner.title 

UNION SELECT 
    inner.title AS title, 
    ltrim(inner.title, '*') AS title2
FROM 
    (SELECT types.text AS title FROM types) AS inner
GROUP BY inner.title 

ORDER BY title2 COLLATE LOCALIZED ASC
Run Code Online (Sandbox Code Playgroud)

上面的查询适用于Andriod <5.0,但在Lollipop中导致错误:

Error: no such column: inner.title

好吧,我把"inner.title"别名为"title",所以我尝试将"GROUP BY inner.title"更改为"GROUP BY title",这实际上是Lollipop的SQLite的解决方案:


SELECT 
    inner.title AS title, 
    ltrim(inner.title, '*') AS title2
FROM 
    (SELECT types.text AS title FROM types) AS inner
GROUP BY title 

UNION SELECT 
    inner.title AS title, 
    ltrim(inner.title, '*') AS title2
FROM 
    (SELECT types.text AS title FROM types) AS inner
GROUP BY title 

ORDER BY title2 COLLATE LOCALIZED ASC
Run Code Online (Sandbox Code Playgroud)

(顺便说一下,在这个答案中,您可以找到Android中使用过的SQLite版本的精彩概述)

现在出现了一个有趣的部分:如果在ORDER BY子句中删除了特定于Android的"COLLATE LOCALIZED",一切都会开始工作,即使是"GROUP BY inner.title":


SELECT 
    inner.title AS title, 
    ltrim(inner.title, '*') AS title2
FROM 
    (SELECT indsntyps.text AS title FROM indsntyps) AS inner
GROUP BY inner.title 

UNION SELECT 
    inner.title AS title, 
    ltrim(inner.title, '*') AS title2
FROM 
    (SELECT indsntyps.text AS title FROM indsntyps) AS inner
GROUP BY inner.title 

ORDER BY title2 ASC
Run Code Online (Sandbox Code Playgroud)

我的棒棒糖体验基于使用Android 5.0 - API Level 21 ARM系统映像的SDK模拟器中的测试.

这个secon问题对我来说似乎是特定于Android的SQLite错误.或者有人可以解释我(在我看来)奇怪的行为?或者,这甚至在某处记录了吗?:-)

提前致谢!

Jef*_*xon 8

我不是一个SQLite专家,我认为你打算这个问题主要是修辞,但请允许我提出一些想法.

比赛

正如您已经指出的那样,MATCH 只考虑前缀术语.将前缀(如果愿意)加上星号前缀会产生意外和不可预测的行为,这并不奇怪.

用别名收集LOCALIZED

这似乎是一个有趣的错误.但是,您可以尝试使用它EXPLAIN QUERY PLAN来尝试和诊断它.

文档

显然,我没有告诉你任何你不知道的事情.然而,你的"问题"是关于文档的.对于初学者,可以在此处找到SQLite发行说明.它们通常相当详细地说明版本之间的变化.

你的第一个问题是编程错误.关于如何使用FTS前缀的文档已经存在.您不会得到关于为什么您的特定语法停止工作的解释.可以说,它从来都不应该开始.

这个LOCALIZED问题很可能是一个错误因此缺乏"文档"(我鼓励你向Google报告).还要记住,SQLite是Android核心的一部分,它不仅具有自定义(如LOCALIZED),还具有本机Java绑定.基础SQLite核心实现和绑定都可能随每个版本而变化.这让我想到了我的主要观点:

考虑使用私有SQLite实现部署您的应用程序.这样做的指令可以在这里找到.这将使您能够控制应用程序使用的SQLite版本,并为您提供如何以及何时升级它的精确控制.然而,这确实是有代价的,因为你丢失LOCALIZED关键字,例如,绑定只支持API 15或更高,我相信.