Копирсаючись з базами даних в андроїді натрапив на вельми неприємну річ: методи SQLiteDatabase для вставлення даних працюють не зовсім так, як написано в документації. Завдання було просте: вставити запис і отримати його ключ для використання в іншій таблиці. У разі, якщо потрібний запис вже є, хочеться дізнатися ключ цього старого запису. Виявилося, що отримати можна що завгодно, крім цього самого старого ключа.
Щоб розібратися в цьому довелося поставити кілька дослідів на кішках, зате тепер дещо прояснилося.
Спочатку подивимося, що ж нам обіцяє андроїд в SQLiteDatabase:
insert () - найпростіший метод. Повинен повернути тільки що вставлений ключ або -1, якщо щось пішло не так.
insertOrThrow () - опис точно відповідає попередньому, але додано, що метод кидає SQLException. Судячи з назви, можна очікувати, що SQLException кинеться замість того, щоб повертати -1, але поки впевненості немає.
insertWithOnConflict () - ось це найцікавіше. Нам обіцяють, що метод поверне ключ свіжого запису АБО ключ існуючого запису, якщо для конфліктів використовувався алгоритм IGNORE, АБО -1. Запам'ятаємо також, що про SQLException тут нічого не говориться.
Тепер подивимося, що ж відбувається на практиці. Створимо найпростішу таблицю і спробуємо задавати для неї різні алгоритми вирішення конфліктів (в тому числі і ніякої). Я не став перевіряти rollback і abort - їх поведінка в даному випадку, судячи з усього, буде аналогічно fail, а мене найбільше цікавив ignore.
Вставляємо декілька унікальних значень у таблицю, а потім спробуємо додати конфліктуюче. Подивимося, як всі ці методи обробляють конфлікти.
У разі, якщо алгоритм вирішення конфліктів не заданий, простий insert () повертає -1 (як і обіцялося), а insertOrThrow () і insertWithOnConflict () обидва кидають SQLiteConstraintException. І якщо для insertOrThrow () це правильно, то від insertWithOnConflict () можна було очікувати -1. Те ж саме відбувається і при алгоритмі'fail'.
Алгоритм'replace'всі методи відпрацювали нормально - видалили старий запис, вставили новий і повернули його ключ.
А ось з таким потрібним'ignore'вийшов повний облом. Жоден метод не видав помилку (-1 або SQLiteConstraintException). Всі дружно повернули якесь число - імовірно ключ останнього вставленого в таблицю запису. Зрозуміло, це зовсім не те, що нам обіцяли.
Загалом, документації вірити не можна. Будь-який метод може як повернути найбільш несподівані значення, так і кинути виняток. А ось бажаного результату досягти так і не вдалося - жоден метод не зміг при конфлікті повернути ключ існуючого запису.
