RussianLDP Рейтинг@Mail.ru
WebMoney: 
WMZ Z294115950220 
WMR R409981405661 
WME E134003968233 
Visa 
4274 3200 2453 6495 

Small. Fast. Reliable.
Choose any three.

Изменения SQLite от версии 3.4.2 до 3.5.0

SQLite version 3.5.0 (2007-09-04) вводит новый слой интерфейса OS, который несовместим со всеми предыдущими версиями SQLite. Кроме того, несколько существующих интерфейсов были обобщены, чтобы работать через все соединения с базой данных в рамках процесса, а не просто все связи в потоке. Цель этой статьи состоит в том, чтобы описать изменения 3.5.0 подробно так, чтобы пользователи предыдущих версий SQLite могли модернизировать до более новых версий.

1.0. Обзор изменений

Быстрое перечисление изменений в версии 3.5.0 SQLite обеспечивается здесь. Последующие секции опишут эти изменения более подробно.

  1. Слой интерфейса OS был полностью переделан:
    1. Недокументированный интерфейс sqlite3_os_switch() был удален.
    2. Флаг времени компиляции SQLITE_ENABLE_REDEF_IO больше не функционирует. Процедуры I/O теперь всегда redefinable.
    3. Три новых объекта определяются для определения процедур I/O: sqlite3_vfs, sqlite3_file и sqlite3_io_methods.
    4. Три новых интерфейса используются, чтобы создать альтернативные интерфейсы OS: sqlite3_vfs_register(), sqlite3_vfs_unregister() и sqlite3_vfs_find().
    5. Новый интерфейс был добавлен для обеспечения дополнительного контроля над созданием новых соединений с базой данных: sqlite3_open_v2(). Устаревшие интерфейсы sqlite3_open() и sqlite3_open16() продолжают полностью поддерживаться.
  2. Дополнительный общий кэш и особенности управления памятью, которые были введены в версии 3.3.0, могут теперь использоваться через многократные потоки в рамках того же самого процесса. Раньше эти расширения относились только к соединениям с базой данных, работающим в единственном потоке.
    1. sqlite3_enable_shared_cache() теперь относится ко всем потокам в рамках процесса, не только к одному, в котором этим управляли.
    2. sqlite3_soft_heap_limit() теперь относится ко всем потокам в рамках процесса, не только к одному, в котором этим управляли.
    3. sqlite3_release_memory() теперь попытается уменьшить использования памяти через все соединения с базой данных во всех потоках, не только связи в потоке, где интерфейс вызывают.
    4. sqlite3_thread_cleanup() ничего не делает.
  3. Ограничения на использование того же самого соединения с базой данных многократными потоками отменены. Для многократных потоков теперь безопасно использовать то же самое соединение с базой данных в то же время.
  4. Есть теперь выбор времени компиляции, который позволяет приложению определить альтернативу malloc()/free(), не имея необходимости изменять любой основной код SQLite.
  5. Есть теперь выбор времени компиляции, который позволяет определить альтернативу mutex, не имея необходимости изменять любой основной код SQLite.

Из этих изменений, только 1a и с 2a по 2c имеют несовместимости в любом формальном смысле. Но пользователи, которые ранее сделали модификации исходного текста SQLite (например, чтобы добавить свой слой OS для встроенных аппаратных средств) могли бы найти, что эти изменения оказывают большее влияние. С другой стороны, важная цель этих изменений состоит в том, чтобы сделать намного легче настройку SQLite для использования на различных операционных системах.

2.0. Слой интерфейса OS

Если ваша система определит свой интерфейс OS для SQLite или если вы использовали недокументированный sqlite3_os_switch(), то необходимо будет сделать модификации, чтобы модернизировать до версии 3.5.0 SQLite. Это может казаться болезненным на первый взгляд. Но вы, вероятно, обнаружите, что ваши изменения делаются меньше, их легче понять и справиться с новым интерфейсом SQLite. Вероятно, что ваши изменения будут теперь также работать беспрепятственно с объединением SQLite. Вы больше не должны будете делать изменения в коде SQLite. Все ваши изменения могут быть вызваны кодом приложения, их можно скомпоновать со стандартной, неизмененной, версией SQLite. Кроме того, слой интерфейса OS, который был раньше не документирован, теперь официально интерфейс поддержки для SQLite. Таким образом, у вас есть некоторая гарантия, что это будет одноразовым изменением и что ваш новый бэкенд продолжит работать в будущих версиях SQLite.

2.1. Объект виртуальной файловой системы

