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

Small. Fast. Reliable.
Choose any three.
Виртуальный механизм таблицы SQLite
Оглавление

1. Введение

Виртуальная таблица это объект, который зарегистрирован в открытом соединении с базой данных SQLite. С точки зрения SQL-оператора виртуальный объект таблицы похож на любую другую таблицу или представление. Но негласно, запросы и обновления на виртуальной таблице зывают методы отзыва виртуального объекта таблицы вместо того, чтобы читать и писать файл базы данных.

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

  • Нельзя создать триггер на виртуальной таблице.
  • Нельзя создать дополнительные индексы на виртуальной таблице (у виртуальных таблиц могут быть индексы, но это должно быть встроено в виртуальное внедрение таблицы. Индексы не могут быть добавлены отдельно, используя CREATE INDEX).
  • Нельзя управлять ALTER TABLE ... ADD COLUMN против виртуальной таблицы.

Отдельные виртуальные внедрения таблицы могли бы наложить дополнительные ограничения. Например, некоторые виртуальные внедрения могли бы обеспечить таблицы только для чтения. Или некоторые виртуальные внедрения таблицы могли бы позволить INSERT или DELETE, но не UPDATE. Или некоторые виртуальные внедрения таблицы могли бы ограничить виды UPDATE, которые могут быть сделаны.

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

Вот некоторое существующее и постулируемое использование для виртуальных таблиц:

  • Полнотекстовый поиск
  • Пространственные индексы, используя R-Tree
  • Анализируйте дисковое содержание файла базы данных SQLite (виртуальная таблица dbstat)
  • Прочитайте и/или напишите содержание файла отделенных запятыми значений (CSV)
  • Получите доступ к файловой системе компьютера, как будто это таблица базы данных
  • Предоставление возможности манипуляции SQL данными в пакетах статистики

См. здесь для более длинного списка фактических виртуальных внедрений таблицы.

1.1. Применение

Виртуальная таблица составлена, используя CREATE VIRTUAL TABLE.

create-virtual-table-stmt:

CREATE VIRTUAL TABLE IF NOT EXISTS schema-name . table-name USING module-name ( module-argument ) ,

CREATE VIRTUAL TABLE составляет новую таблицу, названную table-name , полученную из module-name. Здесь module-name это имя, которое зарегистрировано для виртуальной таблицы через sqlite3_create_module().

CREATE VIRTUAL TABLE tablename USING modulename;

Можно также обеспечить отделенные запятой аргументы модулю после имени модуля:

CREATE VIRTUAL TABLE tablename USING modulename(arg1, arg2, ...);

Формат аргументов модуля очень общий. Каждый module-argument может содержать ключевые слова, строковые литералы, идентификаторы, числа и пунктуацию. Каждый module-argument передан как написано (как текст) в метод constructor виртуального внедрения таблицы, когда виртуальная таблица составлена, и тот конструктор ответственен за парсинг и интерпретацию аргументов. Синтаксис аргумента достаточно общий, так что виртуальное внедрение таблицы, если это хочет, может интерпретировать свои аргументы как определения столбца в обычном запросе CREATE TABLE . Внедрение могло также наложить некоторую другую интерпретацию на аргументы.

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

1.1.1. Временные виртуальные таблицы

Нет команды "CREATE TEMP VIRTUAL TABLE". Чтобы составить временную виртуальную таблицу, добавьте схему "temp" перед именем таблицы.

CREATE VIRTUAL TABLE temp.tablename USING module(arg1, ...);

1.1.2. Одноименные виртуальные таблицы

Некоторые виртуальные таблицы существуют автоматически в схеме "main" каждого соединения с базой данных, в котором их модуль зарегистрирован, даже без CREATE VIRTUAL TABLE. Такие виртуальные таблицы называют "одноименными виртуальными таблицами". Чтобы использовать одноименную виртуальную таблицу, просто используйте имя модуля, как будто это была таблица. Одноименные виртуальные таблицы существуют только в схеме "main", таким образом, они не будут работать, если указано другое имя схемы.

Пример одноименной виртуальной таблицы это таблица dbstat. Чтобы использовать dbstat в качестве одноименной виртуальной таблицы, просто запросите имя модуля "dbstat", как будто это была обычная таблица (обратите внимание на то, что SQLite должен быть собран с выбором SQLITE_ENABLE_DBSTAT_VTAB, чтобы включать dbstat).

SELECT * FROM dbstat;

Виртуальная таблица одноименна, если метод xCreate это та же самая функция, как метод xConnect или если метод xCreate = NULL. Метод xCreate вызывают, когда виртуальная таблица сначала составлена, используя CREATE VIRTUAL TABLE. Метод xConnect вызван каждый раз, когда соединение с базой данных создано или повторно разбирают схему. Когда эти два метода то же самое, это указывает, что у виртуальной таблицы нет постоянного состояния, которое должно быть создано и разрушено.

1.1.3. Одноименно-единственные виртуальные таблицы

Если метод xCreate = NULL, CREATE VIRTUAL TABLE запрещаются для той виртуальной таблицы и виртуальные таблицы являются "одноименно-единственными". Одноименно-единственные виртуальные таблицы полезны как табличные функции.

Обратите внимание на то, что до version 3.9.0 (2015-10-14) SQLite не проверял xCreate метод на NULL прежде, чем вызвать его. Таким образом, если одноименно-единственная виртуальная таблица зарегистрирована в SQLite version 3.8.11.1 (2015-07-29) или ранее и CREATE VIRTUAL TABLE предпринята для того виртуального модуля таблицы, произойдет переход на NULL, приводя к сбою.

1.2. Реализация

Несколько новых объектов C-уровня используются виртуальным внедрением таблицы:

typedef struct sqlite3_vtab sqlite3_vtab;
typedef struct sqlite3_index_info sqlite3_index_info;
typedef struct sqlite3_vtab_cursor sqlite3_vtab_cursor;
typedef struct sqlite3_module sqlite3_module;

Структура sqlite3_module определяет объект модуля, используемый, чтобы осуществить виртуальную таблицу. Думайте о модуле как о классе, из которого можно построить многократные виртуальные таблицы, имеющие подобные свойства. Например, можно было бы иметь модуль, который обеспечивает доступ только для чтения к файлам comma-separated-value (CSV). Тот модуль может тогда использоваться, чтобы составить несколько виртуальных таблиц, где каждая виртуальная таблица обращается к различному файлу CSV.

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

Каждый виртуальный экземпляр таблицы представляется структурой sqlite3_vtab. Структура sqlite3_vtab похожа на это:

struct sqlite3_vtab {
  const sqlite3_module *pModule;
  int nRef;
  char *zErrMsg;
};

Виртуальные внедрения таблицы будут обычно подклассифицировать эту структуру, чтобы добавить дополнительные частные и определенные для внедрения области. nRef используется внутренне ядром SQLite и не должна быть изменена виртуальным внедрением таблицы. Виртуальное внедрение таблицы может передать текст сообщения об ошибке ядру, поместив последовательность сообщения об ошибке в zErrMsg. Пространство, чтобы содержать эту последовательность сообщения об ошибке, должно быть получено из функции выделения памяти SQLite, такой как sqlite3_mprintf() или sqlite3_malloc(). До назначения нового значения zErrMsg виртуальное внедрение таблицы должно освободить любое существующее ранее содержание zErrMsg, используя sqlite3_free(). Отказ сделать это приведет к утечке памяти. Ядро SQLite освободит и обнулит содержание zErrMsg, когда это поставит текст сообщения об ошибке клиентскому приложению или когда это разрушит таблицу. Виртуальное внедрение таблицы должно волноваться об освобождении содержания zErrMsg, только когда это переписывает содержание новым сообщением об ошибке.

Структура sqlite3_vtab_cursor представляет указатель на определенную строку таблицы. Это то, на что похожа sqlite3_vtab_cursor:

struct sqlite3_vtab_cursor {
  sqlite3_vtab *pVtab;
};

Еще раз, практические внедрения, вероятно, подклассифицируют эту структуру, чтобы добавить дополнительные частные области.

sqlite3_index_info используется, чтобы передать информацию в и из метода xBestIndex модуля, который осуществляет таблицу.

Перед выполнением CREATE VIRTUAL TABLE модуль, определенный в том запросе, должен быть зарегистрирован в соединении с базой данных. Это достигается, используя любой из sqlite3_create_module() или sqlite3_create_module_v2():

int sqlite3_create_module(
  sqlite3 *db,               /* SQLite connection to register module with */
  const char *zName,         /* Name of the module */
  const sqlite3_module *,    /* Methods for the module */
  void *                     /* Client data for xCreate/xConnect */
);

