Spec-Zone.ru › SQLite

Расширение RBU

Содержание
1. Расширение RBU
2. Обновления RBU
2.1. Ограничения обновлений RBU
2.2. Подготовка файла обновления RBU
2.2.1. Схема базы данных RBU
2.2.2. Содержимое базы данных RBU
2.2.3. Использование RBU с таблицами FTS3/4
2.2.4. Автоматическое создание обновлений RBU с помощью sqldiff
2.3. Программирование обновлений RBU на C/C++
3. RBU Вакуум
3.1. Ограничения RBU Вакуума
3.2. Программирование RBU Вакуума на C/C++

1. Расширение RBU

Расширение RBU — это дополнение для SQLite, предназначенное для работы с большими файлами баз данных SQLite на устройствах с низким энергопотреблением на периферии сети. RBU может использоваться для двух отдельных задач:

  • Операции обновления RBU. Обновление RBU — это пакетное обновление файла базы данных, которое может включать множество операций вставки, обновления и удаления в одной или нескольких таблицах.
  • Операции вакуума RBU. RBU Вакуум оптимизирует и перестраивает весь файл базы данных, с результатами, аналогичными команде VACUUM родного SQLite.

Аббревиатура RBU расшифровывается как «Resumable Bulk Update».

Обе функции RBU могут быть выполнены с помощью встроенных команд SQL SQLite — обновление RBU через серию команд INSERT, DELETE и UPDATE в рамках одной транзакции, и вакуум RBU одной командой VACUUM. Модуль RBU предоставляет следующие преимущества по сравнению с этими более простыми подходами:

  1. RBU может быть более эффективным

    Наиболее эффективный способ внесения изменений в B-дерево (структура данных, которую SQLite использует для хранения каждой таблицы и индекса на диске) — внесение изменений в порядке ключей. Но если таблица SQL имеет один или несколько индексов, порядок ключей для каждого индекса может отличаться от основной таблицы и других вспомогательных индексов. В результате при выполнении серии операторов INSERT, UPDATE и DELETE обычно невозможно упорядочить операции так, чтобы все B-деревья обновлялись в порядке ключей. Процесс обновления RBU обходит эту проблему, применяя все изменения к основной таблице за один проход, затем применяя изменения к каждому индексу в отдельных проходах, гарантируя, что каждое B-дерево обновляется оптимальным образом. Для большого файла базы данных (который не помещается в кэш диска ОС) эта процедура может привести к ускорению обновления в два раза.

    Операция RBU Вакуума требует меньше временного дискового пространства и записывает меньше данных на диск, чем SQLite VACUUM. SQLite VACUUM требует примерно в два раза больше места на временном диске, чем итоговый размер файла базы данных. Общий объем записываемых данных составляет примерно в три раза больше, чем размер итогового файла базы данных. В отличие от этого, RBU Вакуум требует примерно столько же временного дискового пространства, сколько и итоговый файл базы данных, и записывает в два раза больше данных на диск.

    С другой стороны, RBU Вакуум использует больше ЦП, чем обычный SQLite VACUUM — в одном тесте до пяти раз больше. По этой причине RBU Вакуум часто значительно медленнее, чем SQLite VACUUM в одинаковых условиях.

  2. RBU работает в фоновом режиме

    Активная операция RBU (обновление или вакуум) не мешает чтению файла базы данных.

  3. RBU работает поэтапно

    Операции RBU могут быть приостановлены и позже возобновлены, возможно, с перерывами на отключение электропитания и/или перезагрузку системы. Для обновления RBU исходное содержимое базы данных остается доступным для всех читателей базы данных до тех пор, пока не будет применено полное обновление — даже если обновление приостановлено и позже возобновлено.

Расширение RBU по умолчанию не включено. Чтобы включить его, скомпилируйте амальгамированный файл с опцией компиляции SQLITE_ENABLE_RBU.

2. Обновления RBU

2.1. Ограничения обновлений RBU

