Spec-Zone.ru › SQLite

Применение набора изменений к базе данных

int sqlite3changeset_apply(
  sqlite3 *db,                    /* Apply change to "main" db of this handle */
  int nChangeset,                 /* Size of changeset in bytes */
  void *pChangeset,               /* Changeset blob */
  int(*xFilter)(
    void *pCtx,                   /* Copy of sixth arg to _apply() */
    const char *zTab              /* Table name */
  ),
  int(*xConflict)(
    void *pCtx,                   /* Copy of sixth arg to _apply() */
    int eConflict,                /* DATA, MISSING, CONFLICT, CONSTRAINT */
    sqlite3_changeset_iter *p     /* Handle describing change and conflict */
  ),
  void *pCtx                      /* First argument passed to xConflict */
);
int sqlite3changeset_apply_v2(
  sqlite3 *db,                    /* Apply change to "main" db of this handle */
  int nChangeset,                 /* Size of changeset in bytes */
  void *pChangeset,               /* Changeset blob */
  int(*xFilter)(
    void *pCtx,                   /* Copy of sixth arg to _apply() */
    const char *zTab              /* Table name */
  ),
  int(*xConflict)(
    void *pCtx,                   /* Copy of sixth arg to _apply() */
    int eConflict,                /* DATA, MISSING, CONFLICT, CONSTRAINT */
    sqlite3_changeset_iter *p     /* Handle describing change and conflict */
  ),
  void *pCtx,                     /* First argument passed to xConflict */
  void **ppRebase, int *pnRebase, /* OUT: Rebase data */
  int flags                       /* SESSION_CHANGESETAPPLY_* flags */
);
int sqlite3changeset_apply_v3(
  sqlite3 *db,                    /* Apply change to "main" db of this handle */
  int nChangeset,                 /* Size of changeset in bytes */
  void *pChangeset,               /* Changeset blob */
  int(*xFilter)(
    void *pCtx,                   /* Copy of sixth arg to _apply() */
    sqlite3_changeset_iter *p     /* Handle describing change */
  ),
  int(*xConflict)(
    void *pCtx,                   /* Copy of sixth arg to _apply() */
    int eConflict,                /* DATA, MISSING, CONFLICT, CONSTRAINT */
    sqlite3_changeset_iter *p     /* Handle describing change and conflict */
  ),
  void *pCtx,                     /* First argument passed to xConflict */
  void **ppRebase, int *pnRebase, /* OUT: Rebase data */
  int flags                       /* SESSION_CHANGESETAPPLY_* flags */
);

Применяет набор изменений или набор исправлений к базе данных. Эти функции пытаются обновить базу данных «main», подключённую к дескриптору db, изменениями, содержащимися в наборе изменений, переданном через второй и третий аргументы.

Все изменения, внесённые этими функциями, заключены в транзакцию savepoint. Если возникает какая-либо другая ошибка (кроме нарушения ограничения при попытке записи в целевую базу данных), транзакция savepoint откатывается, восстанавливая исходное состояние целевой базы данных, и возвращается код ошибки SQLite. Кроме того, начиная с версии 3.51.0, в дескрипторе базы данных сохраняются код ошибки и сообщение об ошибке, доступные с помощью API sqlite3_errcode() и sqlite3_errmsg().

Четвёртый аргумент (xFilter), передаваемый этим функциям, — это «функция обратного вызова фильтра». Ему можно передать NULL; в этом случае к базе данных применяются все изменения из набора изменений. Для sqlite3changeset_apply() и sqlite3_changeset_apply_v2(), если аргумент не равен NULL, функция вызывается один раз для каждой таблицы, затронутой хотя бы одним изменением в наборе изменений. В этом случае имя таблицы передаётся как второй аргумент, а копия указателя контекста, переданного как шестой аргумент функции apply() или apply_v2(), — как первый. Если «функция обратного вызова фильтра» возвращает ноль, попытка применить какие-либо изменения к таблице не предпринимается. В противном случае, если возвращаемое значение отлично от нуля, предпринимается попытка применить все изменения, относящиеся к таблице.