int sqlite3_create_module_v2(
  sqlite3 *db,               /* SQLite connection to register module with */
  const char *zName,         /* Name of the module */
  const sqlite3_module *,    /* Methods for the module */
  void *,                    /* Client data for xCreate/xConnect */
  void(*xDestroy)(void*)     /* Client data destructor function */
);

sqlite3_create_module() и sqlite3_create_module_v2() связывает имя модуля со структурой sqlite3_module и отдельными данными клиента, которые являются определенными для каждого модуля. Единственная разница между двумя методами create_module в том, что _v2-метод включает дополнительный параметр, который определяет деструктор для указателя данных клиента. Структура модуля определяет поведение виртуальной таблицы. Структура модуля похожа на это:

struct sqlite3_module {
  int iVersion;
  int (*xCreate)(sqlite3*, void *pAux, int argc, char *const*argv,
               sqlite3_vtab **ppVTab, char **pzErr);
  int (*xConnect)(sqlite3*, void *pAux, int argc, char *const*argv,
               sqlite3_vtab **ppVTab, char **pzErr);
  int (*xBestIndex)(sqlite3_vtab *pVTab, sqlite3_index_info*);
  int (*xDisconnect)(sqlite3_vtab *pVTab);
  int (*xDestroy)(sqlite3_vtab *pVTab);
  int (*xOpen)(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor);
  int (*xClose)(sqlite3_vtab_cursor*);
  int (*xFilter)(sqlite3_vtab_cursor*, int idxNum, const char *idxStr,
                int argc, sqlite3_value **argv);
  int (*xNext)(sqlite3_vtab_cursor*);
  int (*xEof)(sqlite3_vtab_cursor*);
  int (*xColumn)(sqlite3_vtab_cursor*, sqlite3_context*, int);
  int (*xRowid)(sqlite3_vtab_cursor*, sqlite_int64 *pRowid);
  int (*xUpdate)(sqlite3_vtab *, int, sqlite3_value **, sqlite_int64 *);
  int (*xBegin)(sqlite3_vtab *pVTab);
  int (*xSync)(sqlite3_vtab *pVTab);
  int (*xCommit)(sqlite3_vtab *pVTab);
  int (*xRollback)(sqlite3_vtab *pVTab);
  int (*xFindFunction)(sqlite3_vtab *pVtab, int nArg, const char *zName,
                     void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
                     void **ppArg);
  int (*xRename)(sqlite3_vtab *pVtab, const char *zNew);
  /* The methods above are in version 1 of the sqlite_module object. Those
  ** below are for version 2 and greater. */
  int (*xSavepoint)(sqlite3_vtab *pVTab, int);
  int (*xRelease)(sqlite3_vtab *pVTab, int);
  int (*xRollbackTo)(sqlite3_vtab *pVTab, int);
  /* The methods above are in versions 1 and 2 of the sqlite_module object.
  ** Those below are for version 3 and greater. */
  int (*xShadowName)(const char*);
  /* The methods above are in versions 1 through 3 of the sqlite_module object.
  ** Those below are for version 4 and greater. */
  int (*xIntegrity)(sqlite3_vtab *pVTab, const char *zSchema,
                    const char *zTabName, int mFlags, char **pzErr);
};

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

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

1.3. Виртуальные таблицы и общий кэш

До SQLite version 3.6.17 (2009-08-10) виртуальный механизм таблицы предполагает, что каждое соединение с базой данных содержит свою собственную копию схемы базы данных. Следовательно, виртуальный механизм таблицы не мог использоваться в базе данных, которая использует режим общего кэширования. sqlite3_create_module() возвратил бы ошибку, если режим общего кэширования включен. Это ограничение было ослаблено в SQLite version 3.6.17.

1.4. Создание новых виртуальных внедрений таблицы

Выполните эти шаги, чтобы составить вашу собственную виртуальную таблицу:

  1. Напишите все необходимые методы.
  2. Создайте экземпляр структуры sqlite3_module, содержащий указатели на все методы из шага 1.
  3. Зарегистрируйте свою структуру sqlite3_module через sqlite3_create_module() или sqlite3_create_module_v2().
  4. Выполните CREATE VIRTUAL TABLE, которая определяет новый модуль в пункте USING.

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

  • templatevtab.c → виртуальная таблица, составленная определенно, чтобы служить шаблоном для других виртуальных таблиц.
  • series.c → Внедрение табличной функции generate_series().
  • json.c → Содержит исходные тексты табличных функций json_each() и json_tree().
  • csv.c → Виртуальная таблица, которая читает файлы CSV.

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

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

2. Виртуальные методы таблицы

2.1. Метод xCreate

int (*xCreate)(sqlite3 *db, void *pAux, int argc, char *const*argv,
               sqlite3_vtab **ppVTab, char **pzErr);

Метод xCreate вызывают, чтобы создать новый экземпляр виртуальной таблицы в ответ на CREATE VIRTUAL TABLE. Если метод xCreate тот же самый указатель, как метод xConnect, виртуальная таблица это одноименная виртуальная таблица. Если метод xCreate опущен (если это NULL), виртуальная таблица это одноименно-единственная виртуальная таблица.

db это указатель на SQLite соединение с базой данных, которое выполняет CREATE VIRTUAL TABLE. pAux это копия указателя данных клиента, который был четвертым аргументом sqlite3_create_module() или sqlite3_create_module_v2(), который зарегистрировал виртуальный модуль таблицы. argv это множество argc указателей на законченные null последовательности. Первая последовательность, argv[0], является названием вызываемого модуля. Имя модуля это имя, обеспеченное как второй аргумент sqlite3_create_module() и как аргумент пункта USING в CREATE VIRTUAL TABLE. Второй, argv[1], название базы данных, в которой составляется новая виртуальная таблица. Имя базы данных "main" для основной базы данных, "temp" для БД TEMP или имя, данное в команде ATTACH. Третий элемент множества, argv[2], является названием новой виртуальной таблицы, как определено после ключевого слова TABLE в CREATE VIRTUAL TABLE. Если существуют, четвертая и последующие последовательности в множестве argv[] сообщают об аргументах модуля в CREATE VIRTUAL TABLE.

Работа по этому методу состоит в том, чтобы построить новый виртуальный объект таблицы (объект sqlite3_vtab) и возвратить указатель на него в *ppVTab.

Как часть задачи создания новой структуры sqlite3_vtab, этот метод ДОЛЖЕН вызвать sqlite3_declare_vtab(), чтобы сказать ядру SQLite о колонках и типах данных в виртуальной таблице. sqlite3_declare_vtab() API имеет следующий прототип:

int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable)

Первым аргументом sqlite3_declare_vtab() должен быть тот же самый указатель соединения с базой данных как первый параметр к этому методу. Второй аргумент sqlite3_declare_vtab() должен быть законченной нолем строкой UTF-8, которая содержит правильно построенный запрос CREATE TABLE, который определяет колонки в виртуальной таблице и их типы данных. Название таблицы в этом CREATE TABLE проигнорировано, как все ограничения. Только имена столбцов и типы данных важны. Строка CREATE TABLE не должна быть проведена в постоянной памяти. Последовательность может быть освобождена и/или снова использована, как только вернется sqlite3_declare_vtab().

Метод xConnect может также произвольно вызвать специальные функции для виртуальной таблицы, сделав одно или несколько обращений к sqlite3_vtab_config():

int sqlite3_vtab_config(sqlite3 *db, int op, ...);

Вызов sqlite3_vtab_config() опционален. Но для максимальной безопасности, рекомендуется, чтобы виртуальные внедрения таблицы вызвали "sqlite3_vtab_config(db, SQLITE_VTAB_DIRECTONLY)", если виртуальная таблица не будет использоваться изнутри триггеров или обзоров.

Метод xCreate не должен инициализировать поля pModule, nRef и zErrMsg объекта sqlite3_vtab. Ядро SQLite будет заботиться об этом.

xCreate должен возвратить SQLITE_OK, если это успешно в составлении новой виртуальной таблицы или SQLITE_ERROR, если это не успешно. Если не успешно, структура sqlite3_vtab не должна быть ассигнована. Сообщение об ошибке может произвольно быть возвращено в *pzErr. Место, чтобы содержать последовательность сообщения об ошибке должно быть выделено, используя функцию выделения памяти SQLite sqlite3_malloc() или sqlite3_mprintf(), поскольку ядро SQLite попытается освободить пространство, используя sqlite3_free() после того, как об ошибке сообщили.