Новый интерфейс OS для SQLite строится вокруг объекта, названного sqlite3_vfs. "vfs" это "Virtual File System". Объект sqlite3_vfs в основном структура, содержащая указатели на функции, которые осуществляют примитивный дисковый I/O, который должен выполнить SQLite, чтобы прочитать и написать базы данных. В этой статье мы будем часто именовать объекты sqlite3_vfs как "VFS".

SQLite в состоянии использовать много VFS в то же время. Каждое отдельное соединение с базой данных связано со всего одним VFS. Но если у вас есть многократные соединения с базой данных, каждая связь может быть связана с различным VFS.

Всегда есть VFS по умолчанию. Старые интерфейсы sqlite3_open() и sqlite3_open16() всегда его используют. Новый интерфейс для создания соединений с базой данных, sqlite3_open_v2(), позволяет вам определять, какой VFS вы хотите использовать по имени.

2.1.1. Регистрация новых объектов VFS

Обычные сборки SQLite для Unix или Windows поставляются с единственным VFS, названным "unix" или "win32", соответственно. Этот VFS также используется по умолчанию. Таким образом, если вы будете использовать старые функции, все продолжит работать, как прежде. Изменение в том, что у применения теперь есть гибкость добавления новых модулей VFS, чтобы осуществить настроенный слой OS. sqlite3_vfs_register() API может использоваться, чтобы сказать SQLite об одном или нескольких определенных применением модулях VFS:

int sqlite3_vfs_register(sqlite3_vfs*, int makeDflt);

Приложения могут вызвать sqlite3_vfs_register() в любое время, хотя, конечно, VFS должен быть зарегистрирован, прежде чем сможет использоваться. Первый аргумент это указатель на настроенный объект VFS, который подготовило применение. Второй аргумент true, чтобы сделать новый VFS по умолчанию, чтобы это использовалось старыми sqlite3_open() и sqlite3_open16() API. Если новый VFS не по умолчанию, то необходимо будет, вероятно, использовать новый sqlite3_open_v2() API. Отметьте, однако, что, если новый VFS это единственный VFS, известный SQLite (если SQLite был собран без его обычного VFS по умолчанию или если предварительно собранный VFS по умолчанию удален через sqlite3_vfs_unregister()), новый VFS автоматически становится VFS по умолчанию независимо от параметра makeDflt в sqlite3_vfs_register().

Стандартные сборки включают VFS по умолчанию "unix" или "win32". Но если вы используете выбор времени компиляции -DOS_OTHER=1, тогда SQLite собирается без VFS по умолчанию. В этом случае приложение должно зарегистрировать по крайней мере один VFS до запроса sqlite3_open(). Это подход, который должны использовать встраиваемые приложения. Вместо того, чтобы изменять SQLite, чтобы вставить альтернативный слой OS, как было сделано в предшествующих выпусках SQLite, соберите неизмененный исходный файл SQLite с опцией -DOS_OTHER=1, потом вызовите sqlite3_vfs_register(), чтобы определить интерфейс к основной файловой системе до создания любых соединений с базой данных.

2.1.2. Дополнительный контроль над объектами VFS

sqlite3_vfs_unregister() API используется, чтобы удалить существующий VFS из системы.

int sqlite3_vfs_unregister(sqlite3_vfs*);

sqlite3_vfs_find() API используется, чтобы определить местонахождение конкретного VFS по имени. Его прототип такой:

sqlite3_vfs *sqlite3_vfs_find(const char *zVfsName);

Аргумент это символьное имя для желаемого VFS. Если аргумент NULL, возвращен VFS по умолчанию. Функция возвращает указатель на объект sqlite3_vfs, который осуществляет VFS. Или это возвращает NULL, если никакой объект не мог бы быть найден, который соответствовал критериям поиска.

2.1.3. Модификации существующего VFS

Как только VFS был зарегистрирован, он никогда не должен изменяться. Если изменение в поведении требуется, новый VFS должен быть зарегистрирован. Применение могло, возможно, использовать sqlite3_vfs_find(), чтобы определить местонахождение старого VFS, превратить копию старого VFS в новый объект sqlite3_vfs, сделать желаемые модификации к новому VFS, разрегистрировать старый VFS, затем зарегистрировать новый VFS на его месте. Существующие соединения с базой данных продолжили бы использовать старый VFS даже после того, как это разрегистрировано, но новые соединения с базой данных использовали бы новый VFS.

2.1.4. Объект VFS

Объект VFS это экземпляр следующей структуры:

typedef struct sqlite3_vfs sqlite3_vfs;
struct sqlite3_vfs {
  int iVersion;            /* Structure version number */
  int szOsFile;            /* Size of subclassed sqlite3_file */
  int mxPathname;          /* Maximum file pathname length */
  sqlite3_vfs *pNext;      /* Next registered VFS */
  const char *zName;       /* Name of this virtual file system */
  void *pAppData;          /* Pointer to application-specific data */
  int (*xOpen)(sqlite3_vfs*, const char *zName, sqlite3_file*,
               int flags, int *pOutFlags);
  int (*xDelete)(sqlite3_vfs*, const char *zName, int syncDir);
  int (*xAccess)(sqlite3_vfs*, const char *zName, int flags);
  int (*xGetTempName)(sqlite3_vfs*, char *zOut);
  int (*xFullPathname)(sqlite3_vfs*, const char *zName, char *zOut);
  void *(*xDlOpen)(sqlite3_vfs*, const char *zFilename);
  void (*xDlError)(sqlite3_vfs*, int nByte, char *zErrMsg);
  void *(*xDlSym)(sqlite3_vfs*,void*, const char *zSymbol);
  void (*xDlClose)(sqlite3_vfs*, void*);
  int (*xRandomness)(sqlite3_vfs*, int nByte, char *zOut);
  int (*xSleep)(sqlite3_vfs*, int microseconds);
  int (*xCurrentTime)(sqlite3_vfs*, double*);
  /* New fields may be appended in figure versions.  The iVersion
  ** value will increment whenever this happens. */
};

Чтобы создать новый VFS, приложение заполняет экземпляр этой структуры с соответствующими значениями и затем вызывает sqlite3_vfs_register().

Поле iVersion в sqlite3_vfs должно быть 1 для SQLite version 3.5.0. Это число может увеличиться в будущих версиях SQLite, если мы должны изменить объект VFS в некотором роде. Мы надеемся, что этого никогда не будет, но на всякий случай предусмотрели.

Поле szOsFile это размер в байтах структуры, которая определяет открытый файл: объект sqlite3_file. Этот объект будет описан более полно ниже. Каждое внедрение VFS может определить свой собственный объект sqlite3_file, содержащий любую информацию, которую внедрение VFS должно хранить об открытом файле. SQLite должен знать, насколько большой этот объект, чтобы предварительно ассигновать достаточно места.

Поле mxPathname это максимальная длина пути файла, который может использовать этот VFS. SQLite иногда должен предварительно ассигновать буфера этого размера, таким образом, это должно быть максимально маленьким. Некоторые файловые системы разрешают огромные пути, но на практике пути редко были больше 100 байтов или около этого. Вы не должны помещать самый большой путь, с которым основная файловая система может обращаться, здесь. Необходимо поместить самый большой путь, с которым вы хотите, чтобы SQLite был в состоянии обращаться. Несколько сотен это хорошее значение в большинстве случаев.

Поле pNext используется внутренне SQLite. Определенно, SQLite использует это поле, чтобы сформировать связанный список зарегистрированных VFS.

Поле zName это символьное имя VFS. Это имя, по которому sqlite3_vfs_find() ищет VFS.

pAppData не использован ядром SQLite. Указатель доступен, чтобы хранить вспомогательную информацию, которую информация VFS могла бы хотеть.

Остающиеся области объекта sqlite3_vfs хранят указатели на функции, которые осуществляют примитивные операции. Мы называем эти "методы". Первый метод, xOpen, используется, чтобы открыть файлы на носителе. Результат: объект sqlite3_file. Есть дополнительные методы, определенные самим объектом sqlite3_file, которые используются, чтобы прочитать, написать и закрыть файл. Дополнительные методы детализированы ниже. Имя файла находится в UTF-8. SQLite гарантирует, что последовательность zFilename, переданная xOpen(), является полным путем, как произведено xFullPathname() и что последовательность будет действительна и неизменна, пока не будет вызван xClose(). Таким образом, sqlite3_file может сохранить указатель на имя файла, если это должно помнить имя файла по некоторым причинам. Аргументом flags для xOpen() является копия аргумента флагов sqlite3_open_v2(). Если sqlite3_open() или sqlite3_open16() используется, то флаги SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE. Если xOpen() открывает файл только для чтения, он устанавливает *pOutFlags, чтобы включить SQLITE_OPEN_READONLY. Другие биты в *pOutFlags могут быть установлены. SQLite также добавит один из следующих флагов к xOpen(), в зависимости от открываемого объекта:

Внедрение файлового ввода-вывода может использовать флаги типа объекта для изменения того, как оно имеет дело с файлами. Например, применение, которое не заботится о восстановлении катастрофы или обратной перемотке, могло бы заставить открытый журнал ничего не делать. Записи в него также могут не работать. Любая попытка прочитать журнал возвращает SQLITE_IOERR. Или внедрение могло бы использовать файл базы данных, выровненный со страницей на сектор для чтения и записи в произвольном порядке и настроил свою подсистему I/O соответственно. SQLite мог бы также добавить один из следующих флагов к методу xOpen: SQLITE_OPEN_DELETEONCLOSE означает, что файл должен быть удален, когда закрывается. Это будет всегда устанавливаться для баз данных TEMP, журналов и для поджурналов. SQLITE_OPEN_EXCLUSIVE означает, что файл должен быть открыт для эксклюзивного доступа. Этот флаг установлен для всех файлов за исключением главного файла базы данных. Структура sqlite3_file, переданная как третий аргумент xOpen, ассигнуется вызывающим. xOpen просто заполняет ее. Вызывающий ассигнует минимум szOsFile байтов для структуры sqlite3_file.

Различия между базой данных SQLITE_OPEN_TEMP_DB и SQLITE_OPEN_TRANSIENT_DB: SQLITE_OPEN_TEMP_DB используется для явно заявленных и названных таблиц TEMP (используя CREATE TEMP TABLE) или для названных таблиц во временной базе данных, которая создается, открывая базу данных с именем файла, которое является пустой строкой. SQLITE_OPEN_TRANSIENT_DB хранит таблицу базы данных, которую SQLite создает автоматически, чтобы оценить подвопрос или пункт GROUP BY или ORDER BY. Базы данных TEMP_DB и TRANSIENT_DB частные и удалены автоматически. Базы данных TEMP_DB существует в период соединения с базой данных. Базы данных TRANSIENT_DB существуют только при работе единственного SQL-оператора.

Метод xDelete используется, чтобы удалить файл. Название файла дано во втором параметре. Имя файла будет в UTF-8. VFS должен преобразовать имя файла в любое символьное представление, которое ожидает основная операционная система. Если syncDir = true, xDelete не должен возвращаться до изменения директивного содержания для каталога, содержащего удаленный файл, чтобы гарантировать, что файл не "вновь появляется", если перебой в питании происходит вскоре после действия.

Метод xAccess используется, чтобы проверить права доступа на файле. Имя файла будет закодировано в UTF-8. Аргументом flags будет SQLITE_ACCESS_EXISTS, чтобы проверить на существование файла, SQLITE_ACCESS_READWRITE, чтобы проверить, чтобы видеть, читаемый и перезаписываемый файл или нет, или SQLITE_ACCESS_READ, чтобы проверить, читаемый ли файл по крайней мере. "file", названный вторым параметром, мог бы быть именем каталога.

Метод xGetTempName вычисляет название временного файла, который может использовать SQLite. Имя должно быть написано в буфер, данный вторым параметром. SQLite измерит тот буфер, чтобы хранить по крайней мере, mxPathname байт. Произведенное имя файла должно быть в UTF-8. Чтобы избежать проблем безопасности, произведенное временное имя файла должно содержать достаточно хаотичности, чтобы препятствовать тому, чтобы нападавший предположил временное имя файла заранее.

Метод xFullPathname используется, чтобы преобразовать относительный путь в полный. Получающийся полный путь написан в буфер, обеспеченный третьим параметром. SQLite измерит буфер вывода к по крайней мере mxPathname байт. Оба имени ввода и вывода должны быть в UTF-8.

Методы xDlOpen, xDlError, xDlSym и xDlClose используются для доступа к общим библиотекам во время выполнения. Эти методы могут быть опущены (и их указатели установлены к нолю), если библиотека собрана с SQLITE_OMIT_LOAD_EXTENSION или если sqlite3_enable_load_extension() никогда не используется, чтобы позволить динамическую дополнительную нагрузку. Метод xDlOpen открывает общую библиотеку или DLL и возвращает указатель на обработчик. Если не открылась, вернет NULL. Если открытие терпит неудачу, метод xDlError может использоваться, чтобы получить текстовое сообщение об ошибке. Сообщение написано в буфер zErrMsg третьего параметра, который является по крайней мере nByte байт в длину. xDlSym возвращает указатель на символ в общей библиотеке. Название символа дано вторым параметром. Кодирование UTF-8 принято. Если символ не найден, вернется NULL. xDlClose закрывает общую библиотеку.

Метод xRandomness используется точно однажды, чтобы инициализировать генератор псевдослучайного числа (PRNG) в SQLite. Только xRandomness метод применяется на VFS по умолчанию. К методам xRandomness на другом VFS никогда не получает доступ SQLite. xRandomness просит, чтобы nByte байт хаотичности были написаны в zOut. Возвращает фактическое число байтов полученной хаотичности. Качество хаотичности, так полученной, определит качество хаотичности, произведенной встроенными функциями SQLite вроде random() и randomblob(). SQLite также использует свой PRNG, чтобы произвести временные имена файлов. На некоторых платформах (исключая: Windows) SQLite предполагает, что временные имена файлов уникальны, на самом деле не проверяя на столкновения, таким образом, важно иметь хаотичность хорошего качества, даже если random() и randomblob() никогда не используются.

