![]() |
|
|||
WebMoney: WMZ Z294115950220 WMR R409981405661 WME E134003968233 |
Visa 4274 3200 2453 6495 |
Расширение сессии обеспечивает механизм для записи изменений
некоторых или всех определенных таблиц
в базе данных SQLite, и упаковывающий те изменения в файл
"changeset" или "patchset", который может позже
использоваться, чтобы применить тот же самый набор изменений к другой базе
данных с той же самой схемой и совместимыми стартовыми данными.
"changeset" может также инвертироваться и использоваться,
чтобы "отменить" сессию.
Этот документ это введение в расширение сессии.
Детали интерфейса находятся в отдельном документе
здесь. Предположим, что SQLite используется в качестве
формата файла приложения для конкретного
применения. Два пользователя, Элис и Боб, каждый работают с массивом
данных, который приблизительно гигабайт в размере. Они весь день работают
параллельно, каждый делает их собственные настройки.
В конце дня они хотели бы слить свои изменения вместе в
единственный объединенный объект. Расширение сессии облегчает это, делая запись всех изменений и баз данных
Элис и Боба и соединяя те изменения в changeset или patchset файлы.
В конце дня Элис может послать свой changeset Бобу, и Боб может
"применить" его к своей базе данных. Результат (допустим, нет
никаких конфликтов) состоит в том, что база данных Боба тогда содержит его
изменения и изменения Элис. Аналогично, Боб может послать changeset своей
работы Элис, и она может применить его изменения к своей базе данных. Другими словами, расширение сессии предоставляет средство для файлов базы
данных SQLite, которое подобно утилите unix
patch
или возможностям "merge" систем управления версиями, таким как
Fossil,
Git или
Mercurial. С version 3.13.0 (2016-05-18)
расширение сессии было включено в исходное SQLite
объединение.
По умолчанию расширение сессии отключено. Чтобы позволить его, соберите со
следующими параметрами компилятора: Или, используя систему сборки autoconf, передайте скрипту
configure опцию --enable-session. Нет никакой поддержки
виртуальных таблиц.
Их изменения не захватываются. Расширение сессии работает только с таблицами, у которых есть
заявленный PRIMARY KEY. PRIMARY KEY может быть INTEGER PRIMARY KEY
(псевдоним rowid) или внешний PRIMARY KEY. SQLite позволяет хранить NULL в столбцах
PRIMARY KEY. Однако, расширение сессии игнорирует все такие строки.
Никакие изменения, затрагивающие строки с одним или более NULL в столбцах
PRIMARY KEY, не зарегистрированы модулем сессий. Модуль сессий вращается вокруг создания и управления changeset.
changeset это blob данных, которые кодируют серию изменений базы данных.
Каждое изменение в changeset это одно из следующего: Изменение UPDATE не содержит информации относительно областей не-PRIMARY
KEY, которые не изменяются этим изменением. Для изменения UPDATE невозможно
определить модификации к областям PRIMARY KEY. Единственный changeset может содержать изменения, которые относятся
больше, чем к одной таблице базы данных. Для каждой таблицы, для которой
changeset включает по крайней мере одно изменение, это также
кодирует следующие данные: Changeset может быть применен только к базам данных, которые содержат
таблицы, соответствующие вышеупомянутым трем критериям,
как сохранено в changeset. patchset подобен changeset. Это немного более компактно, чем changeset, но
обеспечивает более ограниченное обнаружение конфликта и параметры разрешения
(см. следующую секцию для деталей).
Различия между patchset и changeset в том, что: Для изменения UPDATE полезный груз состоит только из областей
PRIMARY KEY и новых значений измененных областей. Исходные значения
измененных областей не сохранены как часть patchset. Когда changeset или patchset применяются к базе данных, предпринята
попытка, чтобы вставить новую стрку для каждого изменения INSERT, удалить
строку для каждого изменения DELETE и изменить строку для каждого изменения
UPDATE. Если целевая база данных находится в том же самом состоянии,
как оригинальная база данных, на которой был зарегистрирован changeset,
это простой вопрос. Однако, если содержание целевой базы данных не
находится в точно этом состоянии, конфликты могут произойти, применяя
changeset или patchset. Обрабатывая изменение INSERT, следующие
конфликты могут произойти: Обрабатывая изменение DELETE, следующие конфликты
могут быть обнаружены: Обрабатывая изменение UPDATE, следующие конфликты
могут быть обнаружены: В зависимости от типа конфликта у применения сессий есть множество
конфигурируемых возможностей для контакта с конфликтами, в пределах от
исключения противоречивого изменения, прерывания применения всего changeset
или применения изменения несмотря на конфликт. Для получения дополнительной
информации см. документацию для
sqlite3changeset_apply() API. После того, как объект сессии формировался, он начинает контролировать
изменения его таблиц. Однако, это не делает запись всего изменения каждый
раз, когда строка в базе данных изменяется. Вместо этого это делает запись
просто областей PRIMARY KEY для каждой вставленной строки
и просто PRIMARY KEY и всех первоначальных значений строки
для любых обновленных или удаленных строк. Если строка изменяется несколько
раз единственной сессией, никакая новая информация не зарегистрирована. Другая информация, запрошенная, чтобы создать changeset или patchset,
прочитана из файла базы данных, когда вызвана
sqlite3session_changeset() или
sqlite3session_patchset(). Одно значение вышеупомянутого: если изменение внесено и затем разрушено в
единственной сессии (например, если строка вставлена и затем удалена снова),
модуль сессий не сообщает ни о каком изменении вообще. Или если строка
обновляется многократно в той же самой сессии, все обновления соединены в
единственное обновление в любом changeset или patchset blob. Эта секция обеспечивает примеры, которые демонстрируют, как
использовать расширение сессий. Пример кода ниже демонстрирует шаги, вовлеченные в захват changeset,
выполняя команды SQL. Таким образом: Единственный объект сессии наблюдает изменения, сделанные к единой
базе данных (то есть, "main", "temp" или приложенная база данных) через
единственный обработчик базы данных sqlite3*. По умолчанию объект сессии не наблюдает изменения ни на какой таблице
базы данных. Прежде чем он это сделает,
это должно формироваться. Есть три способа формировать набор таблиц,
чтобы наблюдать изменения: Пример кода ниже использует второй из методов, перечисленных выше:
он контролирует изменения на всех таблицах базы данных. Не надо удалять объект сессии после извлечения changeset или patchset из
него. Это можно оставить приложенным к дескриптору
базы данных и продолжить контролировать изменения в таблицах
как прежде. Однако, если
sqlite3session_changeset()
или
sqlite3session_patchset() вызовут во второй раз на объекте сессии,
changeset или patchset будут содержать все изменения, которые
произошли на связи после того, как сессия была создана.
Другими словами, объект сессии не перезагружен или обнулен
sqlite3session_changeset() или sqlite3session_patchset(). Применение Changeset к базе данных более просто, чем захват changeset.
Обычно единственное обращение к
sqlite3changeset_apply(),
как изображено в примере кода ниже, достаточно. В случаях, где это сложно, осложнения в применении changeset лежат в
разрешении конфликтов. Обратитесь к документации API выше для деталей. Пример кода ниже демонстрирует методы, которые проходят набор
и извлекают данные, связанные со всеми изменениями в changeset. Если iterator указывает на действительный вход DELETE или UPDATE,
sqlite3changeset_old() API
может использоваться, чтобы получить значения old.* в
полезном грузе изменения. Большинство запросов будут использовать только функциональность модуля
сессии, описанную в предыдущей секции. Однако, следующая дополнительная
функциональность доступна для использования и манипуляции
changeset и patchset blob:
Choose any three.
1. Введение
1.1. Типичный вариант использования
1.2. Получение расширения сессии
-DSQLITE_ENABLE_SESSION -DSQLITE_ENABLE_PREUPDATE_HOOK
1.3. Ограничения
2. Понятия
2.1. Changesets и Patchsets
2.2. Конфликты
2.3. Создание Changeset
3. Используя расширение сессии
3.1. Захват Changeset
/*
** Argument zSql points to a buffer containing an SQL script to execute
** against the database handle passed as the first argument. As well as
** executing the SQL script, this function collects a changeset recording
** all changes made to the "main" database file. Assuming no error occurs,
** output variables (*ppChangeset) and (*pnChangeset) are set to point
** to a buffer containing the changeset and the size of the changeset in
** bytes before returning SQLITE_OK. In this case it is the responsibility
** of the caller to eventually free the changeset blob by passing it to
** the sqlite3_free function.
**
** Or, if an error does occur, return an SQLite error code. The final
** value of (*pChangeset) and (*pnChangeset) are undefined in this case.
*/
int sql_exec_changeset
(
sqlite3 *db, /* Database handle */
const char *zSql, /* SQL script to execute */
int *pnChangeset, /* OUT: Size of changeset blob in bytes */
void **ppChangeset /* OUT: Pointer to changeset blob */
)
{
sqlite3_session *pSession = 0;
int rc;
/* Create a new session object */
rc = sqlite3session_create(db, "main", &pSession);
/* Configure the session object to record changes to all tables */
if( rc==SQLITE_OK ) rc = sqlite3session_attach(pSession, NULL);
/* Execute the SQL script */
if( rc==SQLITE_OK ) rc = sqlite3_exec(db, zSql, 0, 0, 0);
/* Collect the changeset */
if( rc==SQLITE_OK ){
rc = sqlite3session_changeset(pSession, pnChangeset, ppChangeset);
}
/* Delete the session object */
sqlite3session_delete(pSession);
return rc;
}
3.2. Применение Changeset к базе данных
/*
** Conflict handler callback used by apply_changeset(). See below.
*/
static int xConflict(void *pCtx, int eConflict, sqlite3_changset_iter *pIter)
{
int ret = (int)pCtx;
return ret;
}
/*
** Apply the changeset contained in blob pChangeset, size nChangeset bytes,
** to the main database of the database handle passed as the first argument.
** Return SQLITE_OK if successful, or an SQLite error code if an error
** occurs.
**
** If parameter bIgnoreConflicts is true, then any conflicting changes
** within the changeset are simply ignored. Or, if bIgnoreConflicts is
** false, then this call fails with an SQLTIE_ABORT error if a changeset
** conflict is encountered.
*/
int apply_changeset(
sqlite3 *db, /* Database handle */
int bIgnoreConflicts, /* True to ignore conflicting changes */
int nChangeset, /* Size of changeset in bytes */
void *pChangeset /* Pointer to changeset blob */
)
{
return sqlite3changeset_apply(db, nChangeset, pChangeset, 0, xConflict,
(void*)bIgnoreConflicts);
}
3.3. Просмотр содержания Changeset
/*
** Print the contents of the changeset to stdout.
*/
static int print_changeset(void *pChangeset, int nChangeset)
{
int rc;
sqlite3_changeset_iter *pIter = 0;
/* Create an iterator to iterate through the changeset */
rc = sqlite3changeset_start(&pIter, nChangeset, pChangeset);
if( rc!=SQLITE_OK ) return rc;
/* This loop runs once for each change in the changeset */
while( SQLITE_ROW==sqlite3changeset_next(pIter))
{
const char *zTab; /* Table change applies to */
int nCol; /* Number of columns in table zTab */
int op; /* SQLITE_INSERT, UPDATE or DELETE */
sqlite3_value *pVal;
/* Print the type of operation and the table it is on */
rc = sqlite3changeset_op(pIter, &zTab, &nCol, &op, 0);
if( rc!=SQLITE_OK ) goto exit_print_changeset;
printf("%s on table %s\n",
op==SQLITE_INSERT?"INSERT" : op==SQLITE_UPDATE?"UPDATE" : "DELETE",
zTab);
/* If this is an UPDATE or DELETE, print the old.* values */
if( op==SQLITE_UPDATE || op==SQLITE_DELETE )
{
printf("Old values:");
for(i=0; i<nCol; i++)
{
rc = sqlite3changeset_old(pIter, i, &pVal);
if( rc!=SQLITE_OK ) goto exit_print_changeset;
printf(" %s", pVal ? sqlite3_value_text(pVal) : "-");
}
printf("\n");
}
/* If this is an UPDATE or INSERT, print the new.* values */
if( op==SQLITE_UPDATE || op==SQLITE_INSERT )
{
printf("New values:");
for(i=0; i<nCol; i++)
{
rc = sqlite3changeset_new(pIter, i, &pVal);
if( rc!=SQLITE_OK ) goto exit_print_changeset;
printf(" %s", pVal ? sqlite3_value_text(pVal) : "-");
}
printf("\n");
}
}
/* Clean up the changeset and return an error code (or SQLITE_OK) */
exit_print_changeset:
rc2 = sqlite3changeset_finalize(pIter);
if( rc==SQLITE_OK ) rc = rc2;
return rc;
}
4.
Расширенная функциональность