Если метод xCreate = NULL, виртуальная таблица это одноименно-единственный виртуальная таблица . Новые экземпляры виртуальной таблицы не могут быть созданы, используя CREATE VIRTUAL TABLE и виртуальная таблица может использоваться только через имя модуля. Обратите внимание на то, что версии SQLite до 3.9.0 (2015-10-14) не понимают одноименно-единственные виртуальные таблицы, и будет segfault, если будет предпринята попытка CREATE VIRTUAL TABLE на одноименно-единственном виртуальной таблице, потому что метод xCreate не был проверен на пустой указатель.

Если метод xCreate это тот же самый указатель, как xConnect, это указывает, что виртуальная таблица не должна инициализировать запоминающее устройство. Такая виртуальная таблица может использоваться в качестве одноименной виртуальной таблицы, в качестве названной виртуальной таблицы, используя CREATE VIRTUAL TABLE, или обеих.

2.1.1. Скрытые столбцы в виртуальных таблицах

Если тип данных колонки содержит специальное ключевое слово "HIDDEN" (в какой-либо комбинации прописных и строчных букв), ключевое слово опущено от имени типа данных колонки, а колонка отмечена как скрытый столбец внутренне. Скрытый столбец отличается от нормальной колонки в трех отношениях:

  • Скрытые столбцы не перечисляются в наборе данных, возвращенном "PRAGMA table_info",
  • Скрытые столбцы не включены в расширение "*" в наборе результатов SELECT,
  • Скрытые столбцы не включены в неявный список столбцов, используемый INSERT который испытывает недостаток в явном списке столбцов.

Например, если следующий SQL передается к sqlite3_declare_vtab():

CREATE TABLE x(a HIDDEN VARCHAR(12), b INTEGER, c INTEGER Hidden);

виртуальная таблица была бы составлена с двумя скрытыми столбцами, и с типами данных "VARCHAR(12)" и "INTEGER".

Использование в качестве примера скрытых столбцов может быть замечено в виртуальном внедрении таблицы FTS3, где каждая виртуальная таблица FTS содержит скрытый столбец FTS, который используется, чтобы передать информацию от виртуальной таблицы в вспомогательные функции FTS и оператор FTS MATCH.

2.1.2. Табличные функции

Виртуальная таблица, которая содержит скрытые столбцы, может использоваться как табличная функция в пункте FROM в SELECT. Аргументы табличной функции становятся ограничениями на скрытые столбцы виртуальной таблицы.

Например, расширение "generate_series" (находится в файле ext/misc/series.c в исходном дереве) реализует одноименную виртуальную таблицу со следующей схемой:

CREATE TABLE generate_series(value, start HIDDEN, stop HIDDEN, step HIDDEN);

Метод sqlite3_module.xBestIndex во внедрении этой таблицы проверяет на ограничения равенства против скрытых столбцов и использует те как входные параметры, чтобы определить диапазон вывода integer "value", чтобы произвести. Разумные умолчания используются для любых неограниченных столбцов. Например, чтобы перечислить все целые числа между 5 и 50:

SELECT value FROM generate_series(5,50);

Предыдущий запрос эквивалентен следующему:

SELECT value FROM generate_series WHERE start=5 AND stop=50;

Аргументы на виртуальном имени таблицы соответствуют скрытым столбцам. Количество аргументов может быть меньше, чем количество скрытых столбцов, в этом случае последние скрытые столбцы не ограничены. Однако, будет ошибка, если есть больше аргументов, чем в виртуальной таблице есть скрытых столбцов.

2.1.3. Виртуальные таблицы WITHOUT ROWID

С SQLite version 3.14.0 (2016-08-08) CREATE TABLE, который передается в sqlite3_declare_vtab(), может содержать WITHOUT ROWID. Это полезно для случаев, где виртуальные строки таблицы не могут легко быть отображены в уникальные целые числа. CREATE TABLE, который включает WITHOUT ROWID, должен определить одну или более колонок как PRIMARY KEY. Каждая колонка PRIMARY KEY должна индивидуально быть NOT NULL, и все колонки для каждой строки должны быть коллективно уникальными.

Обратите внимание на то, что SQLite не проводит в жизнь PRIMARY KEY для виртуальной таблицы WITHOUT ROWID. Осуществление это ответственность основного виртуального внедрения таблицы. Но SQLite предполагает, что ограничение PRIMARY KEY действительно, что определенные колонки действительно UNIQUE и NOT NULL и использует это предположение, чтобы оптимизировать запросы для виртуальной таблицы.

rowid недоступна на виртуальной таблице WITHOUT ROWID (конечно).

Метод xUpdate был первоначально разработан вокруг наличия ROWID как единственное значение. Метод xUpdate был расширен, чтобы приспособить произвольный PRIMARY KEY вместо ROWID, но PRIMARY KEY должен все еще быть только одной колонкой. Поэтому SQLite отклонит любую таблицу WITHOUT ROWID, у которой есть больше, чем одна колонка PRIMARY KEY и не-NULL метод xUpdate.

2.2. Метод xConnect

int (*xConnect)(sqlite3*, void *pAux, int argc, char *const*argv,
                sqlite3_vtab **ppVTab, char **pzErr);

Метод xConnect очень похож на xCreate. Он имеет те же самые параметры и строит новую структуру sqlite3_vtab подобно xCreate. И это должно также вызвать sqlite3_declare_vtab(), как xCreate. Это должно также сделать все те же самые вызовы sqlite3_vtab_config(), как xCreate.

Различие в том, что xConnect вызывают, чтобы установить новую связь с существующей виртуальной таблицей, тогда как xCreate, чтобы составить новую виртуальную таблицу с нуля.

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

Как пример, рассмотрите виртуальное внедрение таблицы, которое обеспечивает доступ только для чтения к существующим файлам comma-separated-value (CSV) на диске. Нет никакого запоминающего устройства, которое должно быть создано или инициализировано для такого виртуальной таблицы (так как файлы CSV уже существуют на диске), так что xCreate и xConnect будут идентичны для этого модуля.

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

xConnect должен вернуть SQLITE_OK, если это успешно в составлении новой виртуальной таблицы, или SQLITE_ERROR иначе. Если неуспешно, структура sqlite3_vtab не должна быть ассигнована. Сообщение об ошибке может произвольно быть возвращено в *pzErr, если была неудача. Место, чтобы содержать последовательность сообщения об ошибке должно быть выделено, используя функцию выделения памяти SQLite sqlite3_malloc() или sqlite3_mprintf(), поскольку ядро SQLite попытается освободить пространство, используя sqlite3_free() после того, как об ошибке сообщили.

xConnect требуется для каждого виртуального внедрения таблицы, хотя указатели xCreate и xConnect объекта sqlite3_module могут указать на ту же самую функцию, если виртуальная таблица не должна инициализировать запоминающее устройство.

2.3. Метод xBestIndex

SQLite применяет xBestIndex виртуального модуля таблицы, чтобы определить лучший способ получить доступ к виртуальной таблице. У метода xBestIndex есть такой прототип:

int (*xBestIndex)(sqlite3_vtab *pVTab, sqlite3_index_info*);

Ядро SQLite общается с xBestIndex, заполняя определенные области структуры sqlite3_index_info и передавая указатель на ту структуру в xBestIndex как второй параметр. Метод xBestIndex заполняет другие области этой структуры, которая формирует ответ. Структура sqlite3_index_info похожа на это:

struct sqlite3_index_info {
  /* Inputs */
  const int nConstraint;     /* Number of entries in aConstraint */
  const struct sqlite3_index_constraint {
     int iColumn;              /* Column constrained.  -1 for ROWID */
     unsigned char op;         /* Constraint operator */
     unsigned char usable;     /* True if this constraint is usable */
     int iTermOffset;          /* Used internally - xBestIndex should ignore */
  } *const aConstraint;      /* Table of WHERE clause constraints */
  const int nOrderBy;        /* Number of terms in the ORDER BY clause */
  const struct sqlite3_index_orderby {
     int iColumn;              /* Column number */
     unsigned char desc;       /* True for DESC.  False for ASC. */
  } *const aOrderBy;         /* The ORDER BY clause */