К обновлениям RBU применяются следующие ограничения:

  • Изменения должны состоять только из операций INSERT, UPDATE и DELETE. Операции CREATE и DROP не поддерживаются.

  • В операторах INSERT не могут использоваться значения по умолчанию.

  • Операторы UPDATE и DELETE должны определять целевые строки по rowid или по значениям НЕ NULL PRIMARY KEY.

  • Операторы UPDATE не могут изменять значения PRIMARY KEY или rowid.

  • Обновления RBU не могут быть применены к таблицам, содержащим столбец с именем «rbu_control».

  • Обновление RBU не сработает при срабатывании триггеров.

  • Обновление RBU не обнаружит и не предотвратит нарушения ограничений внешних ключей или CHECK.

  • Все обновления RBU используют механизм обработки ограничений «OR ROLLBACK».

  • Целевая база данных не может находиться в режиме WAL.

  • Целевая база данных не может содержать индексы по выражениям. Индексы по выражениям поддерживаются начиная с SQLite 3.30.0 (2019-10-04).
  • Другие записи не могут быть внесены в целевую базу данных во время применения обновления RBU. Для этого используется блокировка чтения на целевой базе данных.

2.2. Подготовка файла обновления RBU

Все изменения, которые должны быть применены RBU, хранятся в отдельной базе данных SQLite, называемой «базой данных RBU». База данных, которая должна быть изменена, называется «целевой базой данных».

Для каждой таблицы в целевой базе данных, которая будет изменена обновлением, в базе данных RBU создается соответствующая таблица. Схема таблицы базы данных RBU не совпадает со схемой целевой базы данных, но выводится из неё, как описано ниже.

Таблица базы данных RBU содержит одну строку для каждой строки целевой базы данных, вставленной, обновлённой или удалённой в результате обновления. Заполнение таблиц базы данных RBU описано в следующем разделе.

2.2.1. Схема базы данных RBU

Для каждой таблицы в целевой базе данных база данных RBU должна содержать таблицу с именем «data<целое число>_<имя-таблицы-цели>», где <имя-таблицы-цели> — имя таблицы в целевой базе данных, а <целое число> — любая последовательность из нуля или более цифровых символов (0-9). Таблицы в базе данных RBU обрабатываются в порядке по имени (от наименьшего к наибольшему в соответствии с порядком сортировки BINARY), поэтому порядок, в котором обновляются целевые таблицы, зависит от выбора части <целое число> имени таблицы data%. Хотя это может быть полезно при использовании RBU для обновления некоторых типов виртуальных таблиц, обычно нет причин использовать что-либо кроме пустой строки вместо <целое число>.

Таблица data_% должна иметь все те же столбцы, что и целевая таблица, плюс один дополнительный столбец под названием «rbu_control». Таблица data_% не должна иметь ограничений PRIMARY KEY или UNIQUE, но каждый столбец должен иметь тот же тип, что и соответствующий столбец в целевой базе данных. Столбец rbu_control не должен иметь никакого типа. Например, если целевая база данных содержит:

CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT, c UNIQUE);

Тогда база данных RBU должна содержать:

CREATE TABLE data_t1(a INTEGER, b TEXT, c, rbu_control);

Порядок столбцов в таблице data_% не имеет значения.

Если целевая таблица является виртуальной таблицей или таблицей, у которой нет объявления PRIMARY KEY, таблица data_% также должна содержать столбец с именем «rbu_rowid». Столбец rbu_rowid сопоставляется с ROWID таблиц. Например, если целевая база данных содержит что-либо из следующего:

CREATE VIRTUAL TABLE x1 USING fts3(a, b);
CREATE TABLE x1(a, b);

тогда база данных RBU должна содержать:

CREATE TABLE data_x1(a, b, rbu_rowid, rbu_control);

Виртуальные таблицы, для которых столбец «rowid» не работает как значение первичного ключа, не могут быть обновлены с помощью RBU.

Все нескрытые столбцы (т. е. все столбцы, соответствующие «SELECT *») целевой таблицы должны присутствовать в таблице ввода. Для виртуальных таблиц скрытые столбцы являются необязательными — они обновляются RBU, если присутствуют в таблице ввода, или не обновляются в противном случае. Например, для записи в таблицу fts4 со скрытым столбцом languageid, такой как:

CREATE VIRTUAL TABLE ft1 USING fts4(a, b, languageid='langid');

Можно использовать любую из следующих схем таблиц ввода:

CREATE TABLE data_ft1(a, b, langid, rbu_rowid, rbu_control);
CREATE TABLE data_ft1(a, b, rbu_rowid, rbu_control);

2.2.2. Содержимое базы данных RBU

Для каждой строки, которую необходимо вставить в целевую базу данных в рамках обновления RBU, соответствующая таблица data_% должна содержать одну запись, в которой столбец «rbu_control» содержит целое число 0. Другие столбцы должны содержать значения, составляющие новую запись для вставки.