Для sqlite3_changeset_apply_v3() функция обратного вызова xFilter вызывается для каждого изменения. В этом случае вторым аргументом является sqlite3_changeset_iter, который можно опрашивать с помощью обычных API, чтобы получить сведения о текущем изменении. Если в этом случае «функция обратного вызова фильтра» возвращает ноль, попытка применить текущее изменение не предпринимается. Если она возвращает значение, отличное от нуля, изменение применяется.

Для каждой таблицы, не исключённой функцией обратного вызова фильтра, эта функция проверяет, содержится ли в целевой базе данных совместимая таблица. Таблица считается совместимой, если выполняются все следующие условия:

  • Имя таблицы совпадает с именем, записанным в наборе изменений;
  • В таблице не меньше столбцов, чем указано в наборе изменений;
  • Столбцы первичного ключа таблицы расположены в тех же позициях, что и в наборе изменений.

Если совместимой таблицы нет, это не считается ошибкой, однако связанные с этой таблицей изменения не применяются. С помощью механизма sqlite3_log() выдаётся предупреждение с кодом ошибки SQLITE_SCHEMA. Для каждой таблицы в наборе изменений выдаётся не более одного такого предупреждения.

Для каждого изменения, для которого найдена совместимая таблица, предпринимается попытка изменить содержимое таблицы в соответствии с каждым изменением UPDATE, INSERT или DELETE, не исключённым функцией обратного вызова фильтра. Если изменение не удаётся применить без ошибок, может быть вызвана функция-обработчик конфликтов, переданная в качестве пятого аргумента sqlite3changeset_apply(). Ниже точно описано, в каких случаях для каждого типа изменений вызывается обработчик конфликтов.

В отличие от аргумента xFilter, аргументу xConflict нельзя передавать NULL. Результаты передачи в качестве аргумента xConflict любого значения, кроме указателя на допустимую функцию, не определены.

При каждом вызове функции-обработчика конфликтов она должна возвращать одно из значений SQLITE_CHANGESET_OMIT, SQLITE_CHANGESET_ABORT или SQLITE_CHANGESET_REPLACE. Значение SQLITE_CHANGESET_REPLACE можно возвращать только в том случае, если второй аргумент, переданный обработчику конфликтов, равен SQLITE_CHANGESET_DATA или SQLITE_CHANGESET_CONFLICT. Если обработчик конфликтов возвращает недопустимое значение, все уже внесённые изменения откатываются, а вызов sqlite3changeset_apply() возвращает SQLITE_MISUSE. В зависимости от значения, возвращённого при каждом вызове функции-обработчика конфликтов, sqlite3changeset_apply() выполняет разные действия. Подробности см. в документации по трём допустимым возвращаемым значениям.

Изменения DELETE
Для каждого изменения DELETE функция проверяет, содержит ли целевая база данных строку с теми же значениями первичного ключа, что и исходные значения строки, сохранённые в наборе изменений. Если такая строка есть и значения во всех столбцах, не входящих в первичный ключ, также совпадают со значениями из набора изменений, строка удаляется из целевой базы данных.

Если найдена строка с совпадающими значениями первичного ключа, но одно или несколько полей, не входящих в первичный ключ, содержат значения, отличающиеся от исходных значений строки, сохранённых в наборе изменений, вызывается функция-обработчик конфликтов, которой в качестве второго аргумента передаётся SQLITE_CHANGESET_DATA. Если в таблице базы данных больше столбцов, чем записано в наборе изменений, с текущим содержимым базы данных сравниваются только значения полей, не входящих в первичный ключ; все последующие столбцы таблицы базы данных игнорируются.

Если в базе данных не найдена строка с совпадающими значениями первичного ключа, вызывается функция-обработчик конфликтов, которой в качестве второго аргумента передаётся SQLITE_CHANGESET_NOTFOUND.

Если предпринимается попытка выполнить операцию DELETE, но SQLite возвращает SQLITE_CONSTRAINT (это может произойти только при нарушении ограничения внешнего ключа), вызывается функция-обработчик конфликтов, которой в качестве второго аргумента передаётся SQLITE_CHANGESET_CONSTRAINT. Это относится и к случаю, когда операция DELETE выполняется после того, как предыдущий вызов функции-обработчика конфликтов вернул SQLITE_CHANGESET_REPLACE.