  /* Outputs */
  struct sqlite3_index_constraint_usage {
    int argvIndex;           /* if >0, constraint is part of argv to xFilter */
    unsigned char omit;      /* Do not code a test for this constraint */
  } *const aConstraintUsage;
  int idxNum;                /* Number used to identify the index */
  char *idxStr;              /* String, possibly obtained from sqlite3_malloc */
  int needToFreeIdxStr;      /* Free idxStr using sqlite3_free() if true */
  int orderByConsumed;       /* True if output is already ordered */
  double estimatedCost;      /* Estimated cost of using this index */
  /* Fields below are only available in SQLite 3.8.2 and later */
  sqlite3_int64 estimatedRows;    /* Estimated number of rows returned */
  /* Fields below are only available in SQLite 3.9.0 and later */
  int idxFlags;              /* Mask of SQLITE_INDEX_SCAN_* flags */
  /* Fields below are only available in SQLite 3.10.0 and later */
  sqlite3_uint64 colUsed;    /* Input: Mask of columns used by statement */
};

Отметьте предупреждения на "estimatedRows", "idxFlags" и colUsed. Эти области были добавлены с версий 3.8.2, 3.9.0 и 3.10.0, соответственно. Любое расширение, которое читает или пишет эти области, должно сначала проверить, что версия библиотеки SQLite в использовании больше или равна соответствующей версии, возможно, сравнение значения, возвращенные из sqlite3_libversion_number(), с константами 3008002, 3009000 и/или 3010000. Результат попытки получить доступ к этим областям в структуре sqlite3_index_info, созданной более старой версией SQLite, не определен.

Кроме того, есть некоторые определенные константы:

#define SQLITE_INDEX_CONSTRAINT_EQ         2
#define SQLITE_INDEX_CONSTRAINT_GT         4
#define SQLITE_INDEX_CONSTRAINT_LE         8
#define SQLITE_INDEX_CONSTRAINT_LT        16
#define SQLITE_INDEX_CONSTRAINT_GE        32
#define SQLITE_INDEX_CONSTRAINT_MATCH     64
#define SQLITE_INDEX_CONSTRAINT_LIKE      65  /* 3.10.0 and later */
#define SQLITE_INDEX_CONSTRAINT_GLOB      66  /* 3.10.0 and later */
#define SQLITE_INDEX_CONSTRAINT_REGEXP    67  /* 3.10.0 and later */
#define SQLITE_INDEX_CONSTRAINT_NE        68  /* 3.21.0 and later */
#define SQLITE_INDEX_CONSTRAINT_ISNOT     69  /* 3.21.0 and later */
#define SQLITE_INDEX_CONSTRAINT_ISNOTNULL 70  /* 3.21.0 and later */
#define SQLITE_INDEX_CONSTRAINT_ISNULL    71  /* 3.21.0 and later */
#define SQLITE_INDEX_CONSTRAINT_IS        72  /* 3.21.0 and later */
#define SQLITE_INDEX_CONSTRAINT_LIMIT     73  /* 3.38.0 and later */
#define SQLITE_INDEX_CONSTRAINT_OFFSET    74  /* 3.38.0 and later */
#define SQLITE_INDEX_CONSTRAINT_FUNCTION 150  /* 3.25.0 and later */
#define SQLITE_INDEX_SCAN_UNIQUE           1  /* Scan visits at most 1 row */

Используйте sqlite3_vtab_collation(), чтобы найти название последовательности сопоставления, которая должна использоваться, оценивая i-е ограничение:

const char *sqlite3_vtab_collation(sqlite3_index_info*, int i);

Ядро SQLite вызывает xBestIndex, когда это собирает запрос, который включает виртуальную таблицу. Другими словами, SQLite вызывает этот метод, когда это управляет sqlite3_prepare() или эквивалент. Вызывая этот метод, ядро SQLite говорит виртуальной таблице, что должно получить доступ к некоторому подмножеству строк в виртуальной таблице, и хочет знать самый эффективный способ сделать это. xBestIndex отвечает информацией, что именно ядро SQLite может использовать, чтобы провести эффективный поиск в виртуальной таблице.

Собирая единственный SQL-запрос, ядро SQLite могло бы вызвать xBestIndex неоднократно с различными параметрами настройки в sqlite3_index_info. Ядро SQLite тогда выберет комбинацию, которая, кажется, дает лучшую производительность.

Прежде, чем вызвать этот метод, ядро SQLite инициализирует экземпляр структуры sqlite3_index_info с информацией о запросе, который это в настоящее время пытается обработать. Эта информация происходит, главным образом, из оператора Where и пунктов ORDER BY или GROUP BY запроса, но также и от любого пункта ON или USING, если запрос это соединение. Информация, которую ядро SQLite предоставляет методу xBestIndex, содержится в части структуры, которая отмечена как "Inputs". Секция "Outputs" инициализируется к нолю.

p>Информация в структуре sqlite3_index_info эфемерна и может быть переписана или освобождена, как только метод xBestIndex возвращается. Если xBestIndex должен помнить какую-либо часть структуры sqlite3_index_info, это должно сделать копию. Важно сохранить копию в месте, где это будет освобождено, например, в области idxStr с needToFreeIdxStr = 1.

Обратите внимание на то, что xBestIndex будут всегда вызывать прежде xFilter, так как выводы idxNum и idxStr из xBestIndex требуют вход в xFilter. Однако, нет никакой гарантии, что xFilter вызовут после успешного xBestIndex.

Метод xBestIndex требуется для каждого виртуального внедрения таблицы.

2.3.1. Входы

Главным, что ядро SQLite пытается сообщить виртуальной таблице, являются ограничения, которые доступны, чтобы ограничить количество строк, которые должны быть просмотрены. Массив aConstraint[] содержит один вход для каждого ограничения. Будет точно nConstraint записей в этом множестве.

Каждое ограничение будет обычно соответствовать термину в операторе Where, в пункте USING или ON, который имеет форму:

column OP EXPR

Здесь "column" это колонка в виртуальной таблице, OP оператор вроде "=" или "<", EXPR произвольное выражение. Так, например, если оператор Where содержал такой термин:

a = 5

Тогда одно из ограничений было бы на колонке "a" с оператором "=" и выражение "5". У ограничений не должно быть буквального представления оператора Where. Оптимизатор запросов мог бы сделать преобразования к оператору Where, чтобы извлечь столько ограничений, сколько сможет. Так, например, если оператор Where содержал что-то вроде этого:

x BETWEEN 10 AND 100 AND 999>y

Оптимизатор запросов мог бы перевести это в три отдельных ограничения:

x >= 10
x <= 100
y < 999

Для каждого такого ограничения поле aConstraint[].iColumn указывает, какая колонка появляется на левой стороне ограничения. Первая колонка виртуальной таблицы это колонка 0. rowid виртуальной таблицы это колонка -1. Поле aConstraint[].op указывает, какой оператор используется. Константы SQLITE_INDEX_CONSTRAINT_* отображают константы целого числа в значения оператора. Колонки происходят в порядке, в котором они были определены в вызове sqlite3_declare_vtab() в методе xCreate или xConnect. Скрытые столбцы посчитаны, определяя индекс столбца.

Если метод xFindFunction() для виртуальной таблицы определяется, и если xFindFunction() иногда возвращает SQLITE_INDEX_CONSTRAINT_FUNCTION или больше, то ограничения могли бы также иметь форму:

FUNCTION(column, EXPR)

В этом случае значение aConstraint[].op совпадает со значениею, возвращенным xFindFunction() для FUNCTION.

Массив aConstraint[] содержит информацию обо всех ограничениях, которые относятся к виртуальной таблице. Но некоторые ограничения не могли бы быть применимыми из-за способа, которым таблицы упорядочены в соединении. xBestIndex должен поэтому рассмотреть только ограничения, у которых есть флаг aConstraint[].usable = true.

В дополнение к ограничениям оператора Where ядро SQLite также говорит методу xBestIndex о пункте ORDER BY. В агрегатном запросе ядро SQLite могло бы вставить информацию о пункте GROUP BY вместо информации о пункте ORDER BY, но этот факт не должен иметь никакого значения для метода xBestIndex. Если все условия пункта ORDER BY будут колонками в виртуальной таблице, то nOrderBy будет количеством условий в пункте ORDER BY и массив aOrderBy[] определит колонку для каждого термина в порядке пункта и является ли та колонка ASC или DESC.

В SQLite version 3.10.0 (2016-01-06) и позже есть поле colUsed, чтобы указать, какие области виртуальной таблицы на самом деле используются подготовленным запросом. Если младший бит colUsed установлен, это означает, что первая колонка используется. Второй бит соответствует второй колонке. И т. д. Если старший бит colUsed установлен, это означает, что используются одна или более колонок кроме первых 63 колонок. Если информация об использовании колонки необходима методу xFilter, то необходимые биты должны быть закодированы в выводе поля idxNum или idxStr.

2.3.1.1. LIKE, GLOB, REGEXP и MATCH