Столбец «rbu_control» также может быть установлен в целое число 2 для INSERT. В этом случае новая строка бесшумно заменяет любую существующую строку, имеющую те же значения первичного ключа. Это эквивалентно DELETE, за которым следует INSERT с теми же значениями первичного ключа. Это не то же самое, что команда SQL REPLACE, так как в этом случае новая строка может заменить любые конфликтующие строки (т. е. те, которые конфликтуют из-за ограничений UNIQUE или индексов), а не только те, которые конфликтуют по первичным ключам.

Если у целевой таблицы есть целочисленный первичный ключ, невозможно вставить значение NULL в столбец IPK. Попытка сделать это приводит к ошибке SQLITE_MISMATCH.

Для каждой строки, которую необходимо удалить из целевой базы данных в рамках обновления RBU, соответствующая таблица data_% должна содержать одну запись, в которой столбец «rbu_control» содержит целое число 1. Реальные значения первичного ключа удаляемой строки должны храниться в соответствующих столбцах таблицы data_%. Значения, хранящиеся в других столбцах, не используются.

Для каждого ряда, который нужно обновить в целевой базе данных в рамках обновления RBU, соответствующая таблица data_% должна содержать одну запись с столбцом "rbu_control", значение которого должно быть текстового типа. Реальные значения первичного ключа, идентифицирующие ряд для обновления, должны храниться в соответствующих столбцах записи таблицы data_%, а также новые значения всех обновляемых столбцов. Текстовое значение в столбце "rbu_control" должно содержать такое же количество символов, как и столбцов в целевой таблице базы данных, и должно состоять только из символов 'x' и '.' (или в некоторых особых случаях 'd' - см. ниже). Для каждого столбца, который обновляется, соответствующий символ устанавливается в 'x'. Для тех, которые остаются неизменными, соответствующий символ значения rbu_control должен быть установлен в '.'. Например, для указанных выше таблиц, оператор обновления:

UPDATE t1 SET c = 'usa' WHERE a = 4;

представлен записью в таблице data_t1, созданной следующим образом:

INSERT INTO data_t1(a, b, c, rbu_control) VALUES(4, NULL, 'usa', '..x');

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

  • В формате "фоссильного различия" - формате, используемом для различий BLOB в системе управления исходным кодом Fossil; или
  • В пользовательском формате, определённом приложением RBU.

Формат фоссильного различия может использоваться только для обновления значений BLOB. Вместо хранения нового BLOB в таблице data_%, сохраняется фоссильное различие. А вместо указания 'x' в строке rbu_control для столбца, подлежащего обновлению, сохраняется символ 'f'. При обработке обновления 'f', RBU загружает исходные данные BLOB с диска, применяет к ним фоссильное различие и сохраняет результаты обратно в файл базы данных. Базы данных RBU, сгенерированные с помощью sqldiff --rbu, используют фоссильные различия, когда это позволяет сэкономить место в базе данных RBU.

Для использования пользовательского формата различий приложение RBU должно зарегистрировать пользовательскую SQL-функцию с именем "rbu_delta" перед началом обработки обновления. Функция rbu_delta() будет вызвана с двумя аргументами - исходным значением, хранящимся в столбце целевой таблицы, и значением различия, предоставленным в рамках обновления RBU. Она должна вернуть результат применения различия к исходному значению. Для использования пользовательской функции различий, символ значения rbu_control, соответствующий столбцу целевой таблицы для обновления, должен быть установлен в 'd' вместо 'x'. Затем, вместо обновления целевой таблицы значением, хранящимся в соответствующем столбце таблицы data_%, RBU вызывает пользовательскую SQL-функцию "rbu_delta()" и сохраняет результат в столбце целевой таблицы.

Например, эта запись:

INSERT INTO data_t1(a, b, c, rbu_control) VALUES(4, NULL, 'usa', '..d');

приводит к обновлению целевой таблицы базы данных примерно так:

UPDATE t1 SET c = rbu_delta(c, 'usa') WHERE a = 4;

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

INSERT INTO data_ft1(a, b, rbu_rowid, rbu_control) 
  VALUES(NULL, 'usa', 12, '.x');

приводит к результату, подобному:

UPDATE ft1 SET b = 'usa' WHERE rowid = 12;

В самих таблицах data_% не должно быть объявлений первичного ключа. Однако, RBU более эффективна, если чтение строк из каждой таблицы data_% в порядке "rowid" примерно равно чтению их в порядке сортировки по первичному ключу соответствующей целевой таблицы базы данных. Другими словами, строки должны быть отсортированы по полям первичного ключа целевой таблицы перед их вставкой в таблицы data_%.

