Android Lollipop - измененное поведение SQLite
При тестировании одного из моих приложений для совместимости с Android 5.0 я обнаружил, что one два моих SQL-запроса не работают больше не работают на Lollipop. Обе мои проблемы привели к значительно большим результатам на Lollipop по сравнению со старыми версиями Android.
Ниже я расскажу о тех проблемах и их решениях более глубоко, если у вас возникнут аналогичные проблемы.
Мой главный вопрос довольно прост: не документированы ли какие-либо несоответствующие изменения?
Проблема номер один: MATCH
Кажется, что следующий запрос больше не работает на Lollipop:
SELECT title FROM ents JOIN ctt ON ctt.docid = ents.cttId WHERE (ctt MATCH '*ads*');
Он больше не возвращает никаких результатов, на pre-Lollipop (с той же базой данных и теми же данными, конечно).
Как описано в этом вопросе, например, MATCH соответствует только строковым префиксам. Это действительно так, "*" перед поисковым термином просто игнорировался на Android < 5.0.
Lollipop SQLite, однако, не нравится первый '*' и ничего не возвращает для этого запроса. Мне пришлось изменить запрос на следующий, чтобы он снова работал:
SELECT title FROM ents JOIN ctt ON ctt.docid = ents.cttId WHERE (ctt MATCH 'ads*');
(Я использую FTS3 для полнотекстового поиска.)
Проблема номер два: COLLATE LOCALIZED
Краткая история: GROUPING В столбце с псевдонимом, на который ссылается исходное имя в сочетании с 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
Запрос выше работает на 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
(btw, этот ответ, вы можете найти отличный обзор используемых версий SQLite в Android)
Теперь идет интересная часть: Если конкретный Android "COLLATE LOCALIZED" удаляется в предложении ORDER BY, все начинает работать, даже с "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
Мой опыт Lollipop основан на тестах в эмуляторе SDK с использованием образа системы ARM Android 5.0 - API уровня 21.
Эта проблема secon выглядит как ошибка SQLite, специфичная для Android. Или может кто-нибудь объяснить мне это (на мой взгляд) странное поведение? Или, опять же, это даже где-то документировано?: -)
Спасибо заранее!
Ответы
Ответ 1
Я не эксперт по SQLite, и я предполагаю, что вы решили, что этот вопрос будет в значительной степени риторическим, но позвольте мне предложить некоторые мысли.
MATCH
Как вы уже указали, MATCH
учитывает только префиксные термины. Неудивительно, что неожиданное и непредсказуемое поведение возникло бы из-за префикса префикса (если хотите) со звездочкой.
COLLATE LOCALIZED с псевдонимом
Это кажется интересной ошибкой. Вы можете попробовать и использовать EXPLAIN QUERY PLAN
, чтобы попробовать и диагностировать его.
Документация
Очевидно, я не сказал вам ничего, чего вы еще не знаете. Однако ваш "вопрос" касался документации. Во-первых, примечания о выпуске SQLite можно найти здесь. Обычно они довольно подробно описывают изменения между релизами.
Ваша первая проблема - это действительно ошибка программирования. Документация уже существует о том, как использовать префиксы FTS. Вы не получите объяснение, почему ваш синтаксис перестает работать. Вероятно, с ним никогда не следовало начинать.
Проблема LOCALIZED
, вероятно, является ошибкой, поэтому ее отсутствие "документации" (я рекомендую вам сообщить об этом Google, хотя). Также помните, что SQLite является частью ядра Android и имеет не только настройки (например, LOCALIZED
), но и собственные привязки Java. Как основная реализация ядра SQLite, так и привязки потенциально изменяются с каждой версией. Это подводит меня к моей основной точке:
Рассмотрите возможность развертывания приложения с помощью частной реализации SQLite. Инструкции для этого можно найти здесь. Это позволит вам контролировать версию SQLite, используемую вашим приложением, и дать вам прекрасный контроль над тем, как и когда его обновлять. Однако это связано с ценой, поскольку вы теряете ключевое слово LOCALIZED
, например, и привязки поддерживают API API 15 или выше. Я полагаю.
Ответ 2
Наблюдаемые изменения связаны с тем, что Lollipop поставляется с SQLite 3.8 (Android 4.x поставляется с 3.7.11). Вот список изменений http://www.sqlite.org/releaselog/3_8_0.html
Например, ошибка без такого столбца для "GROUP BY inner.title" обусловлена тем, что "Идентификаторы в предложениях GROUP BY всегда предпочитают имена столбцов вывода".
Ответ 3
Я вижу пару проблем с OP.
-
FTS использует проценты не звездочки в качестве дикой карты.
-
FTS ищет границы слов, поэтому, имея ведущую дикую карту, никогда не работал с FTS, она передала бы ее sqlite, чтобы прочитать всю таблицу ужасно медленно, но OP заставляет нас полагать, что она работала в мимо. Следовательно, этот запрос в прошлом бы работал медленнее, если он вообще выполнялся.
-
Запросы союзников всегда чрезмерны в количестве ресурсов, которые они используют, особенно в сочетании с некоторой группой. Следовательно, этот запрос не имеет места в масштабируемом бизнес-приложении.