Для операторов LIKE, GLOB, REGEXP и MATCH значение aConstraint[].iColumn является виртуальным столбцом таблицы, который является левым операндом оператора. Однако, если эти операторы выражаются как вызовы функции вместо операторов, то значение aConstraint[].iColumn ссылается на столбец виртуальной таблицы, который является вторым аргументом той функции:

LIKE(EXPR, column)
GLOB(EXPR, column)
REGEXP(EXPR, column)
MATCH(EXPR, column)

Следовательно, до вызова метода xBestIndex() следующие две формы эквивалентны:

column LIKE EXPR
LIKE(EXPR,column)

Это специальное поведение рассмотрения второго аргумента функции происходит только для функций LIKE, GLOB, REGEXP и MATCH. Для всех других функций значение aConstraint[].iColumn ссылается на первый аргумент функции.

Эта специальная функция LIKE, GLOB, REGEXP и MATCH не относится к методу xFindFunction(). xFindFunction() всегда выключает левый операнд операторов LIKE, GLOB, REGEXP или MATCH.

2.3.1.2. LIMIT и OFFSET

Когда aConstraint[].op = SQLITE_INDEX_CONSTRAINT_LIMIT или SQLITE_INDEX_CONSTRAINT_OFFSET, это указывает, что есть пункт LIMIT или OFFSET в запросе SQL, который использует виртуальную таблица. У операторов LIMIT и OFFSET нет левого операнда, поэтому когда aConstraint[].op = SQLITE_INDEX_CONSTRAINT_LIMIT или SQLITE_INDEX_CONSTRAINT_OFFSET, значение aConstraint[].iColumn бессмысленно и не должно использоваться.

2.3.1.3. Правые значения стороны ограничений

sqlite3_vtab_rhs_value() может использоваться, чтобы попытаться получить доступ к правому операнду ограничения. Однако, значение правого оператора не могло быть известно в то время, когда работает метод xBestIndex, таким образом, sqlite3_vtab_rhs_value() не мог бы быть успешным. Обычно правый операнд ограничения доступен только xBestIndex, если это закодировано как литеральное значение во входе SQL. Если правильный операнд будет закодирован как выражение или параметр хоста, это, вероятно, не будет доступно для xBestIndex. У некоторых операторов, таких как SQLITE_INDEX_CONSTRAINT_ISNULL и SQLITE_INDEX_CONSTRAINT_ISNOTNULL, нет правого операнда. sqlite3_vtab_rhs_value() для них всегда вернет SQLITE_NOTFOUND.

2.3.2. Вывод

Учитывая всю информацию выше, работа с методом xBestIndex нужна, чтобы выяснить лучший способ искать в виртуальной таблице.

Метод xBestIndex передает стратегию индексации xFilter через idxNum и idxStr. Значение idxNum и содержание строки idxStr произвольны, насколько ядро SQLite затронуто и могут иметь любое значение. Ядро SQLite просто копирует информацию от xBestIndex в xFilter, предполагая только, что последовательность работы, на которую ссылаются через idxStr, закончена NUL.

Значение idxStr может быть последовательностью, полученной из функции выделения памяти SQLite, такой как sqlite3_mprintf(). Если это верно, тогда флаг needToFreeIdxStr должен быть установлен в true так, чтобы ядро SQLite знало, что надо вызвать sqlite3_free() на той последовательности, когда это закончит с ней, и таким образом избегает утечки памяти. Значение idxStr может также быть статической постоянной строкой, в этом случае needToFreeIdxStr должен быть false.

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

Если текущая версия SQLite 3.8.2 или больше, estimatedRows может быть установлена в оценку количества строк, возвращенных предложенным планом запросов. Если это значение явно не установлено, используется оценка по умолчанию 25 строк.

Если текущая версия SQLite 3.9.0 или больше, idxFlags может указать в SQLITE_INDEX_SCAN_UNIQUE, что виртуальная таблица возвратит только ноль или одну строки, данную входными ограничениями. Дополнительные биты idxFlags могли бы быть использованы в более поздних версиях SQLite.

Массив aConstraintUsage[] содержит один элемент для каждого из ограничений nConstraint во входном разделе структуры sqlite3_index_info. aConstraintUsage[] применяется xBestIndex, чтобы сказать ядро, как это использует ограничения.

xBestIndex может установить записи aConstraintUsage[].argvIndex в значения, больше, чем ноль. Точно один вход должен быть установлен в 1, другой к 2, третий 3 и т. д., столько, как метод xBestIndex хочет. EXPR соответствующих ограничений будет тогда передан как параметры argv[] в xFilter.

Например, если aConstraint[3].argvIndex = 1, то, когда xFilter вызывают, argv[0], переданный xFilter, будет иметь значение EXPR ограничения aConstraint[3].

2.3.2.1. Опустите проверку ограничений в bytecode

По умолчанию SQLite производит bytecode, который будет проверять все ограничения дважды на каждую строку виртуальной таблицы, чтобы проверить, что они удовлетворены. Если виртуальная таблица может гарантировать, что ограничение будет всегда удовлетворяться, это может попытаться подавить ту перепроверку, установив aConstraintUsage[].omit. Однако, за некоторыми исключениями, это только намек и нет никакой гарантии, что избыточная проверка ограничения будет подавлена. Ключевые пункты:

  • Флаг omit соблюдают только, если значение argvIndex для ограничения больше 0 и меньше или равно 16. Ограничительная проверка никогда не подавляется для ограничений, которые не передают их правый операнд в метод xFilter. Текущее внедрение в состоянии подавить избыточную ограничительную проверку только первых 16 значений, переданных xFilter, хотя это ограничение могло бы быть увеличено в будущих выпусках.

  • Флаг omit всегда соблюдают для ограничений SQLITE_INDEX_CONSTRAINT_OFFSET пока argvIndex больше 0. Урегулирование флага omit на ограничении SQLITE_INDEX_CONSTRAINT_OFFSET указывает SQLite, что виртуальная таблица самостоятельно подавит первые N строк вывода, где N это правый операнд оператора OFFSET. Если реализация установит omit на ограничении SQLITE_INDEX_CONSTRAINT_OFFSET, но не подавляет первые N строк вывода, неправильный ответ будет следовать из полного запроса.

2.3.2.2. ORDER BY и orderByConsumed

Если виртуальная таблица произведет строки в порядке, определенном пунктом ORDER BY, то флаг orderByConsumed может быть установлен в true. Если вывод не автоматически в правильном порядке, тогда orderByConsumed должен быть оставлен в значение по умолчанию false. Это укажет ядру SQLite, что оно должно будет сделать отдельную сортировку после того, как оно выходит из виртуальной таблицы. Урегулирование orderByConsumed является оптимизацией. Запрос будет всегда получать правильный ответ, если orderByConsumed оставят в его значении по умолчанию (0). Ненужных операций по сортировке можно было бы избежать, приведя к более быстрому запросу, если orderByConsumed установлен, но неправильная установка orderByConsumed может привести к неправильному ответу. Предложено, чтобы новые виртуальные внедрения таблицы оставили значение orderByConsumed первоначальным, а затем после того, как все остальное, как известно, работает правильно, вернулись и попытались оптимизировать, установив orderByConsumed в соответствующих случаях.

Иногда флаг orderByConsumed может быть безопасно установлен, даже если вывод виртуальной таблицы не находится строго в порядке, определенном nOrderBy и aOrderBy. Если sqlite3_vtab_distinct() вернет 1 или 2, это указывает, что порядок может быть смягчен. См. документацию относительно sqlite3_vtab_distinct().

2.3.3. Возвращаемое значение

xBestIndex должен возвратить SQLITE_OK на успехе. Если какой-либо вид фатальной ошибки происходит, соответствующий код ошибки (например, SQLITE_NOMEM) должен быть возвращен вместо этого.

Если xBestIndex вернет SQLITE_CONSTRAINT, это не указывает на ошибку. Скорее SQLITE_CONSTRAINT указывает, что конкретная комбинация входных определенных параметров недостаточна для виртуальной таблицы, чтобы выполнить ее работу. Это логически то же самое, как урегулирование estimatedCost к бесконечности. Если каждое обращение к xBestIndex для конкретного плана запросов возвратит SQLITE_CONSTRAINT, это означает, что нет никакого пути к виртуальной таблице, который будет безопасно использоваться, и sqlite3_prepare() не потерпит неудачу с ошибкой "no query solution".

2.3.4. Предписание обязательных параметров на табличных функциях