2.2.3. Использование RBU с таблицами FTS3/4

Обычно таблица FTS3 или FTS4 является примером виртуальной таблицы с rowid, который работает как первичный ключ. Таким образом, для следующих таблиц FTS4:

CREATE VIRTUAL TABLE ft1 USING fts4(addr, text);
CREATE VIRTUAL TABLE ft2 USING fts4;             -- implicit "content" column

Таблицы data_% могут быть созданы следующим образом:

CREATE TABLE data_ft1 USING fts4(addr, text, rbu_rowid, rbu_control);
CREATE TABLE data_ft2 USING fts4(content, rbu_rowid, rbu_control);

И заполнены так, как будто целевая таблица — обычная таблица SQLite без явных столбцов первичного ключа.

Таблицы FTS4 без содержимого обрабатываются аналогично, за исключением того, что любая попытка обновления или удаления строк вызовет ошибку при применении обновления.

Таблицы FTS4 с внешним содержимым также можно обновлять с помощью RBU. В этом случае пользователю необходимо настроить базу данных RBU таким образом, чтобы тот же набор операций UPDATE, DELETE и INSERT применялся к индексу FTS4, что и к основной таблице содержимого. Как и при всех обновлениях таблиц FTS4 с внешним содержимым, пользователю также необходимо убедиться, что любые операции UPDATE или DELETE применяются к индексу FTS4 до их применения к основной таблице содержимого (см. документацию FTS4 для подробного объяснения). В RBU это делается путем обеспечения того, что имя таблицы data_%, используемой для записи в таблицу FTS4, сортируется раньше имени таблицы data_%, используемой для обновления основной таблицы содержимого, с помощью кодировки BINARY.

Для предотвращения дублирования данных в базе данных RBU может использоваться SQL-вид вместо одной из таблиц data_%. Например, для схемы целевой базы данных:

CREATE TABLE ccc(addr, text);
CREATE VIRTUAL TABLE ccc_fts USING fts4(addr, text, content=ccc);

может использоваться следующая схема базы данных RBU:

CREATE TABLE data_ccc(addr, text, rbu_rowid, rbu_control);
CREATE VIEW data0_ccc_fts AS SELECT * FROM data_ccc;

Затем таблица data_ccc может быть заполнена обычным способом обновлениями, предназначенными для таблицы базы данных ccc. Те же обновления будут считываться RBU из представления data0_ccc_fts и применяться к таблице FTS ccc_fts. Поскольку "data0_ccc_fts" меньше, чем "data_ccc", таблица FTS будет обновлена первой, как требуется.

Случаи, когда основная таблица содержимого имеет явный столбец INTEGER PRIMARY KEY, немного сложнее, так как текстовые значения, хранящиеся в столбце rbu_control, немного отличаются для индекса FTS и его основной таблицы содержимого. Для основной таблицы содержимого в любом значении rbu_control для явного IPK должен быть включен символ, но для самой таблицы FTS, у которой есть неявный rowid, он не должен быть. Это неудобно, но может быть решено с помощью более сложного представления, как показано ниже:

-- Target database schema
CREATE TABLE ddd(i INTEGER PRIMARY KEY, k TEXT);
CREATE VIRTUAL TABLE ddd_fts USING fts4(k, content=ddd);

-- RBU database schema
CREATE TABLE data_ccc(i, k, rbu_control);
CREATE VIEW data0_ccc_fts AS SELECT i AS rbu_rowid, k, CASE 
  WHEN rbu_control IN (0,1) THEN rbu_control ELSE substr(rbu_control, 2) END
FROM data_ccc;

Функция substr() в представлении SQL выше возвращает текст аргумента rbu_control без первого символа (который соответствует столбцу "i", который не требуется для таблицы FTS).

2.2.4. Автоматическое создание обновлений RBU с помощью sqldiff

Начиная с версии SQLite 3.9.0 (2015-10-14), утилита sqldiff может генерировать базы данных RBU, представляющие разницу между двумя базами данных с идентичными схемами. Например, следующая команда:

sqldiff --rbu t1.db t2.db

выводит скрипт SQL для создания базы данных RBU, которая, если используется для обновления базы данных t1.db, исправит её, чтобы её содержимое стало идентичным содержимому базы данных t2.db.

