279 Загружаемые расширения во время выполнения
RussianLDP Рейтинг@Mail.ru
WebMoney: 
WMZ Z294115950220 
WMR R409981405661 
WME E134003968233 
Visa 
4274 3200 2453 6495 

Small. Fast. Reliable.
Choose any three.
Загружаемые расширения во время выполнения

1. Обзор

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

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

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

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

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

Есть также функция SQL, которая может использоваться, чтобы загрузить расширения: load_extension(X,Y) . Это работает точно так же, как sqlite3_load_extension().

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

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

Из оболочки командной строки расширения могут быть загружены, используя команду ".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. Пример расширения/h2>

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

Другие и более сложные расширения могут быть найдены в подпапках под каталогом 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 так, чтобы код , используемый, чтобы осуществить "vfslog" VFS, остался в памяти. Установленный порядок инициализации также вызывает 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. Эти методы осуществляются, используя библиотеку dlopen() в unix (это объясняет, почему SQLite обычно должен связываться с библиотекой "-ldl" в unix) или LoadLibrary() API в Windows. В своей VFS для необычных систем эти методы могут все быть опущены, в этом случае механизм загрузки расширения во время выполнения не будет работать (хотя вы все еще будете в состоянии статически связать дополнительный код, предполагая, что указатели входа имеют уникальные имена). SQLite может быть собран с SQLITE_OMIT_LOAD_EXTENSION, чтобы опустить код загрузки расширения.