Возврат SQLITE_CONSTRAINT из xBestIndex полезен для табличных функций, у которых есть обязательные параметры. Если aConstraint[].usable = false для одного из обязательных параметров, то метод xBestIndex должен возвратить SQLITE_CONSTRAINT. Если обязательное поле не появляется в aConstraint[] вообще, это означает, что соответствующий параметр пропущен во входе SQL. В этом случае xBestIndex должен установить сообщение об ошибке в pVTab->zErrMsg и возвращать SQLITE_ERROR. В итоге:

  1. aConstraint[].usable для обязательного параметра является false вернуть SQLITE_CONSTRAINT.

  2. Обязательный параметр не появляется где угодно в aConstraint[] установить сообщение об ошибке в pVTab->zErrMsg и вернуть SQLITE_ERROR.

Следующий пример лучше иллюстрирует использование SQLITE_CONSTRAINT как возвращаемое значение от xBestIndex:

SELECT * FROM realtab, tablevaluedfunc(realtab.x);

Предполагая, что первый скрытый столбец "tablevaluedfunc" это "param1", запрос выше семантически эквивалентен этому:

SELECT * FROM realtab, tablevaluedfunc
         WHERE tablevaluedfunc.param1 = realtab.x;

Планировщик запроса должен выбрать между многими возможными внедрениями этого запроса, но два плана в особенности знамениты:

  1. Просмотрите все строки realtab и для каждой найдите строки в tablevaluedfunc, где param1 = realtab.x

  2. Просмотрите все строки табличной функции и для каждой строки найдите строки в realtab, где x = tablevaluedfunc.param1.

Метод xBestIndex будет вызван однажды для каждого из потенциальных планов выше. Для плана 1 aConstraint[].usable для ограничений SQLITE_CONSTRAINT_EQ столбца param1 будет true, потому что значение правой стороны для ограничения "param1 = ?" будет известно, так как оно определяется внешним циклом realtab. Но для плана 2 aConstraint[].usable для "param1 = ?" будет false, потому что значение правой стороны определяется внутренним циклом и является таким образом неизвестным количеством. Поскольку param1 это необходимый вход к табличным функциям, метод xBestIndex должен возвратить SQLITE_CONSTRAINT, когда представлен план 2, указав, что необходимый вход отсутствует. Это вынуждает планировщик запроса выбрать план 1.

2.4. Метод xDisconnect

int (*xDisconnect)(sqlite3_vtab *pVTab);

Этот метод освобождает связь с виртуальной таблицей. Только объект sqlite3_vtab ликвидирован. Виртуальная таблица не разрушена и любое запоминающее устройство, связанное с виртуальной таблицей, сохраняется. Этот метод отменяет работу xConnect.

Этот метод это деструктор для связи с виртуальной таблицей. Противопоставьте этот метод xDestroy. xDestroy это деструктор для всей виртуальной таблицы.

xDisconnect требуется для каждого виртуального внедрения таблицы, хотя приемлемо для методов xDisconnect быть и той же самой функцией, если это имеет смысл для конкретной виртуальной таблицы.

2.5. Метод xDestroy

int (*xDestroy)(sqlite3_vtab *pVTab);

Этот метод освобождает связь с виртуальной таблицей, точно так же, как xDisconnect, и это также разрушает внедрение базовой таблицы. Этот метод отменяет работу xCreate.

xDisconnect вызывают каждый раз, когда соединение с базой данных, которое использует виртуальная таблица, закрывается. Метод xDestroy вызывают только когда запрос DROP TABLE выполняется для виртуальной таблицы.

xDestroy требуется для каждого виртуального внедрения таблицы, хотя приемлемо для методов xDisconnect и xDestroy быть той же самой функцией, если это имеет смысл для конкретной виртуальной таблицы.

2.6. Метод xOpen

int (*xOpen)(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor);

xOpen создает новый курсор, используемый для доступа (чтения и/или записи) виртуальной таблицы. Успешный вызов этого метода ассигнует память для sqlite3_vtab_cursor (или подкласса), инициализирует новый объект и делает *ppCursor указателем на новый объект. Успешный вызов тогда возвращает SQLITE_OK.

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

xOpen не должен инициализировать поле pVtab структуры sqlite3_vtab_cursor. Ядро SQLite будет заботиться об этом автоматически.

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

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

xOpen требуется для каждого виртуального внедрения таблицы.

2.7. Метод xClose

int (*xClose)(sqlite3_vtab_cursor*);

xClose закрывает курсор, ранее открытый xOpen. Ядро SQLite будет всегда вызывать xClose однажды для каждого курсора, открытого, используя xOpen.

Этот метод должен высвободить все средства, ассигнованные соответствующим xOpen. Установленный порядок не вызовут снова, даже если он возвратит ошибку. Ядро SQLite не будет использовать sqlite3_vtab_cursor снова после того, как это будет закрыто.

xClose требуется для каждого виртуального внедрения таблицы.

2.8. Метод xEof

int (*xEof)(sqlite3_vtab_cursor*);

xEof должен возвратить false (0), если указанный курсор в настоящее время указывает на действительную строку данных, или true (не 0) иначе. Этот метод немедленно вызывает движок SQL после каждого xFilter и xNext.

xEof требуется для каждого виртуального внедрения таблицы.

2.9. Метод xFilter

int (*xFilter)(sqlite3_vtab_cursor*, int idxNum, const char *idxStr,
               int argc, sqlite3_value **argv);

Этот метод начинает поиск в виртуальной таблице. Первый аргумент это курсор, открытый xOpen. Следующие два аргумента определяют конкретный индекс поиска, ранее выбранный xBestIndex. Определенные значения idxNum и idxStr неважны, xFilter и xBestIndex договариваются, каково то значение.

xBestIndex, возможно, просил значения определенных выражений, используя значения aConstraintUsage[].argvIndex values структуры sqlite3_index_info. Те значения передаются к xFilter с использованием argc и argv.

Если виртуальная таблица содержит одну или несколько строк, которые соответствуют критериям поиска, то курсор должен быть левой точкой в первой строке. Последующие обращения к xEof должны вернуть false (0). Если нет никакого совпадения строк, то курсор нужно оставить в состоянии, которое заставит xEof вернуть true (не 0). Движок SQLite будет использовать xColumn и xRowid, чтобы получить доступ к тому содержанию строки. xNext будет использоваться, чтобы продвинуться к следующей строке.

Этот метод должен возвратить SQLITE_OK в случае успеха или код ошибки sqlite иначе.

Метод xFilter требуется для каждого виртуального внедрения таблицы.

2.10. Метод xNext

int (*xNext)(sqlite3_vtab_cursor*);

xNext двигает виртуальный курсор таблицы к следующей строке набора результатов, инициализированного xFilter. Если курсор уже указывает на последнюю строку, когда этот установленный порядок вызывают, то курсор больше не указывает на действительные данные и последующее обращение к xEof должно возвратить true (не 0). Если курсор успешно продвинут к другой строке содержания, то последующие вызовы xEof должны возвратить false (0).

Этот метод должен возвратить SQLITE_OK в случае успеха или код ошибки sqlite иначе.

Метод xNext требуется для каждого виртуального внедрения таблицы.

2.11. Метод xColumn

int (*xColumn)(sqlite3_vtab_cursor*, sqlite3_context*, int N);

Ядро SQLite вызывает этот метод, чтобы найти значение для энной колонки текущей строки. N основан на ноле, таким образом, первая колонка пронумерована как 0. Метод xColumn может возвратить свой результат назад к SQLite с использованием одного из следующего интерфейсов:

Если реализация метода xColumn не вызывает ни одну из функций выше, то значение колонки по умолчанию SQL NULL.

Чтобы поднять ошибку, xColumn должен использовать один из методов result_text(), чтобы установить текст сообщения об ошибке, затем возвратить соответствующий код ошибки. xColumn должен возвратить SQLITE_OK при успехе. Метод xColumn требуется для каждого виртуального внедрения таблицы.

2.12. Метод xRowid

int (*xRowid)(sqlite3_vtab_cursor *pCur, sqlite_int64 *pRowid);

Успешный вызов этого метода заставит *pRowid быть заполненным rowid строки, на которую в настоящее время указывает виртуальный курсор таблицы pCur. Этот метод возвращает SQLITE_OK при успехе. Это возвращает соответствующий код ошибки при неудаче.

xRowid требуется для каждого виртуального внедрения таблицы.

2.13. Метод xUpdate

int (*xUpdate)(sqlite3_vtab *pVTab, int argc, sqlite3_value **argv,
               sqlite_int64 *pRowid);

Все изменения виртуальной таблицы внесены, используя метод xUpdate. Этот метод может использоваться, чтобы вставить, удалить или обновить.