По умолчанию sqldiff пытается обработать все таблицы, не являющиеся виртуальными, в двух предоставленных ей базах данных. Если какая-либо таблица присутствует в одной базе данных, но отсутствует в другой, или если какая-либо таблица имеет немного другую схему в одной базе данных, это ошибка. Опция "--table" может оказаться полезной, если это вызывает проблемы.

Виртуальные таблицы по умолчанию игнорируются sqldiff. Однако, возможно явно создать таблицу RBU data_% для виртуальной таблицы, которая содержит rowid, функционирующий как первичный ключ, используя команду, подобную этой:

sqldiff --rbu --table <virtual-table-name> t1.db t2.db

К сожалению, несмотря на то, что виртуальные таблицы игнорируются по умолчанию, любые базовые таблицы базы данных, которые они создают для хранения данных в базе данных, не игнорируются, и sqldiff включит их в любую базу данных RBU. По этой причине пользователи, пытающиеся использовать sqldiff для создания обновлений RBU для применения к целевым базам данных с одной или несколькими виртуальными таблицами, скорее всего, должны будут запустить sqldiff с опцией --table отдельно для каждой таблицы, которую нужно обновить в целевой базе данных.

2.3. Программирование обновления RBU на C/C++

Интерфейс расширения RBU позволяет приложению применить обновление RBU, хранящееся в базе данных RBU, к существующей целевой базе данных. Процедура следующая:

  1. Открыть дескриптор RBU с помощью функции sqlite3rbu_open(T,A,S).

    Аргумент T — имя файла целевой базы данных. Аргумент A — имя файла базы данных RBU. Аргумент S — имя "базы данных состояния", используемой для хранения информации о состоянии, необходимой для возобновления обновления после прерывания. Аргумент S может быть NULL, в этом случае информация о состоянии сохраняется в базе данных RBU в разных таблицах, имена которых начинаются с "rbu_".

    Функция sqlite3rbu_open(T,A,S) возвращает указатель на объект "sqlite3rbu", который затем передаётся в последующие интерфейсы.

  2. Зарегистрировать все необходимые модули виртуальных таблиц с помощью дескриптора базы данных, возвращённого функцией sqlite3rbu_db(X) (где аргумент X — указатель sqlite3rbu, возвращённый функцией sqlite3rbu_open()). Кроме того, при необходимости зарегистрировать SQL-функцию rbu_delta() с помощью sqlite3_create_function_v2().

  3. Вызвать функцию sqlite3rbu_step(X) один или несколько раз для указателя объекта sqlite3rbu X. Каждый вызов sqlite3rbu_step() выполняет одну операцию b-дерева, поэтому может потребоваться тысячи вызовов для применения полного обновления. Интерфейс sqlite3rbu_step() вернёт SQLITE_DONE, когда обновление будет полностью применено.

  4. Вызвать sqlite3rbu_close(X) для уничтожения указателя объекта sqlite3rbu. Если функция sqlite3rbu_step(X) была вызвана достаточно раз для полного применения обновления к целевой базе данных, база данных RBU помечается как полностью применённая. В противном случае состояние применения обновления RBU сохраняется в базе данных состояния (или в базе данных RBU, если имя файла базы данных состояния в sqlite3rbu_open() равно NULL) для последующего возобновления обновления.

Если обновление частично применено к целевой базе данных к моменту вызова sqlite3rbu_close(), информация о состоянии сохраняется в базе данных состояния, если она существует, или в противном случае в базе данных RBU. Это позволяет последующим процессам автоматически возобновить обновление RBU с того места, где оно было прервано. Если информация о состоянии сохраняется в базе данных RBU, её можно удалить, удалив все таблицы, имена которых начинаются с "rbu_".

Дополнительные сведения см. в комментариях в файле заголовка sqlite3rbu.h.

3. RBU Вакуум

3.1. Ограничения RBU Вакуума

По сравнению с встроенной командой VACUUM SQLite, RBU Вакуум имеет следующие ограничения:

  • Его нельзя использовать с базой данных, содержащей индексы по выражениям.

  • База данных, которая вакуумируется, не может находиться в режиме WAL.

3.2. Программирование RBU Вакуума на C/C++

В этом разделе представлен обзор и пример кода, демонстрирующий интеграцию RBU Вакуума в приложение. Для получения подробной информации см. комментарии в файле заголовка sqlite3rbu.h.