Изменения INSERT
Для каждого изменения INSERT предпринимается попытка вставить новую строку в базу данных. Если строка набора изменений содержит меньше полей, чем таблица базы данных, последующие поля заполняются значениями по умолчанию.

Если попытка вставить строку завершается неудачей, поскольку в базе данных уже есть строка с такими же значениями первичного ключа, вызывается функция-обработчик конфликтов, которой в качестве второго аргумента передаётся SQLITE_CHANGESET_CONFLICT.

Если попытка вставить строку завершается неудачей из-за другого нарушения ограничения (например, NOT NULL или UNIQUE), вызывается функция-обработчик конфликтов, которой в качестве второго аргумента передаётся SQLITE_CHANGESET_CONSTRAINT. Это относится и к случаю, когда операция INSERT выполняется повторно после того, как предыдущий вызов функции-обработчика конфликтов вернул SQLITE_CHANGESET_REPLACE.

Изменения UPDATE
Для каждого изменения UPDATE функция проверяет, содержит ли целевая база данных строку с теми же значениями первичного ключа, что и исходные значения строки, сохранённые в наборе изменений. Если такая строка есть и значения во всех изменённых столбцах, не входящих в первичный ключ, также совпадают со значениями из набора изменений, строка обновляется в целевой базе данных.

Если найдена строка с совпадающими значениями первичного ключа, но одно или несколько изменённых полей, не входящих в первичный ключ, содержат значения, отличающиеся от исходных значений строки, сохранённых в наборе изменений, вызывается функция-обработчик конфликтов, которой в качестве второго аргумента передаётся SQLITE_CHANGESET_DATA. Поскольку изменения UPDATE содержат значения только для изменяемых полей, не входящих в первичный ключ, для предотвращения вызова обработчика конфликтов SQLITE_CHANGESET_DATA должны совпадать с исходными значениями только эти поля.

Если в базе данных не найдена строка с совпадающими значениями первичного ключа, вызывается функция-обработчик конфликтов, которой в качестве второго аргумента передаётся SQLITE_CHANGESET_NOTFOUND.

Если предпринимается попытка выполнить операцию UPDATE, но SQLite возвращает SQLITE_CONSTRAINT, вызывается функция-обработчик конфликтов, которой в качестве второго аргумента передаётся SQLITE_CHANGESET_CONSTRAINT. Это относится и к случаю, когда операция UPDATE выполняется после того, как предыдущий вызов функции-обработчика конфликтов вернул SQLITE_CHANGESET_REPLACE.

Внутри функции обратного вызова xConflict безопасно выполнять SQL-инструкции, в том числе записывающие данные в таблицу, к которой относится обратный вызов. Это позволяет дополнительно настраивать стратегию разрешения конфликтов в приложении.

Если выходные параметры (ppRebase) и (pnRebase) не равны NULL, а входные данные представляют собой набор изменений (а не набор исправлений), то перед возвратом sqlite3changeset_apply_v2() может установить (*ppRebase) в указатель на «ребазу», которую можно использовать с буфером API sqlite3_rebaser. В этом случае (*pnRebase) устанавливается в размер буфера в байтах. Вызывающая сторона должна в конечном итоге освободить такой буфер с помощью sqlite3_free(). Буфер выделяется и заполняется только в том случае, если при применении набора исправлений возник один или несколько конфликтов. Дополнительные сведения см. в комментариях к API sqlite3_rebaser.

Поведение sqlite3changeset_apply_v2() и соответствующего потокового API можно изменить, передав комбинацию поддерживаемых флагов в качестве 9-го параметра.

Обратите внимание, что API sqlite3changeset_apply_v2() всё ещё является экспериментальным и поэтому может измениться.

См. также списки объектов, констант и функций.

SQLite is in the Public Domain.
https://sqlite.org/session/sqlite3changeset_apply.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API