Параметр argc определяет количество записей во множестве argv. Значение argc будет 1 для чистого удаления, N+2 для вставки, замены или обновления, где N это количество колонок в таблице. В предыдущем предложении N включает любые скрытые столбцы.

Каждый вход argv будет иметь ненулевое значение в C, но может содержать значение SQL NULL. Другими словами, всегда верно, что argv[i]!=0 для i от 0 до argc-1. Однако, могло бы иметь место, что sqlite3_value_type(argv[i])==SQLITE_NULL.

argv[0] это rowid строки в виртуальной таблице, которая будет удалена. Если argv[0] является NULL SQL, то никакое удаление не происходит.

argv[1] это rowid новой строки, которая будет вставлена в виртуальную таблицу. Если argv[1] это SQL NULL, то внедрение должно выбрать rowid для недавно вставленной строки. Последующие записи argv[] содержат значения колонок виртуальной таблицы в порядке, в котором были объявлены колонки. Количество колонок будет соответствовать декларации таблицы, что xConnect или xCreate сделали при вызове sqlite3_declare_vtab(). Все скрытые столбцы включены.

Делая вставку без rowid (argc>1, argv[1] = SQL NULL), на виртуальной таблице, которая использует ROWID (но не на виртуальной таблице WITHOUT ROWID), внедрение должно установить *pRowid в rowid недавно вставленной строки, это станет значением, возвращенным sqlite3_last_insert_rowid(). Установка этого значения во всех других случаях является безопасной, но не делает ничего, SQLite игнорирует возвращаемое значение *pRowid, если argc==1 или argv[1] не SQL NULL.

Каждый вызов xUpdate попадет в один из случаев, показанных ниже. Ссылки на argv[i] указывают на значение SQL в пределах объекта argv[i], но не сам объект argv[i].

argc = 1
argv[0] ≠ NULL

DELETE: единственная строка с rowid или PRIMARY KEY = argv[0] удалена. Никакая вставка не происходит.

argc > 1
argv[0] = NULL

INSERT: новая строка вставляется со значениями столбцов, взятыми от argv[2] и после. В rowid виртуальной таблице, если argv[1] это SQL NULL, то новый уникальный rowid произведен автоматически. argv[1] будет NULL для виртуальной таблицы WITHOUT ROWID, в этом случае внедрение должно взять значение PRIMARY KEY из соответствующей колонки в argv[2] и после.

argc > 1
argv[0] ≠ NULL
argv[0] = argv[1]

UPDATE: строка с rowid или PRIMARY KEY argv[0] обновляется с новыми значениями в argv[2] и после.

argc > 1
argv[0] ≠ NULL
argv[0] ≠ argv[1]

UPDATE с изменением rowid или PRIMARY KEY: строка с rowid или PRIMARY KEY argv[0] обновлена с rowid или PRIMARY KEY в argv[1] и новыми значениями в argv[2] и далее. Это произойдет, когда SQL-оператор обновит rowid, как в запросе:

UPDATE table SET rowid=rowid+1 WHERE ...;

xUpdate должен возвратить SQLITE_OK если и только если это успешно. Если неудача происходит, xUpdate должен возвратить соответствующий код ошибки. При неудаче элемент pVTab->zErrMsg может произвольно быть заменен текстом сообщения об ошибке, сохраненным в памяти, ассигнованной от SQLite, используя функции, такие как sqlite3_mprintf() или sqlite3_malloc().

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

Если xUpdate выполняет UPDATE, то sqlite3_value_nochange(X) может использоваться, чтобы обнаружить, какие колонки виртуальной таблицы были на самом деле изменены UPDATE. sqlite3_value_nochange(X) вернет true для колонок, которые не изменяются. На каждом UPDATE SQLite сначала вызовет xColumn отдельно для каждой неизменной колонки в таблице, чтобы получить значение для той колонки. xColumn может проверить, чтобы видеть, неизменна ли колонка на уровне SQL sqlite3_vtab_nochange(). Если xColumn видит, что колонка не изменяется, это должно возвратиться, не устанавливая результат, используя один из sqlite3_result_xxxxx(). Только в этом случае sqlite3_value_nochange() будет true в xUpdate. Если xColumn действительно вызывает один или несколько sqlite3_result_xxxxx(), SQLite понимает, что есть изменение в значения колонки, и вызов sqlite3_value_nochange() для той колонки в xUpdate возвратит false.

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

xUpdate дополнительный. Если указатель xUpdate в sqlite3_module для виртуальной таблицы это NULL, то виртуальная таблица только для чтения.

2.14. Метод xFindFunction

int (*xFindFunction)(sqlite3_vtab *pVtab, int nArg, const char *zName,
                     void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
                     void **ppArg);

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

Когда функция использует колонку от виртуальной таблицы как первый аргумент, этот метод вызывают, чтобы видеть, хотела ли виртуальная таблица перегрузить функцию. Первые три параметра это входы: виртуальная таблица, количество аргументов функции и название функции. Если никакая перегрузка не желаема, этот метод возвращает 0. Чтобы перегрузить функцию, этот метод вписывает в новую реализацию функции *pxFunc, в пользовательские данные *ppArg и возвращает 1 или число между SQLITE_INDEX_CONSTRAINT_FUNCTION и 255.

Исторически, возвращаемое значение от xFindFunction() было 0 или 1. Ноль означает, что функция не перегружена, 1 означает перегрузку. Способность к возвращаемым значениям SQLITE_INDEX_CONSTRAINT_FUNCTION или больше была добавлена в версии 3.25.0 (2018-09-15). Если xFindFunction вернет SQLITE_INDEX_CONSTRAINT_FUNCTION или больше, это значит, что функция берет два аргумента, и функция может использоваться в качестве boolean в WHERE запроса (и что виртуальная таблица в состоянии эксплуатировать ту функцию, чтобы ускорить результат запроса). Когда xFindFunction возвращает SQLITE_INDEX_CONSTRAINT_FUNCTION или больше, возвращенное значение становится значением sqlite3_index_info.aConstraint.op для одного из ограничений, переданных в xBestIndex(). Первый аргумент функции это колонка, определенная полем aConstraint[].iColumn ограничения, второй аргумент функции это значение, которое будет передано в xFilter() (если значение aConstraintUsage[].argvIndex установлено) или значение, возвращенное из sqlite3_vtab_rhs_value().

Geopoly module это пример виртуальной таблицы, который использует SQLITE_INDEX_CONSTRAINT_FUNCTION, чтобы улучшить работу. xFindFunction() для Geopoly возвращает SQLITE_INDEX_CONSTRAINT_FUNCTION для SQL-функции geopoly_overlap() и это возвращает SQLITE_INDEX_CONSTRAINT_FUNCTION+1 для SQL-функции geopoly_within(). Это разрешает оптимизации поиска для таких запросов, как:

SELECT * FROM geopolytab WHERE geopoly_overlap(_shape, $query_polygon);
SELECT * FROM geopolytab WHERE geopoly_within(_shape, $query_polygon);

Обратите внимание на то, что инфикс функций (LIKE, GLOB, REGEXP и MATCH) полностью изменяет порядок их аргументов. Так "like(A,B)" обычно работал бы как "B like A". Но xFindFunction() всегда смотрит на крайний левый аргумент, а не на первый логический аргумент. Следовательно, для формы "B like A" SQLite смотрит на левый операнд "B" и если тот операнд столбец виртуальной таблицы, он вызывает xFindFunction() на той виртуальной таблице. Но если форма "like(A,B)" используется вместо этого, то SQLite проверяет термин, чтобы видеть, является ли это колонкой виртуальной таблицы, и если так это вызывает xFindFunction() для виртуальной таблицы колонки A.

Указатель функции, возвращенный этим установленным порядком, должен быть действительным для целой жизни объекта sqlite3_vtab, данного в первом параметре.

2.15. Метод xBegin

int (*xBegin)(sqlite3_vtab *pVTab);

Этот метод начинает транзакцию на виртуальной таблице. Этот метод дополнительный. Указатель xBegin в sqlite3_module может быть NULL.

Этот метод всегда сопровождается одним обращением к xCommit или xRollback. Транзакции в виртуальной таблице не вкладываются, таким образом, xBegin не будет вызван несколько раз на единственной виртуальной таблице без обращения к xCommit или xRollback. Множественные вызовы других методов могут и вероятно будут происходить между xBegin и соответствующим xCommit (или xRollback).

2.16. Метод xSync

int (*xSync)(sqlite3_vtab *pVTab);