Метод xSleep используется, чтобы приостановить вызывающий поток на хотя бы заданное число микросекунд. Этот метод используется, чтобы осуществить sqlite3_sleep() и sqlite3_busy_timeout() API. В случае sqlite3_sleep() всегда используется метод xSleep VFS по умолчанию. Если у основной системы нет способности сна с точностью до микросекунд, то время сна должно быть округлено, xSleep возвращает это округленное значение.

Метод xCurrentTime находит текущее время и дату и пишет результат как значение с плавающей точкой двойной точности в указатель, обеспеченный вторым параметром. Время и дата даны в coordinated universal time (UTC) является номером дня по Юлианскому календарю.

2.1.5. Открытый объект файла

Результатом открытия файла является экземпляр объекта sqlite3_file. Объект sqlite3_file это абстрактный базовый класс, определенный следующим образом:

typedef struct sqlite3_file sqlite3_file;
struct sqlite3_file {const struct sqlite3_io_methods *pMethods;};

Каждое внедрение VFS подклассифицирует sqlite3_file, добавляя дополнительные области в конце, чтобы содержать информацию, которую VFS должен знать об открытом файле. Не имеет значения, какая информация хранится, пока полный размер структуры не превышает szOsFile в объекте sqlite3_vfs.

Объект sqlite3_io_methods это структура, которая содержит указатели на методы для чтения, записи и другого контакта с файлами. Этот объект определяется следующим образом:

typedef struct sqlite3_io_methods sqlite3_io_methods;
struct sqlite3_io_methods {
  int iVersion;
  int (*xClose)(sqlite3_file*);
  int (*xRead)(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
  int (*xWrite)(sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst);
  int (*xTruncate)(sqlite3_file*, sqlite3_int64 size);
  int (*xSync)(sqlite3_file*, int flags);
  int (*xFileSize)(sqlite3_file*, sqlite3_int64 *pSize);
  int (*xLock)(sqlite3_file*, int);
  int (*xUnlock)(sqlite3_file*, int);
  int (*xCheckReservedLock)(sqlite3_file*);
  int (*xFileControl)(sqlite3_file*, int op, void *pArg);
  int (*xSectorSize)(sqlite3_file*);
  int (*xDeviceCharacteristics)(sqlite3_file*);
  /* Additional methods may be added in future releases */
};

Поле iVersion sqlite3_io_methods обеспечивается как страховка от будущих изменений. iVersion должно всегда быть 1 для версии SQLite version 3.5.

Метод xClose закрывает файл. Место для структуры sqlite3_file освобождено вызывающим. Но если sqlite3_file содержит указатели на другую ассигнованную память или ресурсы, те должны быть освобождены методом xClose.

Метод xRead читает iAmt байт из файла, начиная с байтового смещения iOfst. Прочитанные данные хранятся в указателе второго параметра. xRead возвращает SQLITE_OK при успехе, SQLITE_IOERR_SHORT_READ, если это не смогло прочитать полное число байтов, потому что это достигло конца файла или SQLITE_IOERR_READ для любой другой ошибки.

Метод xWrite пишет iAmt байт данных из второго параметра в файл, начиная со смещения iOfst. Если размер файла меньше, чем iOfst байт до записи, xWrite должен гарантировать, что файл расширен нолями до хотя бы iAmt+iOfst байт до начала записи. xWrite расширяет файл по мере необходимости так, чтобы размер файла был, по крайней мере, iAmt+iOfst байтами в конце xWrite. xWrite возвращает SQLITE_OK при успехе, Если запись не может закончиться, потому что среда базовой системы хранения полна, то вернется SQLITE_FULL. SQLITE_IOERR_WRITE должен быть возвращен для любой другой ошибки.

Метод xTruncate усекает файл до nByte байт в длину. Если файл уже nByte байт или меньше, тогда этот метод ничего не делает. Метод xTruncate вернет SQLITE_OK при успехе и SQLITE_IOERR_TRUNCATE, если что-нибудь идет не так, как надо.

Метод xSync используется, чтобы вынудить ранее записанные данные покинуть кэш операционной системы. Второй параметр обычно SQLITE_SYNC_NORMAL. Если второй параметр SQLITE_SYNC_FULL, xSync должен удостовериться, что данные также сброшены из кэша контроллера диска. SQLITE_SYNC_FULL аналог F_FULLSYNC ioctl() в Mac OS X. xSync возвращает SQLITE_OK при успехе и SQLITE_IOERR_FSYNC иначе.

Метод xFileSize() определяет текущий размер файла в байтах и вписывает в *pSize. Это возвращает SQLITE_OK при успехе и SQLITE_IOERR_FSTAT, если что-то идет не так, как надо.

Методы xLock и xUnlock используются, чтобы установить и очистить блокировки файла. SQLite поддерживает пять уровней блокировки файла в:

Конкретная реализация может поддержать некоторое подмножество этих уровней пока это отвечает другим требованиям этого параграфа. Уровень определяется как второй аргумент xLock и xUnlock. xLock увеличивает уровень до указанного уровня захвата или выше. xUnlock уменьшает уровень захвата до уровня не ниже, чем определенный. SQLITE_LOCK_NONE означает, что файл разблокируют. SQLITE_LOCK_SHARED дает разрешение прочитать файл. Многократные соединения с базой данных могут хранить SQLITE_LOCK_SHARED в то же время. SQLITE_LOCK_RESERVED аналог SQLITE_LOCK_SHARED, это разрешение прочитать файл. Но только единственная связь может держать зарезервированную блокировку в любом моменте времени. SQLITE_LOCK_PENDING также разрешает прочитать файл. Другие связи могут продолжить читать файл также, но никакой другой связи не позволяют нарастить блокировку от none до shared. SQLITE_LOCK_EXCLUSIVE это разрешение писать в файл. Только единственная связь может держать монопольную блокировку, и никакая другая связь не может держать блокировку (кроме "none") в то время, как одна связь держит монопольную блокировку. xLock возвращает SQLITE_OK при успехе, SQLITE_BUSY, если это неспособно получить блокирвку или SQLITE_IOERR_RDLOCK, если что-то еще идет не так, как надо. xUnlock возвращает SQLITE_OK при успехе или SQLITE_IOERR_UNLOCK иначе.

Метод xCheckReservedLock() проверяет, держит ли другая связь или другой процесс в настоящее время блокировку reserved, pending или exclusive на файле. Возвращает true или false.

xFileControl() это универсальный интерфейс, который позволяет внедрениям VFS непосредственно управлять открытым файлом, используя (новый и экспериментальный) sqlite3_file_control(). Второй аргумент "op" задает код операции в виде целого числа. Третий аргумент это универсальный указатель, который предназначается, чтобы быть указателем на структуру, которая может содержать аргументы или место, в которое можно написать возвращаемые значения. Потенциальное использование для xFileControl() могло бы быть функциями, чтобы позволить блокировки с тайм-аутами, изменить стратегию захвата (например, чтобы использовать точечные блокировки файла), справиться о статусе блокировки или снять старые. Ядро SQLite резервирует коды операции меньше 100 для его собственного использования. Список кодов операции меньше 100 доступен. Запросы, которые определяют свой метод xFileControl, должны использовать коды операции больше 100, чтобы избежать конфликтов.

xSectorSize возвращает "размер сектора" основных энергонезависимых носителей. "Сектор" определяется как самая маленькая единица хранения, которая может быть написана, не нарушая смежное хранение. На дисководе "размер сектора" до недавнего времени составлял 512 байт, хотя есть усилия увеличить это значение до 4KiB. SQLite должен знать размер сектора так, чтобы это могло написать полный сектор за один раз, и таким образом не портить смежное пространство памяти, если потери питания происходят посреди записи.

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

SQLITE_IOCAP_ATOMIC значит, что все записи атомные в том смысле, что или вся запись происходит, или не происходит вообще. Другие значения SQLITE_IOCAP_ATOMICnnn указывают, что записи в выровненных блоках обозначенного размера атомные. SQLITE_IOCAP_SAFE_APPEND означает, что, расширяя файл с новыми данными, сначала новые данные написаны, и затем размер файла обновляется. Таким образом, если перебой в питании происходит, нет никакого шанса, что файл, возможно, был расширен с хаотичностью. SQLITE_IOCAP_SEQUENTIAL значит, что все записи происходят в порядке, в котором они выпущены и не переупорядочены основной файловой системой.

2.1.6. Контрольный список для создания новой VFS

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

