Spec-Zone.ru › SQLite

Загружаемые во время выполнения расширения

Содержание
1. Обзор
2. Загрузка расширения
3. Компиляция загружаемого расширения
4. Программирование загружаемых расширений
4.1. Примеры расширений
5. Постоянно загружаемые расширения
6. Статическая компоновка загружаемого во время выполнения расширения
7. Подробности реализации

1. Обзор

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

Расширения также можно статически скомпоновать с приложением. Приведённый ниже шаблон кода будет работать как со статически скомпонованным расширением, так и с загружаемым во время выполнения, однако следует дать функции точки входа («sqlite3_extension_init») другое имя, чтобы избежать конфликтов имён, если приложение содержит два или более расширений.

2. Загрузка расширения

Расширение SQLite представляет собой общую библиотеку или DLL. Чтобы загрузить его, необходимо указать SQLite имя файла, содержащего общую библиотеку или DLL, и точку входа для инициализации расширения. В коде на C эта информация передаётся с помощью API sqlite3_load_extension(). Дополнительные сведения см. в документации по этой функции.

Обратите внимание, что в разных операционных системах для общих библиотек используются разные расширения файлов. В Windows используется «.dll», на Mac — «.dylib», а в большинстве систем Unix, кроме Mac, — «.so». Если требуется, чтобы код был переносимым, можно не указывать расширение в имени файла общей библиотеки: интерфейс sqlite3_load_extension() автоматически добавит подходящее расширение.

Для загрузки расширений также можно использовать SQL-функцию load_extension(X,Y). Она работает так же, как интерфейс sqlite3_load_extension() на языке C.

Оба способа загрузки расширения позволяют указать имя его точки входа. Этот аргумент можно оставить пустым — передать указатель NULL интерфейсу sqlite3_load_extension() на языке C или опустить второй аргумент интерфейса load_extension() на языке SQL. В этом случае логика загрузчика расширений попытается определить точку входа самостоятельно. Сначала она попробует использовать общее имя расширения «sqlite3_extension_init». Если это не сработает, она сформирует имя точки входа по шаблону «sqlite3_X_init», заменив X символами ASCII в нижнем регистре из имени файла между последним «/» и первой следующей за ним точкой «.». Если первые три символа — «lib», они пропускаются. Например, если имя файла — «/usr/lib/libmathfunc-4.8.so», имя точки входа будет «sqlite3_mathfunc_init». Если же имя файла — «./SpellFixExt.dll», точка входа будет называться «sqlite3_spellfixext_init».

В целях безопасности загрузка расширений по умолчанию отключена. Чтобы использовать функции загрузки расширений на языке C или SQL, сначала необходимо включить загрузку расширений в приложении с помощью API sqlite3_db_config(db,SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION,1,NULL) на языке C.

В оболочке командной строки расширения можно загружать с помощью команды «.load». Например:

.load ./YourCode

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

Команда «.load» с одним аргументом вызывает sqlite3_load_extension() с параметром zProc, равным NULL. Это заставляет SQLite сначала искать точку входа с именем «sqlite3_extension_init», а затем «sqlite3_X_init», где «X» определяется по имени файла. Если точка входа вашего расширения имеет другое имя, укажите его вторым аргументом. Например:

.load ./YourCode nonstandard_entry_point

3. Компиляция загружаемого расширения

Загружаемые расширения пишутся на C. Для их компиляции в большинстве Unix-подобных операционных систем обычно используется команда примерно такого вида:

gcc -g -fPIC -shared YourCode.c -o YourCode.so

Mac относится к Unix-подобным системам, но не следует общепринятым соглашениям об общих библиотеках. Для компиляции общей библиотеки на Mac используйте команду примерно такого вида:

gcc -g -fPIC -dynamiclib YourCode.c -o YourCode.dylib

Если при попытке загрузить библиотеку появляется сообщение об ошибке «mach-o, but wrong architecture», возможно, потребуется добавить параметры командной строки «-arch i386» или «arch x86_64» для gcc — в зависимости от того, как собрано ваше приложение.

Для компиляции в Windows с помощью MSVC обычно подходит команда, подобная следующей:

cl YourCode.c -link -dll -out:YourCode.dll

Для компиляции в Windows с помощью MinGW командная строка такая же, как для Unix, за исключением того, что расширение выходного файла меняется на «.dll», а аргумент -fPIC опускается:

gcc -g -shared YourCode.c -o YourCode.dll

4. Программирование загружаемых расширений

Шаблон загружаемого расширения содержит следующие три элемента:

  1. Используйте «#include <sqlite3ext.h>» в начале файлов исходного кода вместо «#include <sqlite3.h>».

  2. Поместите макрос «SQLITE_EXTENSION_INIT1» на отдельной строке сразу после строки «#include <sqlite3ext.h>».

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

    #ifdef _WIN32
    __declspec(dllexport)
    #endif
    int sqlite3_extension_init( /* <== Change this name, maybe */
      sqlite3 *db, 
      char **pzErrMsg, 
      const sqlite3_api_routines *pApi
    ){
      int rc = SQLITE_OK;
      SQLITE_EXTENSION_INIT2(pApi);
      /* insert code to initialize your extension here */
      return rc;
    }
    

    Лучше настроить имя точки входа так, чтобы оно соответствовало имени создаваемой общей библиотеки, а не использовать общее имя «sqlite3_extension_init». Пользовательское имя точки входа позволит статически скомпоновать два или более расширения в одной программе без конфликтов компоновщика, если впоследствии вы решите использовать статическую, а не динамическую компоновку. Если ваша общая библиотека будет называться «YourCode.so», «YourCode.dll» или «YourCode.dylib», как показано в примерах команд компиляции выше, правильным именем точки входа будет «sqlite3_yourcode_init».

Ниже приведён полный шаблон расширения, который можно скопировать и использовать в качестве отправной точки:

/* Add your header comment here */
#include <sqlite3ext.h> /* Do not use <sqlite3.h>! */
SQLITE_EXTENSION_INIT1

/* Insert your extension code here */

#ifdef _WIN32
__declspec(dllexport)
#endif
/* TODO: Change the entry point name so that "extension" is replaced by
** text derived from the shared library filename as follows:  Copy every
** ASCII alphabetic character from the filename after the last "/" through
** the next following ".", converting each character to lowercase, and
** discarding the first three characters if they are "lib".
*/
int sqlite3_extension_init(
  sqlite3 *db,
  char **pzErrMsg,
  const sqlite3_api_routines *pApi
){
  int rc = SQLITE_OK;
  SQLITE_EXTENSION_INIT2(pApi);
  /* Insert here calls to
  **     sqlite3_create_function_v2(),
  **     sqlite3_create_collation_v2(),
  **     sqlite3_create_module_v2(), and/or
  **     sqlite3_vfs_register()
  ** to register the new features that your extension adds.
  */
  return rc;
}

4.1. Примеры расширений

Множество примеров полноценных и работающих загружаемых расширений можно найти в исходном дереве SQLite, в подкаталоге ext/misc. Каждый файл в этом каталоге представляет собой отдельное расширение. Документация приведена в виде комментария в начале файла. Ниже кратко описаны некоторые расширения из подкаталога ext/misc:

  • carray.c — реализация табличной функции carray.

  • compress.c — реализация определяемых приложением SQL-функций compress() и uncompress(), выполняющих сжатие текста или двоичных данных с помощью zLib.

  • rot13.c — реализация SQL-функции rot13(). Это очень простой пример функции расширения, который удобно использовать в качестве шаблона для создания новых расширений.

  • series.c — реализация виртуальной таблицы generate_series и табличной функции. Это относительно простой пример реализации виртуальной таблицы, который можно использовать в качестве шаблона для создания новых виртуальных таблиц.

Другие, более сложные расширения можно найти во вложенных каталогах ext/, кроме ext/misc/.

5. Постоянно загружаемые расширения