Все приложения RBU Вакуума реализуют некоторую вариацию следующей процедуры:

  1. Дескриптор RBU создается вызовом sqlite3rbu_vacuum(T, S).

    Аргумент T — имя файла базы данных, которую необходимо выполнить вакуум. Аргумент S — имя базы данных, в которой модуль RBU сохранит свое состояние, если операция вакуума приостановлена.

    Если база данных состояния S не существует при вызове sqlite3rbu_vacuum(), она автоматически создается и заполняется единственной таблицей, используемой для хранения состояния вакуума RBU — "rbu_state". Если текущий вакуум RBU приостановлен, эта таблица заполняется данными состояния. В следующий раз, когда sqlite3rbu_vacuum() вызывается с тем же параметром S, она обнаруживает эти данные и пытается возобновить приостановленную операцию вакуума. Когда операция вакуума RBU завершается или сталкивается с ошибкой, RBU автоматически удаляет содержимое таблицы rbu_state. В этом случае следующий вызов sqlite3rbu_vacuum() инициирует полностью новую операцию вакуума с нуля.

    Хорошей практикой является установление соглашения для определения имени базы данных состояния вакуума RBU на основе имени целевой базы данных. Приведенный ниже пример кода использует «<target>-vacuum», где <target> — имя базы данных, которая вакуумируется.

  2. Любые пользовательские последовательности сортировки, используемые индексами в базе данных, которая вакуумируется, регистрируются в обоих дескрипторах базы данных, возвращаемых функцией sqlite3rbu_db().

  3. Функция sqlite3rbu_step() вызывается для дескриптора RBU до тех пор, пока вакуум RBU не завершится, не произойдет ошибка или приложение не захочет приостановить вакуум RBU.

    Каждый вызов sqlite3rbu_step() выполняет небольшую часть работы по завершению операции вакуума. В зависимости от размера базы данных для одного вакуума может потребоваться тысячи вызовов sqlite3rbu_step(). sqlite3rbu_step() возвращает SQLITE_DONE, если операция вакуума завершена, SQLITE_OK, если операция вакуума не завершена, но ошибок не возникло, и код ошибки SQLite, если возникла ошибка. Если ошибка возникает, все последующие вызовы sqlite3rbu_step() немедленно возвращают тот же код ошибки.

  4. Наконец, sqlite3rbu_close() вызывается для закрытия дескриптора RBU. Если приложение прекратило вызовы sqlite3rbu_step() до завершения вакуума или возникновения ошибки, состояние вакуума сохраняется в базе данных состояния для возможности его возобновления позже.

    Как и sqlite3rbu_step(), если операция вакуума завершена, sqlite3rbu_close() возвращает SQLITE_DONE. Если вакуум не завершен, но ошибок не возникло, возвращается SQLITE_OK. В противном случае, если возникла ошибка, возвращается код ошибки SQLite. Если ошибка произошла в ходе предыдущего вызова sqlite3rbu_step(), sqlite3rbu_close() возвращает тот же код ошибки.

Следующий пример кода иллюстрирует описанные выше методы.

/*
** Either start a new RBU vacuum or resume a suspended RBU vacuum on 
** database zTarget. Return when either an error occurs, the RBU 
** vacuum is finished or when the application signals an interrupt
** (code not shown).
**
** If the RBU vacuum is completed successfully, return SQLITE_DONE.
** If an error occurs, return SQLite error code. Or, if the application
** signals an interrupt, suspend the RBU vacuum operation so that it
** may be resumed by a subsequent call to this function and return
** SQLITE_OK.
**
** This function uses the database named "<zTarget>-vacuum" for
** the state database, where <zTarget> is the name of the database 
** being vacuumed.
*/
int do_rbu_vacuum(const char *zTarget){
  int rc;
  char *zState;                   /* Name of state database */
  sqlite3rbu *pRbu;               /* RBU vacuum handle */

  zState = sqlite3_mprintf("%s-vacuum", zTarget);
  if( zState==0 ) return SQLITE_NOMEM;
  pRbu = sqlite3rbu_vacuum(zTarget, zState);
  sqlite3_free(zState);

  if( pRbu ){
    sqlite3 *dbTarget = sqlite3rbu_db(pRbu, 0);
    sqlite3 *dbState = sqlite3rbu_db(pRbu, 1);

    /* Any custom collation sequences used by the target database must
    ** be registered with both database handles here.  */

    while( sqlite3rbu_step(pRbu)==SQLITE_OK ){
      if( <application has signaled interrupt> ) break;
    }
  }
  rc = sqlite3rbu_close(pRbu);
  return rc;
}

Эта страница была в последний раз изменена 08.01.2022 05:02:57 UTC

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

Spec-Zone.ru

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