  1. Определите соответствующий подкласс объекта sqlite3_file.
  2. Осуществите методы, требуемые объектом sqlite3_io_methods.
  3. Создайте статический и постоянный объект sqlite3_io_methods, содержащий указатели на методы от предыдущего шага.
  4. Осуществите метод xOpen, который открывает файл и наполняет объект sqlite3_file, включая урегулирование pMethods, чтобы указать на объект sqlite3_io_methods от предыдущего шага.
  5. Осуществите другие методы, требуемые sqlite3_vfs.
  6. Определите статическую (но не постоянную) структуру sqlite3_vfs, которая содержит указатели на xOpen и другие методы и содержит соответствующие значения для iVersion, szOsFile, mxPathname, zName и pAppData.
  7. Осуществите процедуру, которая вызывает sqlite3_vfs_register() и передает указатель на sqlite3_vfs от предыдущего шага. Эта процедура, вероятно, единственный экспортируемый символ в исходном файле, который осуществляет ваш VFS.

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

3.0. Подсистема выделения памяти

С версии 3.5 SQLite получает всю память кучи с использованием sqlite3_malloc(), sqlite3_free() и sqlite3_realloc(). Это существовало в предыдущих версиях SQLite, но SQLite ранее обошел этот установленный порядок и использовал его собственного распределителя памяти. Это изменено в версии 3.5.0.

Исходное дерево SQLite на самом деле содержит много версий распределителя памяти. Наиболее быстродействующая версия, найденная в "mem1.c", используется для большинства сборок. Но если флаг SQLITE_MEMDEBUG позволен, отдельный распределитель памяти "mem2.c" используется вместо этого. Распределитель mem2.c осуществляет много перехватов, чтобы сделать проверку на ошибки и моделировать сбои выделения памяти для целей тестирования. Оба распределителя используют malloc()/free() в стандартной библиотеке для C.

Настройки не требуются, чтобы использовать любого из этих стандартных распределителей памяти. Если SQLite собран с SQLITE_OMIT_MEMORY_ALLOCATION, тогда никакое внедрение для sqlite3_malloc(), sqlite3_realloc() и sqlite3_free() не обеспечиваются. Вместо этого применение, которое связывается с SQLite, должно обеспечить свое собственное внедрение этих функций. Применение не обязано использовать malloc()/free() в стандартной библиотеке для C. Встраиваемое приложение могло бы предоставить альтернативный распределитель памяти, который использует память для пула постоянной памяти, отложенного для исключительного использования SQLite, например.

Приложения, которые осуществляют их собственный распределитель памяти, должны обеспечить внедрение для обычных трех функций распределения sqlite3_malloc(), sqlite3_realloc() и sqlite3_free(). Они должны также осуществить четвертую функцию:

int sqlite3_memory_alarm(void(*xCallback)(void *pArg, sqlite3_int64 used,
                         int N), void *pArg, sqlite3_int64 iThreshold);

sqlite3_memory_alarm используется, чтобы зарегистрировать отзыв на события выделения памяти. Этот установленный порядок регистрирует или очищает отзыв, который срабатывает, когда ассигнованный объем памяти превышает iThreshold. Только единственный отзыв может быть зарегистрирован за один раз. Каждый вызов sqlite3_memory_alarm() переписывает предыдущий отзыв. Отзыв отключен, установив xCallback = NULL.

Параметры отзыва это значение pArg, объем памяти, использующийся в настоящее время, и размер распределения, которое вызвало отзыв. Отзыв по-видимому вызовет sqlite3_free(), чтобы освободить память. Отзыв может вызвать sqlite3_malloc() или sqlite3_realloc(), но если это сделает, никакие дополнительные отзывы не будут вызваны рекурсивными вызовами.

sqlite3_soft_heap_limit() работает, регистрируя сигнал памяти в мягком пределе кучи и вызывая sqlite3_release_memory() в сигнальном отзыве. Прикладные программы не должны пытаться использовать sqlite3_memory_alarm(), потому что выполнение этого вмешается в модуль sqlite3_soft_heap_limit(). Этот интерфейс выставляется только, чтобы приложения могли обеспечить свое собственное альтернативное внедрение, когда ядро SQLite собрано с SQLITE_OMIT_MEMORY_ALLOCATION.

Встроенные распределители памяти в SQLite также обеспечивают следующие дополнительные интерфейсы:

sqlite3_int64 sqlite3_memory_used(void);
sqlite3_int64 sqlite3_memory_highwater(int resetFlag);

Эти интерфейсы могут использоваться приложением, чтобы контролировать, сколько памяти SQLite использует. sqlite3_memory_used() возвращает число байтов использующейся в настоящее время памяти, а sqlite3_memory_highwater() возвращает максимальное мгновенное использование памяти. Никакой установленный порядок не включает издержки, связанные с распределителем памяти. Этот установленный порядок обеспечивается для использования применением. SQLite никогда не призывает их сам. Таким образом, если применение обеспечивает свою собственную подсистему выделения памяти, оно может опустить эти интерфейсы при желании.

4.0. Подсистема Mutex

SQLite всегда был ориентирован на многопотоковое исполнение в том смысле, что безопасно использовать различные соединения с базой данных SQLite в различных потоках в то же время. Ограничение состояло в том, что то же самое соединение с базой данных не могло использоваться в двух отдельных потоках сразу. Версия 3.5.0 SQLite ослабляет это ограничение.

Чтобы позволить многократным потокам использовать то же самое соединение с базой данных в то же время, SQLite должен сделать широкое применение mutexes. И поэтому новая mutex подсистема добавлена:

sqlite3_mutex *sqlite3_mutex_alloc(int);
void sqlite3_mutex_free(sqlite3_mutex*);
void sqlite3_mutex_enter(sqlite3_mutex*);
int sqlite3_mutex_try(sqlite3_mutex*);
void sqlite3_mutex_leave(sqlite3_mutex*);

Хотя этот установленный порядок существует для использования ядром SQLite, код приложения может его использовать также при желании. mutex это объект sqlite3_mutex. sqlite3_mutex_alloc() ассигнует новый объект mutex и возвращает указатель на него. Аргументом sqlite3_mutex_alloc() должен быть SQLITE_MUTEX_FAST или SQLITE_MUTEX_RECURSIVE для нерекурсивного и рекурсивного mutexes, соответственно. Если основная система не обеспечивает нерекурсивный mutexes, то рекурсивным mutex можно заменить в этом случае. Аргумент sqlite3_mutex_alloc() может также быть постоянным обозначением одного из нескольких статических mutexes:

Эти статические mutexes резервируются для использования внутренне SQLite и не должны использоваться приложением. Статические mutexes все нерекурсивные.

sqlite3_mutex_free() должен использоваться, чтобы освободить нестатический mutex. Если статический mutex передается этой функции, поведение не определено.

sqlite3_mutex_enter() пытается войти в mutex и блокирует, если другой поток уже занял. sqlite3_mutex_try() пытается войти и возвращает SQLITE_OK при успехе или SQLITE_BUSY иначе. sqlite3_mutex_leave() выходит из mutex. mutex проводится, пока количество выходов не соответствует количеству входов. Если sqlite3_mutex_leave() вызывают на mutex, который в настоящее время не держит поток, то поведение не определено. Если функцию вызывают для освобожденного mutex, то поведение не определено.

Исходный код SQLite обеспечивает многократные внедрения API, подходящие для переменной окружающей среды. Если SQLite собран с флагом SQLITE_THREADSAFE=0, тогда обеспечивается внедрение mutex, которое быстро, но не делает никакого реального взаимного исключения. То внедрение подходит для использования в однопоточных системах. Другие реальные внедрения mutex обеспечиваются на основе основной операционной системы.

Встраиваемые приложения могут хотеть обеспечить свое собственное mutex-внедрение. Если SQLite собран с флагом времени компиляции -DSQLITE_MUTEX_APPDEF=1, ядро SQLite не обеспечивает mutex-подсистемы, и mutex-подсистема, которая соответствует интерфейсу, описанному выше, должна быть обеспечена приложением, которое связывается с SQLite.

5.0. Другие интерфейсные изменения

Version 3.5.0 SQLite изменяет поведение некоторых API способами, которые технически несовместимы. Однако они, API редко используется и даже когда они используются, трудно вообразить сценарий, где изменение могло бы сломать что-то. Изменения на самом деле заставляют их взаимодействовать намного лучше.

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

До версии 3.5.0 sqlite3_soft_heap_limit() устанавливал верхнюю границу использования памяти кучи для всех соединений с базой данных в единственном потоке. У каждого потока мог быть свой собственный предел кучи. Начиная с версии 3.5.0, есть единственный предел кучи для всего процесса. Это кажется более строгим (один предел в противоположность многим), но на практике это то, что хочет большинство пользователей.

До версии 3.5.0 sqlite3_release_memory() попыталась бы освободить память от всех соединений с базой данных в том же самом потоке, где вызвана sqlite3_release_memory(). Начиная с версии 3.5.0, sqlite3_release_memory() попытается восстановить память от всех соединений с базой данных во всех потоках.

6.0. Резюме

Переход от версии 3.4.2 SQLite до 3.5.0 является существенным изменением. Каждый файл исходного кода в ядре SQLite должен был быть изменен, некоторые значительно. Изменение ввело некоторые незначительные несовместимости в интерфейсе C. Но мы чувствуем, что выгода перехода от 3.4.2 до 3.5.0 далеко перевешивает проблемы переноса. Новый слой VFS теперь четко определен и стабилен и должен упростить будущие настройки. Слой VFS, отдельный распределитель памяти и mutex подсистемы позволяют стандартному объединению исходного кода SQLite использоваться во вложенном проекте без изменения, значительно упрощая управление конфигурацией. И получающаяся система намного более терпима к очень сложным проектам.