По умолчанию загружаемое расширение выгружается из памяти процесса, когда закрывается соединение с базой данных, вызвавшее sqlite3_load_extension(). (Иными словами, при закрытии соединения с базой данных для всех расширений вызывается метод xDlClose объекта sqlite3_vfs.) Однако, если процедура инициализации возвращает SQLITE_OK_LOAD_PERMANENTLY вместо SQLITE_OK, расширение не выгружается (xDlClose не вызывается) и остаётся в памяти процесса неограниченно долго. Возвращаемое значение SQLITE_OK_LOAD_PERMANENTLY полезно для расширений, которым нужно регистрировать новые VFS.

Уточним: расширение, функция инициализации которого возвращает SQLITE_OK_LOAD_PERMANENTLY, остаётся в памяти после закрытия соединения с базой данных. Однако оно не регистрируется автоматически для последующих соединений с базой данных. Благодаря этому можно загружать расширения, реализующие новые VFS. Чтобы постоянно загружать и регистрировать расширение, реализующее новые SQL-функции, последовательности сортировки и/или виртуальные таблицы, так чтобы эти возможности были доступны всем последующим соединениям с базой данных, процедура инициализации должна также вызвать sqlite3_auto_extension() для подфункции, которая зарегистрирует эти возможности.

Расширение vfsstat.c демонстрирует постоянно регистрируемое загружаемое расширение, которое добавляет новый VFS и новую виртуальную таблицу. Процедура инициализации sqlite3_vfsstat_init() в этом расширении вызывается только один раз — при первой загрузке расширения. Она однократно регистрирует новый VFS «vfslog» и возвращает SQLITE_OK_LOAD_PERMANENTLY, чтобы код реализации VFS «vfslog» оставался в памяти. Процедура инициализации также вызывает sqlite3_auto_extension(), передавая указатель на функцию «vstatRegister()». Благодаря этому при запуске все последующие соединения с базой данных будут вызывать функцию «vstatRegister()» и, следовательно, регистрировать виртуальную таблицу «vfsstat».

6. Статическая компоновка загружаемого во время выполнения расширения

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

Чтобы статически скомпоновать расширение, достаточно добавить параметр компиляции -DSQLITE_CORE. Макрос SQLITE_CORE превращает макросы SQLITE_EXTENSION_INIT1 и SQLITE_EXTENSION_INIT2 в пустые операции. Затем измените приложение, чтобы оно вызывало точку входа напрямую, передавая указатель NULL в качестве третьего параметра «pApi».

Если вы собираетесь статически компоновать два или более расширений, особенно важно использовать имя точки входа, основанное на имени файла расширения, а не общее имя точки входа «sqlite3_extension_init». При использовании общего имени возникнет несколько определений одного и того же символа, и компоновка завершится неудачей.

Если приложение будет открывать несколько соединений с базой данных, вместо того чтобы отдельно вызывать точки входа расширений для каждого соединения, рассмотрите возможность использования интерфейса sqlite3_auto_extension() для регистрации расширений и их автоматического запуска при открытии каждого соединения с базой данных. Каждое расширение достаточно зарегистрировать один раз — это можно сделать в начале функции main(). Регистрация расширений с помощью интерфейса sqlite3_auto_extension() позволяет им работать так, как если бы они были встроены в ядро SQLite: они автоматически становятся доступными при открытии нового соединения с базой данных и не требуют инициализации. Только не забудьте выполнить необходимую настройку с помощью sqlite3_config() до регистрации расширений, поскольку интерфейс sqlite3_auto_extension() неявно вызывает sqlite3_initialize().

7. Подробности реализации

SQLite реализует загрузку расширений во время выполнения с помощью методов xDlOpen(), xDlError(), xDlSym() и xDlClose() объекта sqlite3_vfs. В Unix эти методы реализованы с помощью библиотеки dlopen() (поэтому SQLite в системах Unix часто требуется компоновка с библиотекой «-ldl»), а в Windows — с помощью API LoadLibrary(). В пользовательской VFS для нестандартных систем эти методы можно опустить; в таком случае механизм загрузки расширений во время выполнения работать не будет (однако код расширения всё равно можно будет скомпоновать статически, если имена точек входа уникальны). SQLite можно скомпилировать с параметром SQLITE_OMIT_LOAD_EXTENSION, чтобы исключить из сборки код загрузки расширений.

Последнее обновление этой страницы: 2025-11-13 07:12:58Z

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

Spec-Zone.ru

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