Этот метод сигнализирует о начале двухфазной передачи на виртуальной таблице. Это дополнительный метод, указатель xSync в sqlite3_module может быть NULL.

Этот метод вызван только после xBegin, но до xCommit или xRollback. Чтобы осуществить двухфазную передачу, xSync на всех виртуальных таблицах вызван до xCommit на любой виртуальной таблице. Если какой-либо из xSync терпит неудачу, вся транзакция отменена до прежнего уровня.

2.17. Метод xCommit

int (*xCommit)(sqlite3_vtab *pVTab);

Этот метод заставляет транзакцию виртуальной таблицы выполнить commit. Это дополнительный метод, указатель xCommit в sqlite3_module может быть NULL.

Обращение к этому методу всегда следует за предшествующим xBegin и xSync.

2.18. Метод xRollback

int (*xRollback)(sqlite3_vtab *pVTab);

Этот метод вызывает отмену транзакции виртуальной таблицы. Это дополнительный метод, указатель xRollback в sqlite3_module может быть NULL.

Обращение к этому методу всегда следует за предшествующим xBegin.

2.19. The xRename Method

int (*xRename)(sqlite3_vtab *pVtab, const char *zNew);

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

xRename опционален. Если опущен, то виртуальная таблица не может быть переименована, используя ALTER TABLE RENAME.

PRAGMA legacy_alter_table позволено до вызова этого метода, и значение для legacy_alter_table восстановлено после того, как этот метод заканчивает работу. Это необходимо для правильной обработки виртуальных таблиц, которые используют теневые таблицы, где теневые таблицы должны быть переименованы, чтобы соответствовать новому виртуальному имени таблицы. Если legacy_alter_format = off, xConnect будет вызван для виртуальной таблицы каждый раз, когда xRename пытается изменить название теневой таблицы.

2.20. Методы xSavepoint, xRelease и xRollbackTo

int (*xSavepoint)(sqlite3_vtab *pVtab, int);
int (*xRelease)(sqlite3_vtab *pVtab, int);
int (*xRollbackTo)(sqlite3_vtab *pVtab, int);

Эти методы предоставляют виртуальному внедрению таблицы возможность осуществить вложенные транзакции. Они всегда дополнительные и будут вызваны только в SQLite version 3.7.7 (2011-06-23) и позже.

При вызове xSavepoint(X,N) это является сигналом виртуальной таблице X, что это должно сохранить свое текущее состояние как точку сохранения N. Последующее обращение к xRollbackTo(X,R) означает, что статус виртуальной таблицы должен возвратиться к тому, каким это было, когда был вызван xSavepoint(X,R). Обращение к xRollbackTo(X,R) лишит законной силы все точки сохранения с N>R, ни одна из этих точек сохранения не будет отменена или освобождена, не будучи повторно инициализированной через xSavepoint(). xRelease(X,M) лишает законной силы все точки сохранения, где N>=M.

Методы xSavepoint(), xRelease() или xRollbackTo() никогда не будут вызваны между обращениями к xBegin() и xCommit() или xRollback().

2.21. Метод xShadowName

Некоторые виртуальные внедрения таблицы (например, FTS3, FTS5 и RTREE) используют реальные (невиртуальные) таблицы базы данных, чтобы сохранить содержание. Например, когда содержание вставляется в виртуальную таблицу FTS3, данные в конечном счете хранятся в реальных таблицах, названных "%_content", "%_segdir", "%_segments", "%_stat" и "%_docsize", где "%" это название оригинальной виртуальной таблицы. Эти вспомогательные реальные таблицы, которые хранят содержание для виртуальной таблицы, называют "теневыми таблицами". См. (1), (2) и (3).

Метод xShadowName существует, чтобы позволить SQLite определять, является ли определенная реальная таблица на самом деле теневой таблицей для виртуальной таблицы.

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

  • Название таблицы содержит один или несколько символов "_".
  • Часть имени до последнего "_" точно соответствует названию виртуальной таблицы, которая была составлена, используя CREATE VIRTUAL TABLE. Теневые таблицы не признаны за одноименные виртуальные таблицы и табличные функции.
  • Виртуальная таблица содержит метод xShadowName.
  • Метод xShadowName возвращает true, когда его вход это часть имени таблицы до последнего символа "_".

Если SQLite признает таблицу теневой и если установлен флаг SQLITE_DBCONFIG_DEFENSIVE, то теневая таблица только для чтения для обычных SQL-операторов. Теневая таблица может все еще быть написана, но только SQL, который вызван из одного из методов некоторого виртуального внедрения таблицы.

Метод xShadowName должен защитить содержание теневых таблиц от того, чтобы быть испорченным враждебным SQL. Каждое виртуальное внедрение таблицы, которое использует теневые таблицы, должно быть в состоянии обнаружить и справиться с испорченным теневым содержанием таблицы. Однако, ошибки в виртуальных таблицах могли бы позволить сознательно испорченной теневой таблице вызывать сбой. Механизм xShadowName стремится избежать деяний нулевого дня, предотвращая обычные SQL-операторы от преднамеренного повреждения теневых таблиц.

Теневые таблицы допускают чтение-запись по умолчанию. Теневые таблицы становятся только только для чтения, когда установлен флаг SQLITE_DBCONFIG_DEFENSIVE через sqlite3_db_config(). Теневые таблицы должны быть чтением-записью по умолчанию, чтобы поддержать обратную совместимость. Например, текст SQL, произведенный командой .dump в CLI пишет непосредственно в теневые таблицы.

2.22. Метод xIntegrity

Если iVersion для sqlite3_module будет равняться 4 или больше, и метод xIntegrity не NULL, то PRAGMA integrity_check и PRAGMA quick_check выизовут xIntegrity как часть его обработки. Если xIntegrity напишет последовательность сообщения об ошибке в пятый параметр, то PRAGMA integrity_check сообщит об ошибке как часть вывода. Так, другими словами метод xIntegrity позволяет PRAGMA integrity_check проверять целостность содержания, сохраненного в виртуальной таблице.

Метод xIntegrity вызывают с пятью параметрами:

  • pVTab → указатель на объект sqlite3_vtab, который является виртуальной проверяемой таблицей.
  • zSchema → название схемы ("main", "temp" и т. д.), в которой определяется виртуальная таблица.
  • zTabName → название виртуальной таблицы.
  • mFlags → флаг, чтобы указать, является ли это "integrity_check" или "quick_check". В настоящее время этот параметр всегда будет 0 или 1, хотя будущие версии SQLite могли бы использовать другие биты целого числа, чтобы указать на дополнительные варианты обработки.
  • pzErr → этот параметр указывает на "char*", который инициализируется к NULL. xIntegrity() должно сделать *pzErr указателем на строку ошибки, полученную из sqlite3_malloc() или эквивалента, если это находит какие-либо проблемы.

xIntegrity должен обычно возвращать SQLITE_OK даже если это находит проблемы в содержании виртуальной таблицы. Любой другой код ошибки означает что сам xIntegrity имеет проблемы, пытаясь оценить виртуальное содержание таблицы. Так, например, если инвертированный индекс для FTS5 внутренне непоследователен, то xIntegrity должен написать соответствующее сообщение об ошибке в параметр pzErr и возвратить SQLITE_OK. Но если xIntegrity неспособен закончить свою оценку виртуального содержания таблицы из-за исчерпывания памяти, то это должно возвратить SQLITE_NOMEM.

Если сообщение об ошибке произведено, место для него должно быть получено из sqlite3_malloc64() или аналога. Собственность последовательности сообщения об ошибке перейдет к ядру SQLite, когда xIntegrity возвратится. Ядро удостоверится, что sqlite3_free() вызван, чтобы освободить память. PRAGMA integrity_check, которая вызывает xIntegrity, не изменяет возвращенное сообщение об ошибке. Сам xIntegrity должен включать название виртуальной таблицы как часть сообщения. zSchema и zName обеспечиваются, чтобы сделать это легче.

mFlags в настоящее время boolean (0 или 1), которое указывает, вызвали ли xIntegrity для PRAGMA integrity_check (mFlags==0) или PRAGMA quick_check (mFlags==1). Будущие версии SQLite могли бы использовать старшие биты mFlags, чтобы указать на дополнительные варианты обработки.

Поддержка метода xIntegrity была добавлена в SQLite version 3.44.0 (2023-11-01). В том же самом выпуске xIntegrity был добавлен ко многим встроенным виртуальным таблицам, таким как FTS3, FTS5 и RTREE так, чтобы содержание тех таблиц было впредь автоматически проверено на последовательность, когда выполняется PRAGMA integrity_check.