Байткодовый движок SQLite
Содержание
1. Краткое описание
SQLite работает, преобразуя инструкции SQL в байткод, а затем выполняя этот байткод на виртуальной машине. В этом документе описано, как работает байткодовый движок.
В этом документе описано внутреннее устройство SQLite. Приведённая здесь информация не требуется для повседневной разработки приложений с использованием SQLite. Документ предназначен для тех, кто хочет глубже разобраться во внутренней работе SQLite.
Байткодовый движок не является API SQLite. Детали устройства байткодового движка меняются от одного выпуска SQLite к следующему. Приложения, использующие SQLite, не должны зависеть от каких-либо сведений, приведённых в этом документе.
О причинах, по которым SQLite предпочитает использовать байткод для реализации SQL, см. документ «Почему SQLite использует байткод».
2. Введение
SQLite работает, преобразуя каждую инструкцию SQL в байткод, а затем выполняя этот байткод. Подготовленная инструкция в SQLite — это в основном байткод, необходимый для реализации соответствующей инструкции SQL. Интерфейс sqlite3_prepare_v2() представляет собой компилятор, который преобразует SQL в байткод. Интерфейс sqlite3_step() представляет собой виртуальную машину, которая выполняет байткод, содержащийся в подготовленной инструкции.
Виртуальная машина байткода — сердце SQLite. Программистам, желающим понять, как SQLite работает внутри, необходимо разбираться в байткодовом движке.
Исторически байткодовый движок SQLite называется «Virtual DataBase Engine», или «VDBE». На этом сайте термины «байткодовый движок», «VDBE», «виртуальная машина» и «виртуальная машина байткода» используются как взаимозаменяемые, поскольку означают одно и то же.
В этой статье термины «байткодовая программа» и «подготовленная инструкция» также используются как взаимозаменяемые, поскольку в основном означают одно и то же.
2.1. Исходный код VDBE
Исходный код байткодового движка находится в файле vdbe.c. Определения кодов операций в этом документе составлены на основе комментариев в этом файле. Комментарии в исходном коде являются авторитетным источником сведений о байткодовом движке. Если возникнут сомнения, обращайтесь к исходному коду.
Помимо основного файла исходного кода vdbe.c, в дереве исходного кода есть и другие вспомогательные файлы, все названия которых начинаются с «vdbe» — сокращения от «Virtual DataBase Engine».
Помните, что названия и значения кодов операций часто меняются от одного выпуска SQLite к следующему. Поэтому, если вы изучаете вывод EXPLAIN в SQLite, следует обратиться к версии этого документа (или исходного кода vdbe.c), соответствующей версии SQLite, в которой выполнялся EXPLAIN. В противном случае описание кодов операций может оказаться неточным. Этот документ составлен на основе версии SQLite 3.53.0, зафиксированной в ревизии 4525003a53a7f от 2026-04-09.
2.2. Формат инструкций
Программа на байткоде SQLite состоит из одной или нескольких инструкций. У каждой инструкции есть код операции и пять операндов с именами P1, P2, P3, P4 и P5. Операнды P1, P2 и P3 — это 32-разрядные целые числа со знаком. Эти операнды часто ссылаются на регистры. В инструкциях, работающих с курсорами B-дерева, операнд P1 обычно задаёт номер курсора. В инструкциях перехода P2 обычно задаёт адрес перехода. P4 может быть 32-разрядным целым числом со знаком, 64-разрядным целым числом со знаком, 64-разрядным числом с плавающей точкой, строковым литералом, литералом Blob, указателем на функцию сравнения последовательности сортировки, указателем на реализацию определённой приложением функции SQL или иметь другое значение. P5 — это 16-разрядное беззнаковое целое число, обычно используемое для хранения флагов. Биты флага P5 иногда могут тонко влиять на работу кода операции. Например, если для кода операции Eq установлен бит SQLITE_NULLEQ (0x0080) операнда P5, значения NULL считаются равными друг другу. В противном случае значения NULL считаются различными.
Некоторые коды операций используют все пять операндов. Другие — один или два. Некоторые коды операций не используют ни одного операнда.
Байткодовый движок начинает выполнение с инструкции номер 0. Выполнение продолжается, пока не встретится инструкция Halt, счётчик команд не станет больше адреса последней инструкции или не произойдёт ошибка. Когда байткодовый движок останавливается, освобождается вся выделенная им память и закрываются все открытые курсоры базы данных. Если выполнение остановилось из-за ошибки, все ожидающие транзакции прерываются, а изменения базы данных откатываются.
Код операции ResultRow приостанавливает работу байткодового движка, и соответствующий вызов sqlite3_step() возвращает SQLITE_ROW. Перед вызовом ResultRow байткодовая программа загружает результаты одной строки запроса в последовательность регистров. Результаты запроса извлекаются из этих регистров с помощью API на языке C, например sqlite3_column_int() или sqlite3_column_text(). При следующем вызове sqlite3_step() байткодовый движок продолжает выполнение со следующей после ResultRow инструкции.
2.3. Регистры
В каждой байткодовой программе имеется фиксированное (но потенциально большое) число регистров. Один регистр может хранить объекты разных типов:
- Значение NULL
- 64-разрядное целое число со знаком
- Число с плавающей точкой двойной точности IEEE (64-разрядное)
- Строку произвольной длины
- BLOB произвольной длины
- Объект RowSet (см. коды операций RowSetAdd, RowSetRead и RowSetTest)
- Объект Frame (используется подпрограммами — см. Program)
Регистр также может быть «неопределённым», то есть вообще не содержать значения. Неопределённое значение отличается от NULL. В зависимости от параметров компиляции попытка прочитать неопределённый регистр обычно приводит к ошибке во время выполнения. Если генератор кода (sqlite3_prepare_v2()) создаёт подготовленную инструкцию, которая читает неопределённый регистр, это означает, что в генераторе кода есть ошибка.
Регистры нумеруются начиная с 0. Большинство кодов операций обращаются как минимум к одному регистру.
Число регистров в одной подготовленной инструкции фиксируется во время компиляции. Содержимое всех регистров очищается при сбросе или завершении подготовленной инструкции.
Внутренний объект Mem хранит значение одного регистра. Абстрактный объект sqlite3_value, доступный через API, на самом деле представляет собой объект Mem, то есть регистр.
2.4. Курсоры B-дерева
У подготовленной инструкции может быть ноль или несколько открытых курсоров. Каждый курсор обозначается небольшим целым числом, которое обычно передаётся как параметр P1 кода операции, использующего этот курсор. На одном и том же индексе или таблице могут быть открыты несколько курсоров. Все курсоры работают независимо, в том числе курсоры, указывающие на одни и те же индексы или таблицы. Виртуальная машина может взаимодействовать с файлом базы данных только через курсор. Инструкции виртуальной машины могут создавать курсор (например, OpenRead или OpenWrite), считывать данные из курсора (Column), перемещать курсор к следующей записи таблицы (например, Next или Prev) и выполнять другие действия. Все курсоры автоматически закрываются при сбросе или завершении подготовленной инструкции.
2.5. Подпрограммы, сопрограммы и подпрограммы триггеров
В байткодовом движке нет стека для хранения адреса возврата из подпрограммы. Адреса возврата необходимо хранить в регистрах. Поэтому байткодовые подпрограммы не являются реентерабельными.
Код операции Gosub сохраняет текущий счётчик команд в регистр P1, а затем переходит по адресу P2. Код операции Return выполняет переход по адресу P1+1. Таким образом, каждая подпрограмма связана с двумя целыми числами: адресом точки входа в подпрограмму и номером регистра, используемого для хранения адреса возврата.
Код операции Yield меняет местами значение счётчика команд и целое значение в регистре P1. Этот код операции используется для реализации сопрограмм. Сопрограммы часто используются для реализации подзапросов, содержимое которых извлекается по мере необходимости.
Триггеры должны быть реентерабельными. Поскольку байткодовые подпрограммы не являются реентерабельными, для реализации триггеров используется другой механизм. Каждый триггер реализуется отдельной байткодовой программой с собственными кодами операций, счётчиком команд и набором регистров. Код операции Program вызывает подпрограмму триггера. Инструкция Program выделяет и инициализирует новый набор регистров при каждом вызове подпрограммы, поэтому подпрограммы могут быть реентерабельными и рекурсивными. Код операции Param используется подпрограммами для доступа к содержимому регистров вызывающей байткодовой программы.
2.6. Самоизменяющийся код
Некоторые коды операций изменяют сами себя. Например, код операции Init (всегда являющийся первым кодом операции в каждой байткодовой программе) увеличивает значение своего операнда P1. Последующие коды операций Once сравнивают свои операнды P1 со значением P1 кода операции Init, чтобы определить, следует ли пропустить следующий за ними код однократной инициализации. Другой пример — код операции String8, который преобразует свой операнд P4 из UTF-8 в правильную кодировку строк базы данных, а затем заменяет себя кодом операции String.
3. Просмотр байткода
Каждая инструкция SQL, обрабатываемая SQLite, преобразуется в программу для виртуальной машины. Но если инструкция SQL начинается с ключевого слова EXPLAIN, виртуальная машина не будет выполнять программу. Вместо этого инструкции программы будут возвращены по одной в каждой строке, как результат запроса. Эта возможность полезна для отладки и изучения работы виртуальной машины. Например:
$ sqlite3 ex1.db sqlite> explain delete from tbl1 where two<20; addr opcode p1 p2 p3 p4 p5 comment ---- ------------- ---- ---- ---- ------------- -- ------------- 0 Init 0 12 0 00 Start at 12 1 Null 0 1 0 00 r[1]=NULL 2 OpenWrite 0 2 0 3 00 root=2 iDb=0; tbl1 3 Rewind 0 10 0 00 4 Column 0 1 2 00 r[2]=tbl1.two 5 Ge 3 9 2 (BINARY) 51 if r[2]>=r[3] goto 9 6 Rowid 0 4 0 00 r[4]=rowid 7 Once 0 8 0 00 8 Delete 0 1 0 tbl1 02 9 Next 0 4 0 01 10 Noop 0 0 0 00 11 Halt 0 0 0 00 12 Transaction 0 1 1 0 01 usesStmtJournal=0 13 TableLock 0 2 1 tbl1 00 iDb=0 root=2 write=1 14 Integer 20 3 0 00 r[3]=20 15 Goto 0 1 0 00
Любое приложение может выполнить запрос EXPLAIN, чтобы получить вывод, похожий на приведённый выше. Однако отступы, показывающие структуру циклов, не генерируются ядром SQLite. В командной оболочке есть дополнительная логика для расстановки отступов в циклах. Кроме того, столбец «comment» в выводе EXPLAIN предоставляется только в том случае, если SQLite скомпилирован с параметром -DSQLITE_ENABLE_EXPLAIN_COMMENTS.
При компиляции SQLite с параметром SQLITE_DEBUG становятся доступны дополнительные команды PRAGMA, полезные для отладки и изучения работы VDBE. Например, включение pragma vdbe_trace приводит к тому, что при выполнении каждого кода операции VDBE его дизассемблированное представление выводится в стандартный поток вывода. К этим отладочным pragma относятся:
4. Коды операций
В настоящее время виртуальная машина определяет 192 кода операции. Все определённые на данный момент коды операций описаны в таблице ниже. Эта таблица автоматически создана путём сканирования исходного кода файла vdbe.c.
Помните: коды операций VDBE не являются частью определения интерфейса SQLite. Число кодов операций, их названия и значения меняются от одного выпуска SQLite к следующему. Коды операций, приведённые в таблице ниже, соответствуют версии SQLite 3.53.0, зафиксированной в ревизии 4525003a53a7f от 2026-04-09.
| Название опкода | Описание |
|---|---|
| Abortable | Проверяет, может ли произойти Abort. Вызывает утверждение, если Abort на этом этапе может привести к повреждению базы данных. Этот опкод встречается только в отладочных сборках. Abort безопасен, если записей ещё не было или если активен журнал операторов. |
| Add | Складывает значение в регистре P1 со значением в регистре P2 и сохраняет результат в регистре P3. Если любой из операндов равен NULL, результат равен NULL. |
| AddImm | Прибавляет константу P2 к значению в регистре P1. Результат всегда является целым числом. Чтобы принудительно преобразовать любой регистр в целое число, достаточно прибавить 0. |
| Affinity | Применяет типы аффинности к диапазону из P2 регистров, начиная с P1. P4 — это строка длиной P2 символов. N-й символ строки указывает аффинность столбца, которую следует использовать для N-й ячейки памяти в диапазоне. |
| AggFinal | P1 — это ячейка памяти, служащая аккумулятором для агрегатной или оконной функции. Выполняет функцию-завершитель агрегата и сохраняет результат в P1. P2 — количество аргументов функции шага, а P4 — указатель на FuncDef этой функции. Аргумент P2 этим опкодом не используется. Он нужен только для различения функций, принимающих разное количество аргументов. Аргумент P4 требуется лишь в том случае, если функция шага ранее не вызывалась. |
| AggInverse | Выполняет функцию xInverse агрегата. Функция принимает P5 аргументов. P4 — указатель на структуру FuncDef, задающую функцию. Регистр P3 является аккумулятором. Аргументы P5 берутся из регистра P2 и следующих за ним регистров. |
| AggStep | Выполняет функцию xStep агрегата. Функция принимает P5 аргументов. P4 — указатель на структуру FuncDef, задающую функцию. Регистр P3 является аккумулятором. Аргументы P5 берутся из регистра P2 и следующих за ним регистров. |
| AggStep1 | Выполняет функцию xStep (если P1==0) или xInverse (если P1!=0) агрегата. Функция принимает P5 аргументов. P4 — указатель на структуру FuncDef, задающую функцию. Регистр P3 является аккумулятором. Аргументы P5 берутся из регистра P2 и следующих за ним регистров. Изначально этот опкод записывается как OP_AggStep0. При первом выполнении FuncDef, хранящийся в P4, преобразуется в sqlite3_context, а опкод изменяется. Благодаря этому инициализация sqlite3_context выполняется только один раз, а не при каждом вызове функции шага. |
| AggValue | Вызывает функцию xValue() и сохраняет результат в регистре P3. P2 — количество аргументов функции шага, а P4 — указатель на FuncDef этой функции. Аргумент P2 этим опкодом не используется. Он нужен только для различения функций, принимающих разное количество аргументов. Аргумент P4 требуется лишь в том случае, если функция шага ранее не вызывалась. |
| And | Выполняет логическое И над значениями в регистрах P1 и P2 и записывает результат в регистр P3. Если P1 или P2 равен 0 (false), результат равен 0, даже если другой операнд — NULL. Значение NULL при операции И с true, а также два значения NULL дают результат NULL. |
| AutoCommit | Устанавливает флаг автоматической фиксации базы данных в значение P1 (1 или 0). Если P2 имеет значение true, выполняет откат всех активных транзакций btree. Если имеются активные виртуальные машины (кроме этой), ROLLBACK завершается ошибкой. COMMIT завершается ошибкой, если есть активные виртуальные машины, выполняющие запись, или активные виртуальные машины, использующие общий кэш. Эта инструкция останавливает виртуальную машину. |
| BeginSubrtn | Помечает начало подпрограммы, в которую можно перейти напрямую или вызвать с помощью Gosub. Подпрограмма должна завершаться инструкцией Return, у которой операнд P1 совпадает с операндом P2 этого опкода, а P3 равен 1. Если подпрограмма выполняется напрямую, инструкция Return просто передаст управление следующей инструкции. Если же подпрограмма вызвана с помощью Gosub, инструкция Return вернёт управление к первой инструкции после Gosub. Эта процедура загружает NULL в регистр P2. Когда регистр адреса возврата содержит NULL, инструкция Return ничего не делает и просто передаёт управление следующей инструкции (при условии, что значение P3 опкода Return равно 1). Поэтому при прямом выполнении подпрограммы Return продолжит выполнение следующих за ней инструкций. Если же подпрограмма вызвана через Gosub, инструкция Return вернёт управление по адресу, следующему за Gosub. Этот опкод идентичен Null. У него другое имя только для упрощения чтения и проверки байт-кода. |
| BitAnd | Выполняет побитовое И над значениями в регистрах P1 и P2 и сохраняет результат в регистре P3. Если любой из операндов равен NULL, результат равен NULL. |
| BitNot | Интерпретирует содержимое регистра P1 как целое число. Сохраняет побитовое дополнение значения P1 в регистре P2. Если P1 содержит NULL, сохраняет NULL в P2. |
| BitOr | Выполняет побитовое ИЛИ над значениями в регистрах P1 и P2 и сохраняет результат в регистре P3. Если любой из операндов равен NULL, результат равен NULL. |
| Blob | P4 указывает на блоб данных длиной P1 байт. Сохраняет этот блоб в регистре P2. Если P4 — указатель NULL, создаёт в P2 блоб длиной P1 байт, заполненный нулями. |
| Cast | Принудительно преобразует значение в регистре P1 к типу, заданному P2.
Значение NULL эта процедура не изменяет. Оно остаётся NULL. |
| Checkpoint | Выполняет контрольную точку для базы данных P1. Если P1 в данный момент не работает в режиме WAL, опкод ничего не делает. Параметр P2 принимает одно из значений SQLITE_CHECKPOINT_PASSIVE, FULL, RESTART или TRUNCATE. Записывает 1 или 0 в mem[P3] в зависимости от того, вернула ли контрольная точка SQLITE_BUSY. Записывает количество страниц в WAL после контрольной точки в mem[P3+1], а количество страниц в WAL, для которых контрольная точка завершилась, — в mem[P3+2]. Однако при ошибке mem[P3+1] и mem[P3+2] инициализируются значением -1. |
| Clear | Удаляет всё содержимое таблицы или индекса базы данных, корневая страница которого в файле базы данных задана значением P1. Однако, в отличие от Destroy, не удаляет таблицу или индекс из файла базы данных. Если P2==0, очищаемая таблица находится в основном файле базы данных. Если P2==1, таблица находится во вспомогательном файле базы данных, используемом для хранения таблиц, созданных с помощью CREATE TEMPORARY TABLE. Если значение P3 не равно нулю, счётчик изменений строк увеличивается на количество строк в очищаемой таблице. Если P3 больше нуля, значение в регистре P3 также увеличивается на количество строк в очищаемой таблице. См. также: Destroy |
| Close | Закрывает курсор, ранее открытый как P1. Если P1 в данный момент не открыт, инструкция ничего не делает. |
| ClrSubtype | Очищает подтип регистра P1. |
| CollSeq | P4 — указатель на объект CollSeq. Если при следующем вызове пользовательской функции или агрегата будет вызвана sqlite3GetFuncCollSeq(), будет возвращена эта последовательность сортировки. Она используется встроенными функциями min(), max() и nullif(). Если P1 не равен нулю, он обозначает регистр, который последующий агрегат min() или max() установит в 1, если текущая строка не является минимальной или максимальной. Эта инструкция инициализирует регистр P1 значением 0. Интерфейс, используемый реализациями упомянутых выше функций для получения последовательности сортировки, заданной этим опкодом, публично недоступен. Эту возможность используют только встроенные функции. |
| Column | Интерпретирует данные, на которые указывает курсор P1, как структуру, созданную с помощью инструкции MakeRecord. (Дополнительные сведения о формате данных см. в описании опкода MakeRecord.) Извлекает из этой записи столбец с номером P2. Если в записи меньше чем (P2+1) значений, извлекает NULL. Извлечённое значение сохраняется в регистре P3. Если в записи меньше полей, чем P2, извлекается NULL. Если же аргумент P4 имеет тип P4_MEM, в качестве результата используется значение аргумента P4. Если в P5 установлен бит OPFLAG_LENGTHARG, гарантируется, что результат будет использоваться только функцией length() или её эквивалентом. Содержимое больших блобов не загружается, что позволяет сэкономить циклы ЦП. Если установлен бит OPFLAG_TYPEOFARG, результат будет использоваться только функцией typeof(), операторами IS NULL или IS NOT NULL либо их эквивалентами. В этом случае загрузку всего содержимого можно пропустить. |
| ColumnsUsed | Этот опкод (который существует только в сборках SQLite с SQLITE_ENABLE_COLUMN_USED_MASK) указывает, какие столбцы таблицы или индекса для курсора P1 используются. P4 — 64-разрядное целое число (P4_INT64), в котором каждый из первых 63 битов установлен для соответствующего столбца из первых 63 столбцов таблицы или индекса, фактически используемого курсором. Старший бит устанавливается, если используется какой-либо столбец после 64-го. |
| Compare | Сравнивает два вектора регистров: reg(P1)..reg(P1+P3-1) (обозначим этот вектор «A») и reg(P2)..reg(P2+P3-1) («B»). Сохраняет результат сравнения для использования следующей инструкцией Jump. Если в P5 установлен бит OPFLAG_PERMUTE, порядок сравнения определяется последним оператором Permutation. Если бит OPFLAG_PERMUTE сброшен, регистры сравниваются последовательно. P4 — структура KeyInfo, задающая последовательности сортировки и порядок сортировки для сравнения. Перестановка применяется только к регистрам. Элементы KeyInfo используются последовательно. Сравнение выполняется в порядке сортировки: значения NULL равны друг другу, NULL меньше чисел, числа меньше строк, а строки меньше блобов. Сразу за этим опкодом должен следовать опкод Jump. |
| Concat | Добавляет текст из регистра P1 в конец текста в регистре P2 и сохраняет результат в регистре P3. Если текст в P1 или P2 равен NULL, сохраняет NULL в P3. P3 = P2 || P1 Регистры P1 и P3 не должны совпадать. Иногда, если P3 совпадает с P2, реализация может избежать вызова memcpy(). |
| Copy | Копирует регистры P1..P1+P3 в регистры P2..P2+P3. Если установлен бит 0x0002 параметра P5, в назначении также сбрасывается флаг MEM_Subtype. Бит 0x0001 параметра P5 указывает, что этот опкод Copy нельзя объединять с другими. Бит 0x0001 используется планировщиком запросов и не влияет на выполнение запроса. Эта инструкция создаёт глубокую копию значения. Для любой строковой или блоб-константы создаётся дубликат. См. также SCopy. |
| Count | Сохраняет в регистре P2 количество записей (целое число) в таблице или индексе, открытом курсором P1. Если P3==0, получается точное количество, для чего требуется просмотреть каждую страницу btree таблицы. Если P3 не равен нулю, возвращается оценка, основанная на текущем положении курсора. |
| CreateBtree | Выделяет новое дерево b-tree в основном файле базы данных, если P1==0, во временном файле базы данных, если P1==1, или в подключённой базе данных, если P1>1. Для таблицы с rowid аргумент P3 должен быть равен 1 (BTREE_INTKEY), а для индекса или таблицы WITHOUT ROWID — 2 (BTREE_BLOBKEY). Номер корневой страницы нового дерева b-tree сохраняется в регистре P2. |
| CursorHint | Передаёт курсору P1 подсказку о том, что ему нужно возвращать только строки, удовлетворяющие выражению Expr в P4. Термы TK_REGISTER в выражении P4 ссылаются на значения, хранящиеся в данный момент в регистрах. Термы TK_COLUMN в выражении P4 ссылаются на столбцы дерева b-tree, на которое указывает курсор P1. |
| CursorLock | Блокирует дерево btree, на которое указывает курсор P1, чтобы другой курсор не мог выполнять запись в это дерево. |
| CursorUnlock | Снимает блокировку с дерева btree, на которое указывает курсор P1, чтобы другие курсоры могли выполнять запись в это дерево. |
| DecrJumpZero | В регистре P1 должно находиться целое число. Уменьшает значение в P1 и переходит к P2, если новое значение равно нулю. |
| DeferredSeek | P1 — открытый курсор индекса, а P3 — курсор соответствующей таблицы. Этот опкод выполняет отложенный поиск курсора таблицы P3 по строке, соответствующей текущей строке P1. Поиск выполняется отложенно. Фактически ничего не происходит, пока курсор не используется для чтения записи. Таким образом, если чтение не выполняется, не происходит и лишнего ввода-вывода. P4 может быть массивом целых чисел (тип P4_INTARRAY), содержащим по одной записи для каждого столбца таблицы P3. Если элемент массива a(i) не равен нулю, чтение столбца a(i)-1 курсором P3 эквивалентно выполнению отложенного поиска, а затем чтению столбца i из P1. Эта информация хранится в P3 и используется для перенаправления чтения из P3 в P1, что может избавить от необходимости искать и читать данные курсором P3. |
| Delete | Удаляет запись, на которую в данный момент указывает курсор P1. Если в параметре P5 установлен бит OPFLAG_SAVEPOSITION, курсор останется указывать на следующую или предыдущую запись таблицы. Если он останется указывать на следующую запись, следующая инструкция Next ничего не сделает. Поэтому в этом случае запись можно удалять внутри цикла Next. Если бит OPFLAG_SAVEPOSITION параметра P5 сброшен, курсор останется в неопределённом состоянии. Если в P5 установлен бит OPFLAG_AUXDELETE, это означает, что данное удаление является одним из нескольких удалений, связанных с удалением строки таблицы и всех соответствующих ей записей индекса. Ровно одно из этих удалений является «основным». Остальные выполняются курсорами OPFLAG_FORDELETE или помечены флагом AUXDELETE. Если установлен флаг OPFLAG_NCHANGE (0x01) параметра P2 (обратите внимание: P2, а не P5), счётчик изменений строк увеличивается (иначе он не изменяется). Если установлен флаг OPFLAG_ISNOOP (0x40) параметра P2 (не P5!), запускается pre-update-hook для удаления, но дерево btree в остальном не изменяется. Это происходит, когда за Delete вскоре следует Insert с тем же ключом, в результате чего запись дерева btree перезаписывается. P1 не должен быть псевдотаблицей. Это должна быть настоящая таблица с несколькими строками. Если P4 не равен NULL, он указывает на объект Table. В этом случае может быть вызван обработчик обновления, pre-update-hook или оба обработчика. Перед вызовом этого опкода курсор P1 в таком случае должен быть позиционирован с помощью NotFound. В частности, если pre-update-hook настроен, он вызывается, когда P4 не равен NULL. Обработчик обновления вызывается, если он настроен, P4 не равен NULL и в P2 установлен флаг OPFLAG_NCHANGE. Если в P2 установлен флаг OPFLAG_ISUPDATE, P3 содержит адрес ячейки памяти со значением, которое операция обновления присвоит rowid строки. |
| Destroy | Удаляет всю таблицу или индекс базы данных, корневая страница которого в файле базы данных задана значением P1. Если P3==0, уничтожаемая таблица находится в основном файле базы данных. Если P3==1, таблица находится во вспомогательном файле базы данных, используемом для хранения таблиц, созданных с помощью CREATE TEMPORARY TABLE. Если включён AUTOVACUUM, другая корневая страница может быть перемещена на место только что удалённой корневой страницы, чтобы сохранить непрерывность расположения всех корневых страниц в начале базы данных. Прежнее значение перемещённой корневой страницы — до перемещения — сохраняется в регистре P2. Если перемещение страницы не потребовалось (поскольку удаляемая таблица уже была последней в базе данных), в регистр P2 записывается ноль. Если AUTOVACUUM отключён, в регистр P2 также записывается ноль. Этот опкод вызывает ошибку, если во время его выполнения работают какие-либо виртуальные машины-читатели. Это сделано, чтобы избежать сложностей с обновлением существующих курсоров при перемещении корневой страницы в базе данных с AUTOVACUUM. Ошибка вызывается, даже если база данных не использует AUTOVACUUM, чтобы избежать несовместимости между режимами с автоочисткой и без неё. См. также: Clear |
| Divide | Делит значение в регистре P1 на значение в регистре P2 и сохраняет результат в регистре P3 (P3=P2/P1). Если значение в регистре P1 равно нулю, результат равен NULL. Если любой из операндов равен NULL, результат равен NULL. |
| DropIndex | Удаляет внутренние структуры данных (в памяти), описывающие индекс с именем P4 в базе данных P1. Этот опкод вызывается после удаления индекса с диска (с помощью опкода Destroy), чтобы внутреннее представление схемы соответствовало данным на диске. |
| DropTable | Удаляет внутренние структуры данных (в памяти), описывающие таблицу с именем P4 в базе данных P1. Этот опкод вызывается после удаления таблицы с диска (с помощью опкода Destroy), чтобы внутреннее представление схемы соответствовало данным на диске. |
| DropTrigger | Удаляет внутренние структуры данных (в памяти), описывающие триггер с именем P4 в базе данных P1. Этот опкод вызывается после удаления триггера с диска (с помощью опкода Destroy), чтобы внутреннее представление схемы соответствовало данным на диске. |
| ElseEq | Этот опкод должен следовать за оператором сравнения Lt или Gt. Между ними может находиться ноль или более опкодов OP_ReleaseReg, но никаких других опкодов между этой инструкцией и предыдущей инструкцией Lt или Gt быть не должно. Если результат сравнения Eq тех же двух операндов, что и у предыдущего Lt или Gt, был бы истинным, выполняется переход к P2. Если результат сравнения Eq двух предыдущих операндов был бы ложным или NULL, выполнение продолжается со следующей инструкции. |
| EndCoroutine | Инструкция по адресу в регистре P1 — это Yield. Выполняет Jump к параметру P2 этой инструкции Yield. После перехода в регистре P1 остаётся значение, при котором последующие OP_Yields возвращают управление к той же инструкции EndCoroutine. См. также: InitCoroutine |
| Eq | Сравнивает значения в регистрах P1 и P3. Если reg(P3)==reg(P1), выполняет переход по адресу P2. Часть SQLITE_AFF_MASK параметра P5 должна содержать символ аффинности — SQLITE_AFF_TEXT, SQLITE_AFF_INTEGER и т. д. Перед сравнением выполняется попытка преобразовать оба операнда согласно этой аффинности. Если SQLITE_AFF_MASK равен 0x00, используется числовая аффинность. Обратите внимание, что результаты преобразования аффинности записываются обратно во входные регистры P1 и P3. Таким образом, этот опкод может вносить постоянные изменения в регистры P1 и P3. После выполнения преобразований, если ни одно из значений не равно NULL, значения сравниваются. Если оба значения являются блобами, для определения результата сравнения используется memcmp(). Если оба значения являются текстом, для сравнения используется соответствующая функция сортировки, заданная в P4. Если P4 не задан, строки сравниваются с помощью memcmp(). Если оба значения числовые, используется числовое сравнение. Если значения имеют разные типы, числа считаются меньше строк, а строки — меньше блобов. Если в P5 установлен SQLITE_NULLEQ, результат сравнения всегда будет истинным или ложным и никогда не будет NULL. Если оба операнда равны NULL, результат сравнения — true. Если один из операндов равен NULL, результат — false. Если ни один из операндов не равен NULL, результат будет таким же, как если бы флаг SQLITE_NULLEQ отсутствовал в P5. Этот опкод сохраняет результат сравнения для использования новой инструкцией Jump. |
| Expire | Помечает предварительно скомпилированные операторы как устаревшие. При выполнении устаревшего оператора с помощью sqlite3_step() он либо автоматически подготовится заново (если изначально был создан с помощью sqlite3_prepare_v2()), либо завершится ошибкой SQLITE_SCHEMA. Если P1 равен 0, устаревшими становятся все операторы SQL. Если P1 не равен нулю, устаревшим становится только выполняемый в данный момент оператор. Если P2 равен 0, операторы SQL устаревают немедленно. Если P2 равен 1, выполняющимся операторам SQL разрешается завершить работу. Случай P2==1 возникает при создании индекса CREATE INDEX или другом аналогичном изменении схемы, которое может ускорить выполнение оператора, не влияя на его корректность. |
| Explain | При обычном выполнении запроса эта инструкция эквивалентна Noop. Назначение этого опкода — хранить сведения о плане запроса для вывода EXPLAIN QUERY PLAN. Значение P4 — понятный человеку текст, описывающий элемент плана запроса. Например, «SCAN t1» или «SEARCH t2 USING INDEX t2x1». Значение P1 — идентификатор текущего элемента, а P2 — родительский элемент для вложенных элементов плана запроса. Если P2 равен нулю, этот элемент является элементом верхнего уровня. Для элементов цикла P3 — оценочная стоимость каждого вызова этого элемента. Как и значения параметров всех опкодов, значения параметров Explain могут меняться от выпуска к выпуску. Приложениям не следует пытаться интерпретировать или использовать какую-либо информацию из опкода Explain. Информация, предоставляемая этим опкодом, предназначена только для тестирования и отладки. |
| Filter | Вычисляет хеш ключа, содержащегося в регистрах P4, начиная с r[P3]. Проверяет, присутствует ли этот хеш в фильтре Блума, хранящемся в регистре P1. Если хеш отсутствует, возможно, выполняется переход к P2. В противном случае выполнение продолжается со следующей инструкции. Ложноотрицательные результаты безвредны. Продолжить выполнение безопасно всегда, даже если значение присутствует в фильтре Блума. Ложноотрицательный результат приводит к лишним циклам ЦП, но правильный ответ всё равно будет получен. Однако ложноположительный результат вполне может привести к неправильному ответу — если переход выполняется, хотя следовало продолжить выполнение. |
| FilterAdd | Вычисляет хеш регистров P4, начиная с r[P3], и добавляет этот хеш в фильтр Блума, содержащийся в r[P1]. |
| FinishSeek | Если ранее курсор P1 был перемещён с помощью DeferredSeek, немедленно завершает эту операцию поиска. Если поиск курсора уже выполнен, инструкция ничего не делает. |
| FkCheck | Останавливает выполнение с ошибкой SQLITE_CONSTRAINT, если имеются неразрешённые нарушения ограничений внешнего ключа. Если нарушений ограничений внешнего ключа нет, инструкция ничего не делает. Нарушения ограничений внешнего ключа также проверяются при завершении подготовленного оператора. Этот опкод используется для вызова ошибок ограничений внешнего ключа до возврата таких результатов, как счётчик изменений строк или результат предложения RETURNING. |
| FkCounter | Увеличивает «счётчик ограничений» на P2 (P2 может быть положительным или отрицательным). Если P1 не равен нулю, увеличивается счётчик ограничений базы данных (отложенные ограничения внешнего ключа). Если P1 равен нулю, увеличивается счётчик оператора (немедленные ограничения внешнего ключа). |
| FkIfZero | Этот опкод проверяет, равен ли в данный момент нулю счётчик ограничений внешнего ключа. Если да, выполняется переход к инструкции P2. В противном случае выполнение продолжается со следующей инструкцией. Если P1 не равен нулю, переход выполняется, когда счётчик ограничений базы данных равен нулю (счётчик, учитывающий отложенные нарушения ограничений). Если P1 равен нулю, переход выполняется, когда счётчик ограничений оператора равен нулю (немедленные нарушения ограничений внешнего ключа). |
| Found | Если P4==0, в регистре P3 находится блоб, созданный с помощью MakeRecord. Если P4>0, регистр P3 — первый из P4 регистров, образующих распакованную запись. Курсор P1 установлен на индексе btree. Если запись, заданная P3 и P4, является префиксом какой-либо записи в P1, выполняется переход к P2, а P1 остаётся указывать на соответствующую запись. После этой операции курсор остаётся в состоянии, допускающем перемещение вперёд. Инструкция Next будет работать, а инструкция Prev — нет. См. также: NotFound, NoConflict, NotExists. SeekGe |
| Function | Вызывает пользовательскую функцию (P4 — указатель на объект sqlite3_context, содержащий указатель на вызываемую функцию) с аргументами из регистра P2 и следующих за ним регистров. Количество аргументов указано в объекте sqlite3_context, на который указывает P4. Результат функции сохраняется в регистре P3. Регистр P3 не должен совпадать ни с одним из входных регистров функции. P1 — 32-разрядная битовая маска, указывающая, были ли аргументы функции определены как константы во время компиляции. Если первый аргумент был константой, устанавливается бит 0 в P1. Это используется для определения того, можно ли безопасно сохранять метаданные, связанные с аргументом пользовательской функции с помощью API sqlite3_set_auxdata(), до следующего вызова этого опкода. |
| Ge | Работает так же, как опкод Lt, за исключением того, что переход выполняется, если содержимое регистра P3 больше или равно содержимому регистра P1. Дополнительные сведения см. в описании опкода Lt. |
| GetSubtype | Извлекает значение подтипа из регистра P1 и записывает его в регистр P2. Если у P1 нет подтипа, в P1 записывается NULL. |
| Gosub | Записывает текущий адрес в регистр P1, а затем выполняет переход по адресу P2. |
| Goto | Безусловный переход по адресу P2. Следующей будет выполнена инструкция с индексом P2 от начала программы. Параметр P1 фактически не используется этим опкодом. Однако иногда ему задают значение 1 вместо 0, чтобы сообщить командной оболочке, что этот Goto завершает цикл и строки от P2 до текущей строки следует выделить отступом в выводе EXPLAIN. |
| Gt | Работает так же, как опкод Lt, за исключением того, что переход выполняется, если содержимое регистра P3 больше содержимого регистра P1. Дополнительные сведения см. в описании опкода Lt. |
| Halt | Немедленно завершает выполнение. Все открытые курсоры и прочие объекты закрываются автоматически. P1 — код результата, возвращаемый sqlite3_exec(), sqlite3_reset() или sqlite3_finalize(). При обычном завершении это должно быть SQLITE_OK (0). При ошибке может возвращаться другое значение. Если P1!=0, параметр P2 определяет, следует ли откатить текущую транзакцию. Откат не выполняется, если P2==OE_Fail. Откат выполняется, если P2==OE_Rollback. Если P2==OE_Abort, отменяются все изменения, выполненные за время этого запуска VDBE, но сама транзакция не откатывается. Если P3 не равен нулю, а P4 равен NULL, в P3 находится регистр с текстом сообщения об ошибке. Если P3 равен нулю, а P4 не равен NULL, строка сообщения об ошибке содержится в P4. Если P5 имеет значение от 1 до 4 включительно, строка сообщения об ошибке из P4 изменяется следующим образом: 1: нарушено ограничение NOT NULL: P4 2: нарушено ограничение UNIQUE: P4 3: нарушено ограничение CHECK: P4 4: нарушено ограничение FOREIGN KEY: P4 Если P3 равен нулю, P5 не равен нулю, а P4 равен NULL, всё после символа «:» опускается. В самом конце каждой программы неявно добавляется инструкция «Halt 0 0 0». Поэтому переход за пределы последней инструкции программы эквивалентен выполнению Halt. |
| HaltIfNull | Проверяет значение в регистре P3. Если оно равно NULL, выполняет Halt с параметрами P1, P2 и P4, как если бы это была инструкция Halt. Если значение в регистре P3 не равно NULL, процедура ничего не делает. Параметр P5 должен быть равен 1. |
| IdxDelete | Содержимое регистров P3, начиная с регистра P2, образует распакованный ключ индекса. Этот опкод удаляет соответствующую запись из индекса, открытого курсором P1. P4 — указатель на структуру Index. Если соответствующая запись индекса не найдена и не включён режим writable_schema, возникает ошибка SQLITE_CORRUPT_INDEX. |
| IdxGE | Значения регистров P4, начиная с P3, образуют распакованный ключ индекса, не содержащий PRIMARY KEY. Compare это значение ключа с индексом, на который в данный момент указывает P1, игнорируя поля PRIMARY KEY или ROWID в конце. Если запись индекса P1 больше или равна значению ключа, перейти к P2. В противном случае перейти к следующей инструкции. |
| IdxGT | Значения регистров P4, начиная с P3, образуют распакованный ключ индекса, не содержащий PRIMARY KEY. Compare это значение ключа с индексом, на который в данный момент указывает P1, игнорируя поля PRIMARY KEY или ROWID в конце. Если запись индекса P1 больше значения ключа, перейти к P2. В противном случае перейти к следующей инструкции. |
| IdxInsert | В регистре P2 хранится ключ индекса SQL, созданный инструкциями MakeRecord. Эта операция записывает ключ в индекс P1. Данные записи равны nil. Если P4 не равен нулю, то он задаёт количество значений в распакованном ключе reg(P2). В этом случае P3 — индекс первого регистра распакованного ключа. Наличие распакованного ключа иногда позволяет выполнить оптимизацию. Если в P5 установлен бит OPFLAG_APPEND, это служит подсказкой для уровня b-tree о том, что, вероятно, выполняется добавление в конец. Если в P5 установлен бит OPFLAG_NCHANGE, эта инструкция увеличивает счётчик изменений. Если бит OPFLAG_NCHANGE не установлен, счётчик изменений не изменяется. Если установлен флаг OPFLAG_USESEEKRESULT в P5, реализация может работать быстрее, избегая ненужного поиска по курсору P1. Однако флаг OPFLAG_USESEEKRESULT следует устанавливать только в том случае, если до этого по курсору не выполнялось поиск либо если при последнем поиске использовался ключ, эквивалентный P2. Эта инструкция работает только с индексами. Эквивалентная инструкция для таблиц — Insert. |
| IdxLE | Значения регистров P4, начиная с P3, образуют распакованный ключ индекса, не содержащий PRIMARY KEY или ROWID. Compare это значение ключа с индексом, на который в данный момент указывает P1, игнорируя PRIMARY KEY или ROWID в индексе P1. Если запись индекса P1 меньше или равна значению ключа, перейти к P2. В противном случае перейти к следующей инструкции. |
| IdxLT | Значения регистров P4, начиная с P3, образуют распакованный ключ индекса, не содержащий PRIMARY KEY или ROWID. Compare это значение ключа с индексом, на который в данный момент указывает P1, игнорируя PRIMARY KEY или ROWID в индексе P1. Если запись индекса P1 меньше значения ключа, перейти к P2. В противном случае перейти к следующей инструкции. |
| IdxRowid | Записать в регистр P2 целое число, являющееся последней записью в записи в конце ключа индекса, на который указывает курсор P1. Это число должно быть rowid записи таблицы, на которую указывает эта запись индекса. См. также: Rowid, MakeRecord. |
| If | Перейти к P2, если значение в регистре P1 истинно. Значение считается истинным, если оно числовое и не равно нулю. Если значение в P1 равно NULL, выполнить переход тогда и только тогда, когда P3 не равен нулю. |
| IfEmpty | Проверить, пуста ли таблица b-tree, на которую ссылается курсор P1, и перейти к P2, если она пуста. |
| IFindKey | Эта инструкция всегда следует за инструкцией Found с такими же значениями P1, P2 и P3, как у этой инструкции, и ненулевым значением P4. Значение P4 этой операции имеет тип P4_INDEX и содержит указатель на объект Index для искомого индекса. Эта операция использует sqlite3VdbeFindIndexKey() для поиска рядом с текущей позицией курсора ключа индекса, который точно совпадает по всем полям, не являющимся индексируемыми выражениями или ссылками на виртуальные генерируемые столбцы, а по остальным полям либо точно совпадает, либо содержит вещественные числа, отличающиеся не более чем на 2 ULP. Иными словами, эта операция ищет рядом записи индекса, очень близкие к ключу поиска, но имеющие небольшие различия в значениях с плавающей точкой, полученных в результате вычисления выражения. Если в курсоре P1 не найдено близкой альтернативной записи, перейти к P2. Если найдено близкое совпадение, перейти к следующей инструкции. Эта операция используется командой PRAGMA integrity_check, чтобы помочь отличить действительно повреждённые индексы от индексов по выражениям, содержащих значения с плавающей точкой, отличающиеся на один или два ULP. |
| IfNoHope | Регистр P3 — первый из P4 регистров, образующих распакованную запись. Курсор P1 указывает на индекс b-tree. P2 — адрес перехода. Иными словами, операнды этой операции совпадают с операндами NotFound и IdxGT. Эта операция предназначена только для оптимизации. Если она всегда переходит к следующей инструкции, правильный результат всё равно будет получен, но потребуется дополнительная работа. Значение N во флаге seekHit курсора P1 означает, что существует ключ P3:N, совпадающий с некоторой записью в индексе. Нужно выяснить, может ли запись P3:P4 совпасть с какой-либо записью в индексе. Если это невозможно, часть работы можно пропустить. Поэтому, если seekHit меньше P4, попытаться выяснить, возможно ли совпадение, выполнив NotFound. Эта операция используется при обработке предложения IN для ключа из нескольких столбцов. Если предложение IN связано с элементом ключа, отличным от самого левого, и при последнем поиске по всему ключу совпадений не найдено, возможно, совпадению препятствует один из элементов ключа слева. Следовательно, «надежды» на совпадение нет независимо от того, сколько элементов предложения IN будет проверено. В этом случае поиск по предложению IN досрочно прекращается с помощью этой операции. Название операции связано с тем, что переход выполняется, если «надежды» на совпадение нет. |
| IfNot | Перейти к P2, если значение в регистре P1 ложно. Значение считается ложным, если его числовое значение равно нулю. Если значение в P1 равно NULL, выполнить переход тогда и только тогда, когда P3 не равен нулю. |
| IfNotOpen | Если курсор P1 не открыт или для P1 установлена строка NULL с помощью операции NullRow, перейти к инструкции P2. В противном случае перейти к следующей инструкции. |
| IfNotZero | Регистр P1 должен содержать целое число. Если исходное содержимое регистра P1 больше нуля, уменьшить значение в регистре P1. Если после этого оно не равно нулю (положительное или отрицательное), перейти к P2. Если изначально регистр P1 равен нулю, оставить его без изменений и перейти к следующей инструкции. |
| IfNullRow | Проверить, указывает ли курсор P1 в данный момент на строку NULL. Если да, установить в регистре P3 значение NULL и немедленно перейти к P2. Если P1 не указывает на строку NULL, перейти к следующей инструкции, не внося изменений. Если P1 не является открытым курсором, эта операция ничего не делает. |
| IfPos | Регистр P1 должен содержать целое число. Если значение регистра P1 равно 1 или больше, вычесть P3 из значения в P1 и перейти к P2. Если исходное значение регистра P1 меньше 1, оно не изменяется, и управление передаётся следующей инструкции. |
| IfSizeBetween | Пусть N — приблизительное количество строк в таблице или индексе, на который указывает курсор P1, а X равно 10*log2(N), если N положительно, или -1, если N равно нулю. Перейти к P2, если X находится в диапазоне от P3 до P4 включительно. |
| IncrVacuum | Выполнить один шаг процедуры инкрементной очистки базы данных P1. Если очистка завершена, перейти к инструкции P2. В противном случае перейти к следующей инструкции. |
| Init | В программах эта операция встречается один раз и является самой первой операцией. Если трассировка включена с помощью интерфейса sqlite3_trace(), строка UTF-8 из P4 передаётся в функцию обратного вызова трассировки. Если P4 пуст, используется строка, возвращаемая sqlite3_sql(). Если P2 не равен нулю, перейти к инструкции P2. Увеличить значение P1, чтобы операции Once при первом выполнении в этом запуске выполняли переход. Если P3 не равен нулю, он задаёт адрес перехода при возникновении ошибки SQLITE_CORRUPT. |
| InitCoroutine | Настроить регистр P1 так, чтобы он выполнял Yield для сопрограммы, расположенной по адресу P3. Если P2!=0, реализация сопрограммы находится сразу после этой операции. Поэтому следует перейти через реализацию сопрограммы к адресу P2. См. также: EndCoroutine |
| Insert | Записать запись в таблицу курсора P1. Если такой записи ещё нет, она создаётся; в противном случае данные существующей записи перезаписываются. Данные — это значение MEM_Blob, хранящееся в регистре P2. Ключ хранится в регистре P3. Ключ должен иметь тип MEM_Int. Если установлен флаг OPFLAG_NCHANGE в P5, счётчик изменений строк увеличивается (иначе он не изменяется). Если установлен флаг OPFLAG_LASTROWID в P5, rowid сохраняется для последующего возврата функцией sqlite3_last_insert_rowid() (иначе он не изменяется). Если установлен флаг OPFLAG_USESEEKRESULT в P5, реализация может работать быстрее, избегая ненужного поиска по курсору P1. Однако флаг OPFLAG_USESEEKRESULT следует устанавливать только в том случае, если до этого по курсору не выполнялось поиск либо если при последнем поиске использовался ключ, равный P3. Если установлен флаг OPFLAG_ISUPDATE, эта операция является частью операции UPDATE. В противном случае (если флаг не установлен) эта операция является частью операции INSERT. Это различие важно только для хука обновления. Параметр P4 может указывать на структуру Table или иметь значение NULL. Если он не равен NULL, после успешной вставки вызывается хук обновления (sqlite3.xUpdateCallback). (ПРЕДУПРЕЖДЕНИЕ/ПЛАН: если P1 — псевдокурсор, а P2 выделен динамически, владение P2 передаётся псевдокурсору, а регистр P2 становится временным. Если курсор изменится, значение регистра P2 также изменится. Убедитесь, что это не вызывает проблем.) Эта инструкция работает только с таблицами. Эквивалентная инструкция для индексов — IdxInsert. |
| Int64 | P4 — указатель на 64-разрядное целое значение. Записать это значение в регистр P2. |
| IntCopy | Перенести целое значение из регистра P1 в регистр P2. Это оптимизированная версия SCopy, работающая только с целыми значениями. |
| Integer | Записать 32-разрядное целое значение P1 в регистр P2. |
| IntegrityCk | Выполнить анализ открытой в данный момент базы данных. Записать в регистр (P1+1) текст сообщения об ошибке с описанием найденных проблем. Если проблем не обнаружено, записать в регистр (P1+1) значение NULL. Регистр (P1) содержит число, на единицу меньше максимального допустимого количества ошибок. Будет выведено не более reg(P1) ошибок. Иными словами, анализ прекращается сразу после обнаружения reg(P1) ошибок. Значение reg(P1) обновляется и содержит оставшееся количество ошибок. Номера корневых страниц всех таблиц базы данных — это целые числа, хранящиеся в аргументе P4_INTARRAY. Если P5 не равен нулю, проверка выполняется для вспомогательного файла базы данных, а не для основного. Эта операция используется для реализации pragma integrity_check. |
| IsNull | Перейти к P2, если значение в регистре P1 равно NULL. |
| IsTrue | Эта операция реализует операторы IS TRUE, IS FALSE, IS NOT TRUE и IS NOT FALSE. Интерпретировать значение в регистре P1 как логическое. Записать это логическое значение (0 или 1) в регистр P2. Если значение в регистре P1 равно NULL, записать в регистр P2 значение P3. Если P4 равно 1, инвертировать результат. Логика кратко представлена ниже:
|
| IsType | Перейти к P2, если тип столбца в b-tree соответствует одному из типов, указанных битовой маской P5. Обычно P1 — это курсор на b-tree, для которого кэш декодирования строки действителен как минимум до столбца P3. Иными словами, ранее должна была выполняться операция Column для столбца P3 или более позднего. Если курсор недействителен, эта операция может дать ошибочные результаты. Если строка b-tree содержит меньше столбцов, чем P3, использовать P4 в качестве типа данных. Если P1 равен -1, P3 задаёт номер регистра, а тип данных определяется по значению в этом регистре. P5 — битовая маска типов данных. SQLITE_INTEGER соответствует младшему биту (0x01). SQLITE_FLOAT — биту 0x02. SQLITE_TEXT — 0x04. SQLITE_BLOB — 0x08. SQLITE_NULL — 0x10. ПРЕДУПРЕЖДЕНИЕ: эта операция не позволяет надёжно отличить NULL от REAL, если P1>=0. Если база данных содержит значение NaN, эта операция определит тип данных как REAL, хотя он должен быть NULL. Если P1<0 и значение уже сохранено в регистре P3, операция надёжно различает NULL и REAL. Проблема возникает только при P1>=0. Перейти по адресу P2 тогда и только тогда, когда тип данных значения, определяемого P1 и P3, соответствует одному из битов битовой маски P5. |
| JournalMode | Изменить режим журнала базы данных P1 на P3. P3 должен быть одним из значений PAGER_JOURNALMODE_XXX. При переключении между различными режимами отката (delete, truncate, persist, off и memory) это простая операция. Операции ввода-вывода не требуются. Переключение в режим WAL или из него выполняется сложнее. Записать в регистр P2 строку с итоговым режимом журнала. |
| Jump | Перейти к инструкции по адресу P1, P2 или P3 в зависимости от того, был ли в последней инструкции Compare вектор P1 меньше, равен или больше вектора P2 соответственно. Эта операция должна непосредственно следовать за операцией Compare. |
| Last | При следующем использовании инструкции Rowid, Column или Prev для P1 будет использоваться последняя запись таблицы или индекса базы данных. Если таблица или индекс пусты и P2>0, немедленно перейти к P2. Если P2 равен 0 или таблица либо индекс не пусты, перейти к следующей инструкции. Эта операция настраивает курсор для перемещения в обратном порядке — от конца к началу. Иными словами, курсор настроен на использование Prev, а не Next. |
| Le | Работает так же, как операция Lt, но переход выполняется, если содержимое регистра P3 меньше или равно содержимому регистра P1. Дополнительные сведения см. в описании операции Lt. |
| LoadAnalysis | Прочитать таблицу sqlite_stat1 базы данных P1 и загрузить её содержимое во внутреннюю хеш-таблицу индексов. Благодаря этому результаты анализа будут использоваться при подготовке всех последующих запросов. |
| Lt | Сравнить значения в регистрах P1 и P3. Если reg(P3)<reg(P1), перейти по адресу P2. Если в P5 установлен бит SQLITE_JUMPIFNULL и reg(P1) или reg(P3) равен NULL, выполнить переход. Если бит SQLITE_JUMPIFNULL не установлен, перейти к следующей инструкции, если хотя бы один из операндов равен NULL. Часть SQLITE_AFF_MASK параметра P5 должна содержать символ аффинности — SQLITE_AFF_TEXT, SQLITE_AFF_INTEGER и т. д. Перед сравнением предпринимается попытка преобразовать оба входных значения согласно этой аффинности. Если SQLITE_AFF_MASK равен 0x00, используется числовая аффинность. Обратите внимание: результаты преобразования аффинности записываются обратно во входные регистры P1 и P3. Поэтому эта операция может привести к постоянному изменению регистров P1 и P3. После выполнения преобразований, если ни одно из значений не равно NULL, значения сравниваются. Если оба значения являются blob, для определения результата сравнения используется memcmp(). Если оба значения являются текстом, для сравнения используется соответствующая функция сортировки, указанная в P4. Если P4 не задан, для сравнения текстовых строк используется memcmp(). Если оба значения числовые, выполняется числовое сравнение. Если значения имеют разные типы, числа считаются меньше строк, а строки — меньше blob. Эта операция сохраняет результат сравнения для использования новой операцией Jump. |
| MakeRecord | Преобразовать P2 регистров, начиная с P1, в формат записи, используемый для хранения записи данных в таблице базы данных или ключа в индексе. Позже операция Column может декодировать эту запись. P4 может быть строкой длиной P2 символов. N-й символ строки задаёт аффинность столбца, которую следует использовать для N-го поля ключа индекса. Соответствие символов аффинностям задаётся макросами SQLITE_AFF_, определёнными в sqliteInt.h. Если P4 равен NULL, для всех полей индекса используется аффинность BLOB. Значение P5 зависит от того, включён ли параметр компиляции SQLITE_ENABLE_NULL_TRIM: * Если SQLITE_ENABLE_NULL_TRIM включён, P5 задаёт индекс самого правого столбца таблицы, для которого можно удалить завершающие значения NULL. * Если SQLITE_ENABLE_NULL_TRIM не задан, P5 имеет значение OPFLAG_NOCHNG_MAGIC, если операции MakeRecord разрешено принимать записи без изменений с serial_type 10. Это значение используется только внутри assert() и не влияет на итоговый результат. |
| MaxPgcnt | Попытаться установить максимальное количество страниц базы данных P1 равным значению в P3. Не допускать, чтобы максимальное количество страниц было меньше текущего, и не изменять его значение, если P3==0. Записать максимальное количество страниц после изменения в регистр P2. |
| MemMax | P1 — регистр в корневом кадре этой виртуальной машины (корневой кадр отличается от текущего, если эта инструкция выполняется в подпрограмме). Установить в регистре P1 максимальное из его текущего значения и значения в регистре P2. Эта инструкция вызывает ошибку, если ячейка памяти изначально не содержит целое число. |
| Move | Переместить P3 значений из регистров P1..P1+P3-1 в регистры P2..P2+P3-1. В регистрах P1..P1+P3-1 после этого останется NULL. Диапазоны регистров P1..P1+P3-1 и P2..P2+P3-1 не должны пересекаться. Значение P3 должно быть не меньше 1. |
| Multiply | Умножить значение в регистре P1 на значение в регистре P2 и сохранить результат в регистре P3. Если один из входных операндов равен NULL, результатом будет NULL. |
| MustBeInt | Принудительно преобразовать значение в регистре P1 в целое число. Если значение в P1 не является целым числом и его нельзя преобразовать в целое без потери данных, немедленно перейти к P2 или, если P2==0, вызвать исключение SQLITE_MISMATCH. |
| Ne | Работает так же, как операция Eq, но переход выполняется, если операнды в регистрах P1 и P3 не равны. Дополнительные сведения см. в описании операции Eq. |
| NewRowid | Получить новый номер записи (также называемый «rowid»), используемый в качестве ключа таблицы. Этот номер записи ранее не использовался в качестве ключа в таблице базы данных, на которую указывает курсор P1. Новый номер записи записывается в регистр P2. Если P3>0, P3 — это регистр в корневом кадре данного VDBE, в котором хранится наибольший ранее сгенерированный номер записи. Новые номера записей не могут быть меньше этого значения. Когда это значение достигает максимума, возникает ошибка SQLITE_FULL. Регистр P3 обновляется сгенерированным номером записи. Этот механизм с P3 используется для реализации функции AUTOINCREMENT. |
| Next | Переместить курсор P1 так, чтобы он указывал на следующую пару ключ/данные в таблице или индексе. Если пар ключ/значение больше нет, перейти к следующей инструкции. Если перемещение курсора выполнено успешно, немедленно перейти к P2. Операция Next допустима только после операций SeekGT, SeekGE или Rewind, используемых для позиционирования курсора. За операциями SeekLT, SeekLE или Last операция Next следовать не может. Курсор P1 должен указывать на настоящую таблицу, а не на псевдотаблицу. P1 должен быть открыт до этой операции, иначе программа завершится с ошибкой сегментации. Значение P3 служит подсказкой для реализации b-tree. Если P3==1, это означает, что P1 — индекс SQL, а эту инструкцию можно было бы исключить, если бы индекс был уникальным. Обычно P3 равен 0. P3 всегда равен либо 0, либо 1. Если P5 положителен и выполняется переход, увеличивается счётчик событий с номером P5-1 в подготовленной инструкции. См. также: Prev |
| NoConflict | Если P4==0, в регистре P3 хранится blob, созданный операцией MakeRecord. Если P4>0, регистр P3 — первый из P4 регистров, образующих распакованную запись. Курсор P1 указывает на индекс b-tree. Если запись, определяемая P3 и P4, содержит значение NULL, немедленно перейти к P2. Если все поля записи не равны NULL, проверить, есть ли в индексе b-tree P1 строка с совпадающим префиксом ключа. Если совпадений нет, немедленно перейти к P2. Если совпадение есть, перейти к следующей инструкции, оставив курсор P1 указывающим на соответствующую строку. Эта операция похожа на NotFound, за исключением того, что переход всегда выполняется, если какая-либо часть входного ключа поиска равна NULL. После этой операции курсор переходит в состояние, в котором его нельзя перемещать ни в одном направлении. Иными словами, после неё операции Next и Prev не работают. |
| Noop | Ничего не делать. Перейти к следующей операции. |
| Not | Интерпретировать значение в регистре P1 как логическое. Записать логическое дополнение в регистр P2. Если значение в регистре P1 равно NULL, записать NULL в P2. |
| NotExists | P1 — индекс курсора, открытого на b-tree таблицы SQL (с целочисленными ключами). P3 — целочисленный rowid. Если в P1 нет записи с rowid P3, немедленно перейти к P2. Если P2 равен 0, вызвать ошибку SQLITE_CORRUPT. Если в P1 есть запись с rowid P3, оставить курсор указывающим на эту запись и перейти к следующей инструкции. Операция SeekRowid выполняет то же действие, но также допускает нецелое значение в регистре P3; в этом случае переход выполняется всегда. Эта операция требует, чтобы P3 всегда содержал целое число. Операция NotFound выполняет то же действие для индексных b-tree (с ключами из произвольного количества значений). После этой операции курсор переходит в состояние, в котором его нельзя перемещать ни в одном направлении. Иными словами, после этой операции не работают операции Next и Prev. См. также: Found, NotFound, NoConflict, SeekRowid |
| NotFound | Если P4==0, в регистре P3 хранится blob, созданный операцией MakeRecord. Если P4>0, регистр P3 — первый из P4 регистров, образующих распакованную запись. Курсор P1 указывает на индекс b-tree. Если запись, определяемая P3 и P4, не является префиксом ни одной записи в P1, выполняется переход к P2. Если в P1 есть запись с префиксом, совпадающим с записью P3/P4, управление переходит к следующей инструкции, а P1 остаётся указывать на соответствующую запись. После этой операции курсор переходит в состояние, в котором его нельзя перемещать ни в одном направлении. Иными словами, после неё операции Next и Prev не работают. См. также: Found, NotExists, NoConflict, IfNoHope |
| NotNull | Перейти к P2, если значение в регистре P1 не равно NULL. |
| Null | Записать NULL в регистр P2. Если P3 больше P2, также записать NULL в регистр P3 и во все регистры между P2 и P3. Если P3 меньше P2 (обычно P3 равен нулю), установить NULL только в регистре P2. Если значение P1 не равно нулю, также установить флаг MEM_Cleared, чтобы значения NULL не считались равными, даже если для Ne или Eq установлен SQLITE_NULLEQ. |
| NullRow | Переместить курсор P1 на строку NULL. Любые операции Column, выполняемые, когда курсор находится на строке NULL, всегда записывают NULL. Если курсор P1 ранее не был открыт, открыть его сейчас как специальный псевдокурсор, который всегда возвращает NULL для каждого столбца. |
| Offset | Сохранить в регистре r[P3] байтовое смещение в файле базы данных, соответствующее началу полезной нагрузки записи, на которую в данный момент указывает курсор P1. P2 — номер столбца для аргумента функции sqlite_offset(). Эта операция не использует P2 напрямую, но значение P2 используется генератором кода. Операнды P1, P2 и P3 этой операции совпадают с операндами Column. Эта операция доступна только в том случае, если SQLite скомпилирована с параметром -DSQLITE_ENABLE_OFFSET_SQL_FUNC. |
| OffsetLimit | Эта операция выполняет часто используемое вычисление, связанное с обработкой LIMIT и OFFSET. В r[P1] хранится счётчик ограничения. В r[P3] хранится счётчик смещения. Операция вычисляет суммарное значение LIMIT и OFFSET и сохраняет его в r[P2]. Вычисленное значение r[P2] — это общее количество строк, которые потребуется просмотреть для выполнения запроса. Если r[P3] равен нулю или отрицателен, это означает, что OFFSET отсутствует, и r[P2] устанавливается равным значению LIMIT — r[P1]. Если r[P1] равен нулю или отрицателен, это означает, что LIMIT отсутствует, и r[P2] устанавливается в -1. В остальных случаях r[P2] устанавливается в сумму r[P1] и r[P3]. |
| Once | При первом выполнении этой операции за каждый запуск программы байт-кода перейти к следующей инструкции. При втором и всех последующих выполнениях в рамках того же запуска выполнить Jump к P2. Программы верхнего уровня определяют первый запуск, сравнивая операнд P1 со значением операнда P1 в операции Init в начале программы. Если значения P1 различаются, перейти к следующей инструкции и присвоить P1 этой операции значение P1 из Init. Если значения P1 совпадают, выполнить переход. Для подпрограмм в VdbeFrame имеется битовая маска, определяющая, следует ли выполнять переход. Битовая маска необходима, поскольку при рекурсивных триггерах не работает приём с самомодифицирующимся кодом. Операнд P3 напрямую этой операцией не используется. Однако генератор кода использует P3 следующим образом: если эта операция является началом подпрограммы, использующей фильтр Блума, P3 будет регистром, в котором хранится этот фильтр Блума. Подробности реализации см. в исходном коде под меткой tag-202407032019. |
| OpenAutoindex | Эта операция работает так же, как OpenEphemeral. Ей присвоено другое имя, чтобы отличать её назначение. Таблицы, созданные этой операцией, используются для автоматически создаваемых временных индексов при соединениях. |
| OpenDup | Открыть новый курсор P1, указывающий на ту же временную таблицу, что и курсор P2. Курсор P2 должен быть открыт предшествующей операцией OpenEphemeral. Дублировать можно только временные курсоры. Дублирующиеся временные курсоры используются для самосоединений материализованных представлений. |
| OpenEphemeral | Открыть новый курсор P1 для временной таблицы. Курсор всегда открывается для чтения и записи, даже если основная база данных доступна только для чтения. Временная таблица автоматически удаляется при закрытии курсора. Если курсор P1 уже открыт для временной таблицы, таблица очищается (всё содержимое удаляется). P2 — количество столбцов во временной таблице. Если P4==0, курсор указывает на таблицу BTree, а если P4 не равен 0 — на индекс BTree. Если P4 не равен NULL, он указывает на структуру KeyInfo, определяющую формат ключей в индексе. Параметр P5 может быть маской флагов BTREE_*, определённых в btree.h. Эти флаги управляют работой b-tree. Флаги BTREE_OMIT_JOURNAL и BTREE_SINGLE добавляются автоматически. Если P3 положителен, значение reg[P3] слегка изменяется, чтобы его можно было использовать в качестве данных нулевой длины для Insert. Эта оптимизация позволяет избежать дополнительной операции Blob для инициализации этого регистра. |
| OpenPseudo | Открыть новый курсор, указывающий на фиктивную таблицу, содержащую одну строку данных. Содержимое этой строки — содержимое регистра памяти P2. Иными словами, курсор P1 становится псевдонимом содержимого MEM_Blob, находящегося в регистре P2. Псевдотаблица, созданная этой операцией, используется для хранения одной строки, выданной сортировщиком, чтобы строку можно было разделить на отдельные столбцы с помощью операции Column. Операция Column — единственная операция курсора, работающая с псевдотаблицей. P3 — количество полей в записях, которые будут храниться в псевдотаблице. Если P2 равен 0 или отрицателен, псевдокурсор будет возвращать NULL для каждого столбца. |
| OpenRead | Открыть курсор только для чтения для таблицы базы данных, корневая страница которой в файле базы данных имеет номер P2. Файл базы данных определяется значением P3. P3==0 означает основную базу данных, P3==1 — базу данных, используемую для временных таблиц, а P3>1 — соответствующую подключённую базу данных. Назначить новому курсору идентификатор P1. Значения P1 не обязаны быть последовательными, но все они должны быть небольшими целыми числами. Отрицательное значение P1 является ошибкой. Допустимые биты P5:
Значение P4 может быть целым числом (P4_INT32) или указателем на структуру KeyInfo (P4_KEYINFO). Если это указатель на объект KeyInfo, открываемая таблица должна быть индексным B-деревом, содержимое и последовательность сортировки которого определяются объектом KeyInfo. В противном случае, если P4 — целочисленное значение, открываемая таблица должна быть табличным B-деревом с количеством столбцов не меньше значения P4. |
| OpenWrite | Открыть курсор для чтения и записи с именем P1 для таблицы или индекса, корневая страница которого имеет номер P2 (или номер корневой страницы хранится в регистре P2, если в P5 установлен бит OPFLAG_P2ISREG — см. ниже). Значение P4 может быть целым числом (P4_INT32) или указателем на структуру KeyInfo (P4_KEYINFO). Если это указатель на объект KeyInfo, открываемая таблица должна быть индексным B-деревом, содержимое и последовательность сортировки которого определяются объектом KeyInfo. В противном случае, если P4 — целочисленное значение, открываемая таблица должна быть табличным B-деревом с количеством столбцов не меньше значения P4. Допустимые биты P5:
Эта инструкция работает как OpenRead, но открывает курсор в режиме чтения и записи. |
| Or | Вычислить логическое ИЛИ значений в регистрах P1 и P2 и сохранить результат в регистре P3. Если P1 или P2 не равен нулю (истина), результатом будет 1 (истина), даже если другое входное значение равно NULL. NULL и ложь, а также два значения NULL дают результат NULL. |
| Pagecount | Записать текущее количество страниц базы данных P1 в ячейку памяти P2. |
| Param | Эта операция присутствует только во вложенных программах, вызываемых инструкцией Program. Скопировать значение, находящееся в ячейке памяти вызывающего (родительского) фрейма, в ячейку P2 адресного пространства текущего фрейма. Это используется триггерными программами для доступа к значениям new.* и old.*. Адрес ячейки в родительском фрейме определяется сложением значения аргумента P1 этой операции со значением аргумента P1 вызывающей инструкции Program. |
| ParseSchema | Прочитать и разобрать все записи из таблицы схемы базы данных P1, соответствующие предложению WHERE в P4. Если P4 — нулевой указатель, схема P1 разбирается заново целиком. Эта операция вызывает анализатор для создания новой виртуальной машины, а затем запускает её. Таким образом, это реентерабельная операция. |
| Permutation | Задать перестановку, используемую оператором Compare в следующей инструкции. Перестановка хранится в операнде P4. Перестановка действительна только для следующей операции, которой должна быть Compare с установленным битом OPFLAG_PERMUTE в P5. Первое целое число в массиве целых чисел P4 задаёт длину массива и не входит в перестановку. |
| Prev | Переместить курсор P1 назад, чтобы он указывал на предыдущую пару ключ/данные в своей таблице или индексе. Если предыдущей пары ключ/значение нет, перейти к следующей инструкции. Если же перемещение курсора назад выполнено успешно, немедленно перейти к P2. Операция Prev допустима только после операций SeekLT, SeekLE или Last, используемых для позиционирования курсора. За SeekGT, SeekGE или Rewind операция Prev следовать не может. Курсор P1 должен относиться к настоящей таблице, а не к псевдотаблице. Если P1 не открыт, поведение не определено. Значение P3 является подсказкой для реализации B-дерева. Если P3==1, это означает, что P1 — индекс SQL, и эту инструкцию можно было бы опустить, если бы индекс был уникальным. Обычно P3 равен 0. P3 всегда равен либо 0, либо 1. Если P5 положителен и выполняется переход, счётчик событий с номером P5-1 в подготовленной инструкции увеличивается. |
| Program | Выполнить программу триггера, переданную в P4 (тип P4_SUBPROGRAM). P1 содержит адрес ячейки памяти, в которой находится первая ячейка массива значений, используемого как аргументы вложенной программы. P2 содержит адрес перехода, если вложенная программа выдаёт исключение IGNORE с помощью функции RAISE(). P2 может быть равен нулю, если исключение IGNORE возникнуть не может. Регистр P3 содержит адрес ячейки памяти в этой (родительской) виртуальной машине, используемой для выделения памяти, необходимой вложенной виртуальной машине во время выполнения. P4 — указатель на виртуальную машину, содержащую программу триггера. Если P5 не равен нулю, рекурсивный вызов программы разрешён. |
| PureFunc | Вызвать пользовательскую функцию (P4 — указатель на объект sqlite3_context, содержащий указатель на вызываемую функцию) с аргументами из регистра P2 и следующих за ним регистров. Количество аргументов указано в объекте sqlite3_context, на который указывает P4. Результат функции сохраняется в регистре P3. Регистр P3 не должен совпадать с одним из входных регистров функции. P1 — 32-разрядная битовая маска, указывающая, какие аргументы функции были определены как константы во время компиляции. Если первый аргумент является константой, устанавливается бит 0 в P1. Это используется для определения того, можно ли безопасно сохранять метаданные, связанные с аргументом пользовательской функции через API sqlite3_set_auxdata(), до следующего вызова этой операции. Эта операция работает точно так же, как Function. Отличается только её имя. Она используется в случаях, когда функция должна быть чисто недетерминированной. Некоторые встроенные функции даты и времени могут быть детерминированными или недетерминированными в зависимости от аргументов. При недетерминированном использовании такие функции проверяют, были ли они вызваны с помощью PureFunc, а не Function, и в этом случае выдают ошибку. |
| ReadCookie | Прочитать cookie с номером P3 из базы данных P1 и записать его в регистр P2. P3==1 — версия схемы. P3==2 — формат базы данных. P3==3 — рекомендуемый размер кеша pager и так далее. P1==0 — файл основной базы данных, а P1==1 — файл базы данных, используемый для хранения временных таблиц. Перед выполнением этой инструкции для базы данных должна быть установлена блокировка чтения (должна быть начата транзакция или открыт курсор). |
| Real | P4 — указатель на 64-разрядное число с плавающей точкой. Записать это значение в регистр P2. |
| RealAffinity | Если в регистре P1 хранится целое число, преобразовать его в вещественное. Эта операция используется при извлечении данных из столбца с аффинностью REAL. Для экономии места такие значения столбца могут храниться как целые числа, но после извлечения они должны иметь вещественный тип. |
| ReleaseReg | Освободить регистры. Содержимое регистров после завершения этой операции становится ненадёжным. Освобождаются P2 регистров, начиная с P1, за исключением регистра P1+ii, если установлен бит ii в P3. Иными словами, P3 — маска регистров, которые следует сохранить. Освобождение регистра очищает указатель Mem.pScopyFrom. Это означает, что если содержимое освобождённого регистра было задано с помощью SCopy, изменение значения исходного регистра для SCopy больше не будет вызывать ошибку assert в sqlite3VdbeMemAboutToChange(). Если установлен P5, тип всех освобождённых регистров устанавливается в MEM_Undefined, поэтому любая последующая попытка прочитать освобождённый регистр (до его повторной инициализации) вызовет ошибку assert. P5 следует устанавливать при каждом вызове этой операции. Однако генератор кода в некоторых местах освобождает регистры до их использования, исходя из (верного) предположения, что регистры не будут переназначены для других целей до использования и поэтому их безопасно освободить. Эта операция доступна только в сборках для тестирования и отладки. В релизных сборках она не генерируется. Назначение этой операции — помочь проверить сгенерированный байт-код. Она фактически не участвует в вычислении результата. |
| Remainder | Вычислить остаток от деления целого числа в регистре P2 на значение в регистре P1 и сохранить результат в регистре P3. Если значение в регистре P1 равно нулю, результат — NULL. Если любой из операндов равен NULL, результат — NULL. |
| ReopenIdx | Операция ReopenIdx работает как OpenRead, но сначала проверяет, открыт ли уже курсор P1 для того же B-дерева; если открыт, эта операция ничего не делает. Иными словами, если курсор уже открыт, повторно открывать его не нужно. Операцию ReopenIdx можно использовать только с P5==0 или P5==OPFLAG_SEEKEQ и объектом P4 типа P4_KEYINFO. Кроме того, значение P3 должно совпадать со значением P3 во всех остальных операциях ReopenIdx или OpenRead для того же номера курсора. Допустимые биты P5: |
| ResetCount | Значение счётчика изменений копируется в счётчик изменений дескриптора базы данных (возвращаемый последующими вызовами sqlite3_changes()). Затем внутренний счётчик изменений виртуальной машины сбрасывается в 0. Это используется триггерными программами. |
| ResetSorter | Удалить всё содержимое из временной таблицы или сортировщика, открытого на курсоре P1. Эта операция работает только с курсорами, используемыми для сортировки и открытыми с помощью OpenEphemeral или SorterOpen. |
| ResultRow | Регистры P1—P1+P2-1 содержат одну строку результатов. Эта операция приводит к завершению вызова sqlite3_step() с кодом возврата SQLITE_ROW и настраивает структуру sqlite3_stmt для доступа к значениям r(P1)..r(P1+P2-1) как к строке результата. |
| Return | Перейти по адресу, хранящемуся в регистре P1. Если P1 — регистр адреса возврата, это приводит к возврату из подпрограммы. Если P3 равен 1, переход выполняется только в том случае, если в регистре P1 хранится целочисленное значение; в противном случае выполнение продолжается со следующей операции, а Return ничего не делает. Если P3 равен 0, в регистре P1 должно храниться целое число, иначе возникает ошибка assert(). Для этой операции следует задавать P3 равным 1 при использовании вместе с BeginSubrtn и равным 0 во всех остальных случаях. Значение в регистре P1 этой операцией не изменяется. Движок байт-кода не использует P2. Однако, если P2 положителен и меньше текущего адреса, форматтер вывода "EXPLAIN" в CLI добавит отступ для всех операций от операции P2 до текущей Return, не включая её. P2 должен указывать на первую операцию подпрограммы, из которой выполняется возврат. Таким образом, значение P2 служит подсказкой для отступов в байт-коде. См. tag-20220407a в wherecode.c и shell.c. |
| Rewind | При следующем использовании инструкции Rowid, Column или Next для P1 будет обработана первая запись таблицы или индекса базы данных. Если таблица или индекс пусты, немедленно перейти к P2. Если таблица или индекс не пусты, перейти к следующей инструкции. Если P2 равен нулю, это утверждение о том, что таблица P1 никогда не бывает пустой, а значит, переход никогда не будет выполнен. Эта операция настраивает курсор для перемещения вперёд, от начала к концу. Иными словами, курсор настроен на использование Next, а не Prev. |
| RowCell | P1 и P2 — открытые курсоры. Оба должны быть открыты для таблиц одного типа — intkey или index. Эта операция используется при копировании текущей строки из P2 в P1. Если курсоры открыты для таблиц intkey, регистр P3 содержит rowid, который нужно использовать для новой записи в P1. Если курсоры открыты для индексных таблиц, P3 не используется. За этой операцией должна следовать операция Insert или InsertIdx с установленным флагом OPFLAG_PREFORMAT, чтобы завершить вставку. |
| RowData | Записать в регистр P2 полное содержимое строки, на которую в данный момент указывает курсор P1. Данные не интерпретируются. Они просто копируются в регистр P2 точно в том виде, в каком хранятся в файле базы данных. Если курсор P1 указывает на индекс, содержимым является ключ строки. Если курсор P2 указывает на таблицу, извлекается содержимое строки. Курсор P1 должен указывать на допустимую строку (не на строку NULL) настоящей таблицы, а не псевдотаблицы. Если P3!=0, эта операция может создать временный указатель на страницу базы данных. Это означает, что содержимое выходного регистра станет недействительным при любом перемещении курсора, включая перемещения других курсоров, которые «сохраняют» текущую позицию курсора, чтобы записать данные в ту же таблицу. Если P3==0, данные копируются в память. P3!=0 быстрее, но P3==0 безопаснее. Если P3!=0, содержимое регистра P2 нельзя использовать в OP_Result, и любая операция OP_Result сделает его недействительным. Содержимое регистра P2 также становится недействительным при выполнении таких операций, как Function, или при любом использовании другого курсора, указывающего на ту же таблицу. |
| Rowid | Сохранить в регистре P2 целое число — ключ записи таблицы, на которую в данный момент указывает P1. P1 может быть обычной таблицей или виртуальной таблицей. Раньше для виртуальных таблиц использовалась отдельная операция OP_VRowid, но теперь эта операция работает с таблицами обоих типов. |
| RowSetAdd | Вставить целочисленное значение из регистра P2 в объект RowSet, хранящийся в регистре P1. Если P2 не является целым числом, возникает ошибка assert. |
| RowSetRead | Извлечь наименьшее значение из объекта RowSet в P1 и поместить его в регистр P3. Если объект RowSet P1 изначально пуст, оставить P3 без изменений и перейти к инструкции P2. |
| RowSetTest | Предполагается, что в регистре P3 хранится 64-разрядное целочисленное значение. Если регистр P1 содержит объект RowSet и этот объект содержит значение из P3, перейти к P2. В противном случае вставить целое число из P3 в RowSet и перейти к следующей операции. Объект RowSet оптимизирован для случая, когда наборы целых чисел добавляются отдельными этапами, причём в каждом наборе нет дубликатов. Каждый набор идентифицируется уникальным значением P4. Для первого набора должно выполняться P4==0, для последнего — P4==-1, а для всех остальных — P4>0. Это позволяет выполнять оптимизации: (a) при P4==0 проверять наличие P3 в RowSet не нужно, поскольку гарантируется, что такого значения там нет; (b) при P4==-1 вставлять значение не нужно, поскольку оно никогда не будет проверяться; (c) при вставке значения из набора X не нужно искать, не было ли это же значение уже вставлено в рамках набора X (проверять нужно только, не было ли оно ранее вставлено в рамках другого набора). |
| Savepoint | Открыть точку сохранения с именем, заданным параметром P4, освободить её или выполнить откат к ней — в зависимости от значения P1. Чтобы открыть новую точку сохранения, задать P1==0 (SAVEPOINT_BEGIN). Чтобы освободить (зафиксировать) существующую точку сохранения, задать P1==1 (SAVEPOINT_RELEASE). Чтобы выполнить откат к существующей точке сохранения, задать P1==2 (SAVEPOINT_ROLLBACK). |
| SCopy | Создать поверхностную копию регистра P1 в регистр P2. Эта инструкция создаёт поверхностную копию значения. Если значение является строкой или BLOB-объектом, копия содержит только указатель на исходное значение, поэтому при изменении исходного значения изменится и копия. Более того, если исходное значение освобождено, копия становится недействительной. Следовательно, программа должна гарантировать, что исходное значение не изменится в течение срока существования копии. Для создания полной копии используйте Copy. |
| SeekEnd | Расположить курсор P1 в конце B-дерева, чтобы добавить в него новую запись. Предполагается, что курсор используется только для добавления записей, поэтому, если он действителен, он уже должен указывать на конец B-дерева и перемещать его не нужно. |
| SeekGE | Если курсор P1 указывает на таблицу SQL (B-дерево с целочисленными ключами), использовать значение в регистре P3 в качестве ключа. Если курсор P1 указывает на индекс SQL, P3 — первый регистр в массиве из P4 регистров, используемых как распакованный ключ индекса. Переместить курсор P1 так, чтобы он указывал на наименьшую запись, значение которой больше или равно значению ключа. Если записей со значением больше или равным ключу нет и P2 не равен нулю, перейти к P2. Если курсор P1 был открыт с флагом OPFLAG_SEEKEQ, эта операция либо установит его на запись, точно соответствующую ключу, либо выполнит переход к P2. При курсоре с флагом OPFLAG_SEEKEQ за этой операцией должна следовать операция IdxLE с теми же аргументами. Если эта операция выполнена успешно, операция IdxGT будет пропущена, но в последующих итерациях цикла она будет использоваться. Флаг OPFLAG_SEEKEQ служит подсказкой для уровня B-дерева о том, что выполняется поиск на равенство. Эта операция настраивает курсор для перемещения вперёд, от начала к концу. Иными словами, курсор настроен на использование Next, а не Prev. |
| SeekGT | Если курсор P1 указывает на таблицу SQL (B-дерево с целочисленными ключами), использовать значение в регистре P3 в качестве ключа. Если курсор P1 указывает на индекс SQL, P3 — первый регистр в массиве из P4 регистров, используемых как распакованный ключ индекса. Переместить курсор P1 так, чтобы он указывал на наименьшую запись, значение которой больше значения ключа. Если записей со значением больше ключа нет и P2 не равен нулю, перейти к P2. Эта операция настраивает курсор для перемещения вперёд, от начала к концу. Иными словами, курсор настроен на использование Next, а не Prev. |
| SeekHit | При необходимости увеличить или уменьшить значение seekHit для курсора P1 так, чтобы оно было не меньше P2 и не больше P3. Целое число seekHit обозначает максимальное количество терминов индекса, для которых известно, что есть хотя бы одно совпадение. Если значение seekHit меньше общего числа терминов равенства при поиске по индексу, может выполниться операция IfNoHope, чтобы проверить, можно ли досрочно прервать цикл IN и тем самым избежать лишней работы. Это часть оптимизации досрочного завершения IN. P1 должен быть допустимым курсором B-дерева. |
| SeekLE | Если курсор P1 указывает на таблицу SQL (B-дерево с целочисленными ключами), использовать значение в регистре P3 в качестве ключа. Если курсор P1 указывает на индекс SQL, P3 — первый регистр в массиве из P4 регистров, используемых как распакованный ключ индекса. Переместить курсор P1 так, чтобы он указывал на наибольшую запись, значение которой меньше или равно значению ключа. Если записей со значением меньше или равным ключу нет и P2 не равен нулю, перейти к P2. Эта операция настраивает курсор для перемещения в обратном порядке, от конца к началу. Иными словами, курсор настроен на использование Prev, а не Next. Если курсор P1 был открыт с флагом OPFLAG_SEEKEQ, эта операция либо установит его на запись, точно соответствующую ключу, либо выполнит переход к P2. При курсоре с флагом OPFLAG_SEEKEQ за этой операцией должна следовать операция IdxLE с теми же аргументами. Если эта операция выполнена успешно, операция IdxGE будет пропущена, но в последующих итерациях цикла она будет использоваться. Флаг OPFLAG_SEEKEQ служит подсказкой для уровня B-дерева о том, что выполняется поиск на равенство. |
| SeekLT | Если курсор P1 указывает на таблицу SQL (B-дерево с целочисленными ключами), использовать значение в регистре P3 в качестве ключа. Если курсор P1 указывает на индекс SQL, P3 — первый регистр в массиве из P4 регистров, используемых как распакованный ключ индекса. Переместить курсор P1 так, чтобы он указывал на наибольшую запись, значение которой меньше значения ключа. Если записей со значением меньше ключа нет и P2 не равен нулю, перейти к P2. Эта операция настраивает курсор для перемещения в обратном порядке, от конца к началу. Иными словами, курсор настроен на использование Prev, а не Next. |
| SeekRowid | P1 — индекс курсора, открытого для B-дерева таблицы SQL (с целочисленными ключами). Если в регистре P3 не содержится целое число или в P1 нет записи с rowid P3, немедленно перейти к P2. Если P2 равен 0, выдать ошибку SQLITE_CORRUPT. Если в P1 есть запись с rowid P3, оставить курсор указывающим на эту запись и перейти к следующей инструкции. Операция NotExists выполняет ту же задачу, но для NotExists гарантируется, что в регистре P3 содержится целочисленное значение. При использовании этой операции в регистре P3 может не быть целого числа. Операция NotFound выполняет ту же задачу для индексных B-деревьев (с произвольными составными ключами). Эта операция оставляет курсор в состоянии, в котором его нельзя перемещать ни в одном направлении. Иными словами, после этой операции не будут работать операции Next и Prev. См. также: Found, NotFound, NoConflict, SeekRowid |
| SeekScan | Эта операция является префиксной операцией для SeekGE. Иными словами, за ней должна непосредственно следовать SeekGE. Это ограничение проверяется инструкциями assert(). Эта операция использует операнды P1—P4 следующей операции SeekGE. Далее операнды следующей операции SeekGE обозначаются как SeekOP.P1—SeekOP.P4. Также используются только операнды P1, P2 и P5 этой операции, обозначаемые как This.P1, This.P2 и This.P5. Эта операция помогает оптимизировать операторы IN по многоколоночному индексу, когда оператор IN применяется к более поздним терминам индекса: она позволяет избежать лишних поисков в B-дереве, заменяя их переходами к следующей строке B-дерева. Правильный результат будет получен и в том случае, если эту операцию опустить или если она ничего не делает. Операнды SeekGE.P3 и SeekGE.P4 задают распакованный ключ — требуемую запись, на которую должен указывать курсор SeekGE.P1. Назовём эту строку SeekGE.P3/P4 «целевой». Если курсор SeekGE.P1 в данный момент не указывает на допустимую строку, эта операция ничего не делает и управление переходит к SeekGE. Если курсор SeekGE.P1 указывает на допустимую строку, эта строка может быть целевой, находиться рядом и немного перед целевой строкой или располагаться после неё. Если курсор находится перед целевой строкой, эта операция пытается переместить его на целевую строку или за неё, вызывая sqlite3BtreeStep() для курсора от 1 до This.P1 раз. Параметр This.P5 — это флаг, указывающий, что делать, если курсор укажет на допустимую строку, расположенную после целевой. Если This.P5 равен false (0), выполняется переход к SeekGE.P2. Если This.P5 равен true (не нулю), выполняется переход к This.P2. Случай P5==0 возникает, когда справа от ограничения IN нет ограничений неравенства. Переход к SeekGE.P2 завершает цикл. Случай P5!=0 возникает, когда справа от оператора IN есть ограничения неравенства. В таком случае This.P2 указывает либо непосредственно на операцию IdxGT или IdxGE, либо на подготовительный код перед ней; эта операция проверяет завершение цикла. Возможные результаты выполнения этой операции:
|
| Sequence | Найти следующий доступный номер последовательности для курсора P1. Записать этот номер в регистр P2. После выполнения этой инструкции номер последовательности курсора увеличивается. |
| SequenceTest | P1 — курсор сортировщика. Если счётчик последовательности в данный момент равен нулю, перейти к P2. Независимо от того, выполнен переход или нет, увеличить значение последовательности. |
| SetCookie | Записывает целочисленное значение P3 в cookie с номером P2 базы данных P1. P2==1 — версия схемы. P2==2 — формат базы данных. P2==3 — рекомендуемый размер кэша pager и так далее. P1==0 — основной файл базы данных, а P1==1 — файл базы данных, используемый для хранения временных таблиц. Перед выполнением этого опкода необходимо начать транзакцию. Если P2 — это cookie SCHEMA_VERSION (cookie с номером 1), внутренняя версия схемы устанавливается в P3-P5. В инструкции "PRAGMA schema_version=N" для P5 задано значение 1, поэтому внутренняя версия схемы будет отличаться от версии схемы базы данных, что приведёт к сбросу схемы. |
| SetSubtype | Устанавливает для подтипа регистра P2 целочисленное значение из регистра P1. Если P1 равен NULL, очищает подтип в p2. |
| ShiftLeft | Сдвигает целочисленное значение в регистре P2 влево на количество битов, заданное целым числом в регистре P1. Сохраняет результат в регистре P3. Если любой из входных аргументов равен NULL, результат равен NULL. |
| ShiftRight | Сдвигает целочисленное значение в регистре P2 вправо на количество битов, заданное целым числом в регистре P1. Сохраняет результат в регистре P3. Если любой из входных аргументов равен NULL, результат равен NULL. |
| SoftNull | Устанавливает для регистра P1 значение NULL, как его видит инструкция MakeRecord, но не освобождает память строк или blob-объектов, связанную с регистром, поэтому, если значение было строкой или blob-объектом, ранее скопированным с помощью SCopy, копии останутся действительными. |
| Sort | Этот опкод выполняет ровно то же действие, что и Rewind, за исключением того, что он увеличивает недокументированную глобальную переменную, используемую для тестирования. Сортировка выполняется путём записи записей в индекс сортировки, последующего перемещения к его началу и чтения записей от начала до конца. Для перемещения к началу мы используем опкод Sort вместо Rewind, чтобы увеличить глобальную переменную и дать регрессионным тестам возможность определить, правильно ли оптимизатор исключает сортировки. |
| SorterCompare | P1 — курсор сортировщика. Эта инструкция сравнивает префикс blob-записи в регистре P3 с префиксом записи, на которую в данный момент указывает курсор сортировщика. Сравниваются только первые P4 полей r[P3] и записи сортировщика. Если в P3 или сортировщике одно из значимых полей содержит NULL (не считая последних P4 полей, которые игнорируются), сравнение считается равным. Если записи равны, перейти к следующей инструкции. Если они различаются, выполнить переход к P2 с помощью Jump. |
| SorterData | Записывает текущие данные сортировщика для курсора сортировщика P1 в регистр P2. Затем очищает кэш заголовков столбцов курсора P3. Этот опкод обычно используется для перемещения записи из сортировщика в регистр, который служит источником для курсора псевдотаблицы, созданного с помощью OpenPseudo. Этот курсор псевдотаблицы идентифицируется параметром P3. Очистка кэша столбцов P3 в рамках этого опкода избавляет от необходимости выполнять отдельную инструкцию NullRow для очистки этого кэша. |
| SorterInsert | В регистре P2 находится ключ индекса SQL, созданный с помощью инструкций MakeRecord. Этот опкод записывает ключ в сортировщик P1. Данные записи отсутствуют. |
| SorterNext | Этот опкод работает так же, как Next, за исключением того, что P1 должен быть объектом сортировщика, для которого был вызван опкод SorterSort. Этот опкод перемещает курсор к следующей отсортированной записи или выполняет переход к P2, если отсортированных записей больше нет. |
| SorterOpen | Этот опкод работает как OpenEphemeral, но открывает временный индекс, специально предназначенный для сортировки больших таблиц с помощью алгоритма внешней сортировки слиянием. Если аргумент P3 не равен нулю, это означает, что сортировщик может считать достаточной стабильную сортировку по первым P3 полям каждого ключа для получения требуемых результатов. |
| SorterSort | После вставки всех записей в объект Sorter, идентифицируемый P1, вызовите этот опкод для выполнения сортировки. Если записей для сортировки нет, выполните переход к P2 с помощью Jump. Этот опкод является псевдонимом Sort и Rewind, используемым для объектов Sorter. |
| SqlExec | Выполняет инструкцию или инструкции SQL, указанные в строке P4. Параметр P1 — это битовая маска параметров: 0x0001 Отключает обратные вызовы Auth и Trace на время выполнения инструкций из P4. 0x0002 Устанавливает db->nAnalysisLimit в P2 на время выполнения инструкций из P4. |
| String | Строковое значение P4 длиной P1 (в байтах) сохраняется в регистре P2. Если P3 не равен нулю и содержимое регистра P3 равно P5, тип данных регистра P2 преобразуется в BLOB. Содержимое представляет собой ту же последовательность байтов, которая лишь интерпретируется как BLOB, а не как строка, как если бы было выполнено приведение типа CAST. Иными словами: if( P3!=0 and reg[P3]==P5 ) reg[P2] := CAST(reg[P2] as BLOB) |
| String8 | P4 указывает на строку UTF-8, завершённую нулевым символом. Перед первым выполнением этот опкод преобразуется в опкод String. Во время преобразования вычисляется длина строки P4 и сохраняется в параметре P1. |
| Subtract | Вычитает значение в регистре P1 из значения в регистре P2 и сохраняет результат в регистре P3. Если любой из входных аргументов равен NULL, результат равен NULL. |
| TableLock | Получает блокировку определённой таблицы. Эта инструкция используется только при включённой функции общего кэша. P1 — индекс базы данных в sqlite3.aDb[], для которой приобретается блокировка. Если P3==0, устанавливается блокировка на чтение, а если P3==1 — блокировка на запись. P2 содержит корневую страницу блокируемой таблицы. P4 содержит указатель на имя блокируемой таблицы. Оно используется только для формирования сообщения об ошибке, если получить блокировку не удаётся. |
| Trace | Записывает P4 в вывод трассировки инструкции, если трассировка инструкций включена. Операнд P1 должен быть равен 0x7fffffff, а P2 должен быть положительным. |
| Transaction | Начинает транзакцию для базы данных P1, если транзакция ещё не активна. Если P2 не равен нулю, начинается транзакция на запись или, если транзакция на чтение уже активна, она преобразуется в транзакцию на запись. Если P2 равен нулю, начинается транзакция на чтение. Если P2 равен 2 или больше, начинается эксклюзивная транзакция. P1 — индекс файла базы данных, для которого начинается транзакция. Индекс 0 соответствует основному файлу базы данных, а индекс 1 — файлу, используемому для временных таблиц. Индексы 2 и выше используются для присоединённых баз данных. Если начинается транзакция на запись и флаг Vdbe.usesStmtJournal имеет значение true (этот флаг устанавливается, если Vdbe может изменить более одной строки и вызвать исключение ABORT), также может быть открыта транзакция инструкции. Точнее, транзакция инструкции открывается тогда и только тогда, когда соединение с базой данных в данный момент не находится в режиме autocommit или имеются другие активные инструкции. Транзакция инструкции позволяет отменить изменения, внесённые этим VDBE, после ошибки, не отменяя всю транзакцию. Если ошибки не возникнет, транзакция инструкции будет автоматически зафиксирована при остановке VDBE. Если P5!=0, этот опкод также проверяет cookie схемы по значению P3, а счётчик поколений схемы — по значению P4. Значение cookie меняется при каждом изменении схемы базы данных. Эта операция позволяет обнаружить изменение cookie и необходимость повторного чтения схемы текущим процессом. Если cookie схемы в P3 отличается от cookie схемы в заголовке базы данных или счётчик поколений схемы в P4 отличается от текущего счётчика поколений, возникает ошибка SQLITE_SCHEMA и выполнение останавливается. Затем функция-обёртка sqlite3_step() может заново подготовить инструкцию и выполнить её повторно с самого начала. |
| TypeCheck | Применяет affinity к диапазону из P2 регистров, начиная с P1. Значения affinity берутся из объекта Table в P4. Если какое-либо значение нельзя привести к нужному типу, возникает ошибка. Если P3==0, проверка столбцов VIRTUAL пропускается. Если P3==1, пропускается проверка всех генерируемых столбцов — как VIRTUAL, так и STORED. Если P3>=2, проверяется только столбец с номером P3-2 в таблице (это будет столбец VIRTUAL) на соответствие значению в reg[P1]. В этом случае P2 будет равен 1. Этот опкод похож на Affinity, но принудительно задаёт тип регистра в соответствии с типом столбца Table. Это используется для реализации «строгой affinity». Столбцы GENERATED ALWAYS AS ... STATIC проверяются, только если P3 равен нулю. Если P3 не равен нулю, проверка типов для статических генерируемых столбцов не выполняется. Виртуальные столбцы вычисляются во время выполнения запроса, поэтому они никогда не проверяются. Предварительные условия:
Если какое-либо предварительное условие не выполнено, возникает ошибка утверждения. |
| Vacuum | Выполняет вакуумирование всей базы данных P1. P1 равен 0 для "main" и 2 или больше для присоединённой базы данных. Базу данных "temp" вакуумировать нельзя. Если P2 не равен нулю, это регистр, содержащий строку с именем файла, в который следует записать результат вакуумирования. Если P2 равен нулю, вакуумирование перезаписывает исходную базу данных. |
| Variable | Переносит значения связанного параметра P1 в регистр P2 |
| VBegin | P4 может быть указателем на структуру sqlite3_vtab. Если это так, вызывается метод xBegin этой таблицы. Кроме того, независимо от того, задан ли P4, проверяется, не вызван ли этот опкод из обратного вызова метода xSync() виртуальной таблицы. Если это так, код ошибки устанавливается в SQLITE_LOCKED. |
| VCheck | P4 — указатель на объект Table, представляющий виртуальную таблицу в схеме P1, которая поддерживает метод xIntegrity(). Этот опкод запускает метод xIntegrity() для этой виртуальной таблицы, используя P3 в качестве целочисленного аргумента. Если метод сообщает об ошибке, к сообщению об ошибке добавляется в начало имя таблицы, а затем сообщение сохраняется в P2. Если ошибок не обнаружено, для регистра P2 устанавливается значение NULL. |
| VColumn | Сохраняет в регистре P3 значение столбца с номером P2 текущей строки виртуальной таблицы курсора P1. Если опкод VColumn используется для получения значения неизменяемого столбца во время операции UPDATE, значение P5 равно OPFLAG_NOCHNG. В результате функция sqlite3_vtab_nochange() вернёт true внутри метода xColumn реализации виртуальной таблицы. Столбец P5 также может содержать другие биты (OPFLAG_LENGTHARG или OPFLAG_TYPEOFARG), но опкод VColumn их не использует. |
| VCreate | P2 — регистр, содержащий имя виртуальной таблицы в базе данных P1. Вызывает метод xCreate этой таблицы. |
| VDestroy | P4 — имя виртуальной таблицы в базе данных P1. Вызывает метод xDestroy этой таблицы. |
| VFilter | P1 — курсор, открытый с помощью VOpen. P2 — адрес, на который следует перейти, если отфильтрованный набор результатов пуст. P4 имеет значение NULL или содержит строку, сформированную методом xBestIndex модуля. Интерпретация строки P4 определяется реализацией модуля. Этот опкод вызывает метод xFilter виртуальной таблицы, указанной в P1. Целочисленный параметр плана запроса для xFilter хранится в регистре P3. В регистре P3+1 хранится параметр argc, передаваемый методу xFilter. Регистры P3+2..P3+1+argc содержат argc дополнительных параметров, передаваемых в xFilter как argv. При передаче в xFilter регистр P3+2 становится argv[0]. Если после фильтрации набор результатов окажется пустым, выполняется переход к P2. |
| VInitIn | Устанавливает в регистре P2 указатель на объект ValueList для курсора P1 с регистром кэша P3 и выходным регистром P3+1. Этот объект ValueList можно использовать в качестве первого аргумента для sqlite3_vtab_in_first() и sqlite3_vtab_in_next(), чтобы извлечь все значения, хранящиеся в курсоре P1. Регистр P3 используется для хранения значений, возвращаемых sqlite3_vtab_in_first() и sqlite3_vtab_in_next(). |
| VNext | Перемещает виртуальную таблицу P1 к следующей строке набора результатов и выполняет переход к инструкции P2. Если виртуальная таблица достигла конца набора результатов, выполнение продолжается со следующей инструкции. |
| VOpen | P4 — указатель на объект виртуальной таблицы, структуру sqlite3_vtab. P1 — номер курсора. Этот опкод открывает курсор виртуальной таблицы и сохраняет его в P1. |
| VRename | P4 — указатель на объект виртуальной таблицы, структуру sqlite3_vtab. Этот опкод вызывает соответствующий метод xRename. Значение в регистре P1 передаётся методу xRename как аргумент zName. |
| VUpdate | P4 — указатель на объект виртуальной таблицы, структуру sqlite3_vtab. Этот опкод вызывает соответствующий метод xUpdate. Для вызова xUpdate значения P2 передаются в смежных ячейках памяти, начиная с P3. Значение в регистре (P3+P2-1) соответствует элементу p2 массива argv, передаваемого в xUpdate. Метод xUpdate выполнит DELETE, INSERT или обе операции. Элемент argv[0] (соответствующий ячейке памяти P3) — это rowid строки, которую нужно удалить. Если argv[0] равен NULL, удаление не выполняется. Элемент argv[1] — это rowid новой строки. Он может быть равен NULL, чтобы виртуальная таблица сама выбрала rowid для новой строки. Последующие элементы массива содержат значения столбцов новой строки. Если P2==1, вставка не выполняется. argv[0] — это rowid строки, которую нужно удалить. P1 — логический флаг. Если он установлен в true и вызов xUpdate завершился успешно, значение, возвращаемое sqlite3_last_insert_rowid(), устанавливается в rowid только что вставленной строки. P5 — действия при ошибке (OE_Replace, OE_Fail, OE_Ignore и т. д.), применяемые при нарушении ограничения во время вставки или обновления. |
| Yield | Меняет местами счётчик команд и значение в регистре P1. Это приводит к передаче управления сопрограмме. Если сопрограмма, запущенная этой инструкцией, завершается с помощью Yield или Return, выполнение продолжается со следующей инструкции. Если же сопрограмма, запущенная этой инструкцией, завершается с помощью EndCoroutine, выполняется переход к P2, а не к следующей инструкции. См. также: InitCoroutine |
| ZeroOrNull | Если оба регистра P1 и P3 НЕ равны NULL, записывает ноль в регистр P2. Если один из регистров P1 или P3 равен NULL, записывает NULL в регистр P2. |
Эта страница в последний раз обновлялась 2025-04-16 13:13:29Z
SQLite is in the Public Domain.
https://sqlite.org/opcode.html