SQLite 的運作方式是將 SQL 陳述式轉換為位元組碼,然後在虛擬機器中執行該位元組碼。本文檔描述了位元組碼引擎的運作方式。
本文檔描述了 SQLite 的內部機制。這裡提供的資訊對於使用 SQLite 進行常規應用程式開發並非必要。本文檔適用於想要深入了解 SQLite 內部運作的人員。
位元組碼引擎並非 SQLite 的 API。關於位元組碼引擎的細節會隨著 SQLite 版本的更新而改變。使用 SQLite 的應用程式不應依賴本文檔中的任何細節。
有關 SQLite 偏好使用位元組碼來實作 SQL 的一些原因,請參閱「為何 SQLite 使用位元組碼」文件。
SQLite 的運作方式是將每個 SQL 陳述式轉換為位元組碼,然後執行該位元組碼。SQLite 中的預備陳述式主要就是實作對應 SQL 所需的位元組碼。sqlite3_prepare_v2() 介面是一個編譯器,可將 SQL 轉換為位元組碼。sqlite3_step() 介面是執行預備陳述式中包含的位元組碼的虛擬機器。
位元組碼虛擬機器是 SQLite 的核心。想要了解 SQLite 內部運作方式的程式設計師必須熟悉位元組碼引擎。
在過去,SQLite 中的位元組碼引擎被稱為「虛擬資料庫引擎」或「VDBE」。本網站會交替使用「位元組碼引擎」、「VDBE」、「虛擬機器」和「位元組碼虛擬機器」等術語,因為它們都指的是同一事物。
本文也會交替使用「位元組碼程式」和「預備陳述式」等術語,因為它們大致上是同一事物。
位元組碼引擎的原始碼位於 vdbe.c 原始碼檔案中。本文檔中的操作碼定義源自該原始碼檔案中的註釋。原始碼註釋是關於位元組碼引擎的規範資訊來源。如有疑問,請參閱原始碼。
除了主要的 vdbe.c 原始碼檔案之外,原始碼樹狀目錄中還有其他輔助程式碼檔案,所有檔案的名稱都以「vdbe」開頭,這是「虛擬資料庫引擎」的縮寫。
請記住,操作碼的名稱和含義通常會隨著 SQLite 版本的更新而改變。因此,如果您正在研究 SQLite 的 EXPLAIN 輸出,您應該參考與執行 EXPLAIN 的 SQLite 版本相對應的本文檔版本(或 vdbe.c 原始碼)。否則,操作碼的描述可能不準確。本文檔源自 SQLite 版本 3.46.1 的簽入 c9c2ab54ba1f5,日期為 2024 年 8 月 13 日。
在 SQLite 中,一個位元組碼程式由一個或多個指令組成。每個指令都有一個操作碼和五個運算元,分別命名為 P1、P2、P3、P4 和 P5。P1、P2 和 P3 運算元是 32 位元有號整數。這些運算元通常指向暫存器。對於操作 B 樹游標的指令,P1 運算元通常是游標編號。對於跳轉指令,P2 通常是跳轉目的地。P4 可以是 32 位元有號整數、64 位元有號整數、64 位元浮點值、字串常值、Blob 常值、指向排序序列比較函式的指標,或指向應用程式定義的 SQL 函式實作的指標,或是其他各種資料類型。P5 是一個 16 位元無號整數,通常用於存放旗標。P5 旗標的位元有時會以微妙的方式影響操作碼。例如,如果在 Eq 操作碼上設定了 P5 運算元的 SQLITE_NULLEQ (0x0080) 位元,則 NULL 值彼此比較相等。否則,NULL 值彼此比較不相等。
有些操作碼使用所有五個運算元。有些操作碼使用一或兩個。有些操作碼則完全不使用任何運算元。
位元組碼引擎從指令編號 0 開始執行。執行持續進行,直到遇到 Halt 指令,或者程式計數器變得大於最後一個指令的地址,或者發生錯誤。當位元組碼引擎停止時,它所配置的所有記憶體都會被釋放,並且它可能已開啟的所有資料庫游標都會被關閉。如果執行因錯誤而停止,則任何待處理的事務都會被終止,並且對資料庫所做的更改會被回滾。
ResultRow 操作碼會導致位元組碼引擎暫停,並使對應的 sqlite3_step() 呼叫返回 SQLITE_ROW。在呼叫 ResultRow 之前,位元組碼程式會將查詢單一列的結果載入一系列暫存器中。C 語言 API,例如 sqlite3_column_int() 或 sqlite3_column_text(),會從這些暫存器中提取查詢結果。位元組碼引擎會在下一次呼叫 sqlite3_step() 時,從 ResultRow 之後的下一條指令繼續執行。
每個位元組碼程式都有一定數量(但可能很大)的暫存器。一個暫存器可以存放各種物件:
暫存器也可以是「未定義」的,表示它完全不存放任何值。未定義與 NULL 不同。根據編譯時期的選項,嘗試讀取未定義的暫存器通常會導致執行時期錯誤。如果程式碼產生器 (sqlite3_prepare_v2()) 產生讀取未定義暫存器的 已準備好的陳述式,則這是程式碼產生器中的錯誤。
暫存器從 0 開始編號。大多數操作碼至少會參考一個暫存器。
單一已準備好的陳述式中的暫存器數量在編譯時期是固定的。當已準備好的陳述式被 重置 或 終結 時,所有暫存器的內容都會被清除。
內部的 Mem 物件儲存單一暫存器的值。API 中公開的抽象 sqlite3_value 物件實際上只是一個 Mem 物件或暫存器。
一個預編譯語句可以有零個或多個開啟的游標。每個游標都由一個小的整數標識,該整數通常是用到該游標的操作碼的 P1 參數。同一個索引或表格上可以開啟多個游標。所有游標都獨立運作,即使是指向相同索引或表格的游標也是如此。虛擬機器與資料庫檔案互動的唯一途徑是透過游標。虛擬機器中的指令可以建立新的游標(例如:OpenRead 或 OpenWrite)、從游標讀取資料(Column)、將游標推進到表格中的下一個項目(例如:Next 或 Prev)等等。當預編譯語句被重置或終結時,所有游標都會自動關閉。
位元組碼引擎沒有堆疊來儲存子程序的返回位址。返回位址必須儲存在暫存器中。因此,位元組碼子程序不可重入。
Gosub 操作碼將目前的程式計數器儲存到暫存器 P1 中,然後跳轉到位址 P2。Return 操作碼跳轉到位址 P1+1。因此,每個子程序都與兩個整數相關聯:子程序中入口點的位址以及用於存放返回位址的暫存器編號。
Yield 操作碼將程式計數器的值與暫存器 P1 中的整數值交換。此操作碼用於實現協程。協程通常用於實現根據需要提取內容的子查詢。
觸發器需要可重入。由於位元組碼子程序不可重入,因此必須使用不同的機制來實現觸發器。每個觸發器都使用一個單獨的位元組碼程式來實現,該程式具有自己的操作碼、程式計數器和暫存器組。Program 操作碼會呼叫觸發器子程式。Program 指令會為子程式的每次呼叫分配並初始化一個新的暫存器組,因此子程式可以是可重入和遞迴的。Param 操作碼由子程式用於存取呼叫位元組碼程式暫存器中的內容。
某些操作碼是自修改的。例如,Init 操作碼(它始終是每個位元組碼程式中的第一個操作碼)會遞增其 P1 運算元。後續的 Once 操作碼會將其 P1 運算元與 Init 操作碼的 P1 值進行比較,以確定是否應略過後續的一次性初始化程式碼。另一個例子是 String8 操作碼,它將其 P4 運算元從 UTF-8 轉換為正確的資料庫字串編碼,然後將自身轉換為 String 操作碼。
SQLite 解譯的每個 SQL 語句都會產生一個虛擬機器程式。但如果 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 核心產生的。命令列 shell 包含用於縮排迴圈的額外邏輯。此外,EXPLAIN 輸出中的「註釋」欄位僅在使用 -DSQLITE_ENABLE_EXPLAIN_COMMENTS 選項編譯 SQLite 時才會提供。
當 SQLite 使用 SQLITE_DEBUG 編譯時選項編譯時,會提供額外的 PRAGMA 指令,這些指令對於除錯和探索 VDBE 的運作方式非常有用。例如,啟用 vdbe_trace pragma 可以讓每個 VDBE 操作碼在執行時將其反組譯程式碼輸出到標準輸出。這些除錯 pragma 包括:
虛擬機器目前定義了 189 個操作碼。下表描述了目前定義的所有操作碼。此表是通過掃描 vdbe.c 檔案中的原始碼自動生成的。
請記住:VDBE 操作碼並非 SQLite 介面定義的一部分。操作碼的數量、名稱和含義會隨著 SQLite 版本的更新而改變。下表中顯示的操作碼適用於 SQLite 版本 3.46.1,版本提交 c9c2ab54ba1f5,日期為 2024 年 8 月 13 日。
操作碼名稱 說明 Abortable 驗證是否可以發生中止。如果此時中止可能會導致資料庫損毀,則斷言。此操作碼僅出現在除錯版本中。 如果沒有任何寫入,或者存在有效的語句日誌,則中止是安全的。
Add 將暫存器 P1 中的值與暫存器 P2 中的值相加,並將結果儲存在暫存器 P3 中。如果任一輸入為 NULL,則結果為 NULL。 AddImm 將常數 P2 加到暫存器 P1 中的值。結果始終為整數。 要強制任何暫存器為整數,只需加 0。
Affinity 將 affinity 應用於從 P1 開始的 P2 個暫存器範圍。 P4 是一個長度為 P2 個字元的字串。字串的第 N 個字元表示應在範圍內第 N 個記憶體單元使用的欄位 affinity。
AggFinal P1 是聚集函數或視窗函數的累加器的記憶體位置。執行聚集函數的終結器函數,並將結果儲存在 P1 中。 P2 是 step 函數接受的參數數量,P4 是指向此函數的 FuncDef 的指標。此操作碼不使用 P2 參數。它只是為了區分可以接受不同數量參數的函數。僅當先前未呼叫 step 函數時才需要 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。在第一次評估時,儲存在 P4 中的 FuncDef 會轉換為 sqlite3_context,並且操作碼會更改。這樣,sqlite3_context 的初始化只會發生一次,而不是每次呼叫 step 函數時都發生。
AggValue 呼叫 xValue() 函數並將結果儲存在暫存器 P3 中。 P2 是 step 函數接受的參數數量,P4 是指向此函數的 FuncDef 的指標。此操作碼不使用 P2 參數。它只是為了區分可以接受不同數量參數的函數。僅當先前未呼叫 step 函數時才需要 P4 參數。
And 將暫存器 P1 和 P2 中的值進行邏輯 AND 運算,并将結果寫入暫存器 P3。 如果 P1 或 P2 為 0(假),則即使另一個輸入為 NULL,結果也為 0。NULL 與真值或兩個 NULL 的 AND 運算結果為 NULL。
自動提交 (AutoCommit) 將資料庫自動提交旗標設定為 P1(1 或 0)。如果 P2 為真,則回滾任何當前作用中的 btree 事務。如果有任何作用中的虛擬機器(除了這個之外),則 ROLLBACK 將失敗。如果有作用中的寫入虛擬機器或使用共享快取的作用中虛擬機器,則 COMMIT 將失敗。 此指令會導致虛擬機器停止。
子程式開始 (BeginSubrtn) 標記可以內嵌進入或使用 Gosub 呼叫的子程式的開始。子程式應以 Return 指令終止,該指令的 P1 運算元與此操作碼的 P2 運算元相同,且 P3 設定為 1。如果子程式是內嵌進入的,則 Return 將直接執行下一行指令。但如果子程式是使用 Gosub 呼叫的,則 Return 將跳回 Gosub 之後的下一條指令。 此例程透過將 NULL 載入到 P2 暫存器中來運作。當返回地址暫存器包含 NULL 時,Return 指令將成為無運算指令,直接執行下一條指令(假設 Return 操作碼的 P3 值為 1)。因此,如果子程式是內嵌進入的,則 Return 將導致內嵌執行繼續。但如果子程式是透過 Gosub 呼叫的,則 Return 將導致返回到 Gosub 之後的地址。
此操作碼與 Null 相同。它只是名稱不同,以便更容易閱讀和驗證位元組碼。
位元 AND (BitAnd) 對暫存器 P1 和 P2 中的值執行位元 AND 運算,并将結果儲存在暫存器 P3 中。如果任一輸入為 NULL,則結果為 NULL。 位元反向 (BitNot) 將暫存器 P1 的內容解譯為整數。將 P1 值的單補數儲存到暫存器 P2 中。如果 P1 包含 NULL,則在 P2 中儲存 NULL。 位元 OR (BitOr) 對暫存器 P1 和 P2 中的值執行位元 OR 運算,并将結果儲存在暫存器 P3 中。如果任一輸入為 NULL,則結果為 NULL。 二進位大型物件 (Blob) P4 指向一個長度為 P1 位元組的二進位大型物件資料。將此二進位大型物件儲存在暫存器 P2 中。如果 P4 是 NULL 指標,則在 P2 中建構一個長度為 P1 位元組的零填充二進位大型物件。 轉型 (Cast) 強制將暫存器 P1 中的值轉換為 P2 定義的類型。
- P2=='A' → BLOB(二進位大型物件)
- P2=='B' → TEXT(文字)
- P2=='C' → NUMERIC(數值)
- P2=='D' → INTEGER(整數)
- P2=='E' → REAL(實數)
NULL 值不會被此例程更改。它仍然是 NULL。
檢查點 (Checkpoint) 檢查點資料庫 P1。如果 P1 目前不在 WAL 模式,則此操作無效。參數 P2 為 SQLITE_CHECKPOINT_PASSIVE、FULL、RESTART 或 TRUNCATE 其中之一。如果檢查點返回 SQLITE_BUSY,則在 mem[P3] 中寫入 1,否則寫入 0。將檢查點之後 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() 函式或等效函式使用。不會載入大型 BLOB 的內容,從而節省 CPU 週期。如果設定了 OPFLAG_TYPEOFARG 位元,則結果將僅由 typeof() 函式或 IS NULL 或 IS NOT NULL 運算子或等效運算子使用。在這種情況下,可以省略所有內容載入。
ColumnsUsed (已使用的欄位) 此操作碼(僅在使用 SQLITE_ENABLE_COLUMN_USED_MASK 編譯 SQLite 時存在)識別游標 P1 的表格或索引中實際使用了哪些欄位。P4 是一個 64 位元整數 (P4_INT64),其中前 63 個位元對應表格或索引中實際使用的前 63 個欄位,如果使用了該欄位,則對應的位元設為 1。如果使用了第 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 小於數字,數字小於字串,字串小於 BLOB。
此操作碼之後必須緊接著一個 Jump 操作碼。
Concat (串接) 將暫存器 P1 中的文字附加到暫存器 P2 中文字的末尾,並將結果儲存在暫存器 P3 中。如果 P1 或 P2 的文字為 NULL,則在 P3 中儲存 NULL。 P3 = P2 || P1
P1 和 P3 不能是同一個暫存器。有時,如果 P3 與 P2 是同一個暫存器,則實作可以避免 memcpy()。
複製 將暫存器 P1..P1+P3 的內容複製到暫存器 P2..P2+P3。 如果 P5 的 0x0002 位元已設定,則同時清除目標中的 MEM_Subtype 旗標。P5 的 0x0001 位元表示此 複製 操作碼無法合併。0x0001 位元由查詢規劃器使用,在查詢執行期間不會生效。
此指令會進行值的深層複製。任何字串或 blob 常數都會被複製。另請參閱 淺層複製 (SCopy)。
計數 將由游標 P1 開啟的表格或索引中的項目數(整數值)儲存在暫存器 P2 中。 如果 P3==0,則會取得精確計數,這涉及訪問表格的每個 btree 頁面。但如果 P3 非零,則會根據目前的游標位置返回估計值。
建立 B 樹 如果 P1==0,則在主資料庫檔案中配置新的 b 樹;如果 P1==1,則在 TEMP 資料庫檔案中配置;如果 P1>1,則在附加的資料庫中配置。對於 rowid 表格,P3 參數必須為 1 (BTREE_INTKEY);對於索引或 WITHOUT ROWID 表格,P3 參數必須為 2 (BTREE_BLOBKEY)。新的 b 樹的根頁碼儲存在暫存器 P2 中。 游標提示 向游標 P1 提供提示,告知它只需要返回滿足 P4 中運算式 (Expr) 的列。P4 運算式中的 TK_REGISTER 項是指目前儲存在暫存器中的值。P4 運算式中的 TK_COLUMN 項是指游標 P1 指向的 b 樹中的欄位。 鎖定游標 鎖定游標 P1 指向的 b 樹,以便其他游標無法寫入該 b 樹。 解鎖游標 解鎖游標 P1 指向的 b 樹,以便其他游標可以寫入它。 遞減跳躍零 暫存器 P1 必須包含一個整數。遞減 P1 中的值,如果新值恰好為零,則跳轉到 P2。 延遲搜尋 P1 是一個已開啟的索引游標,P3 是對應表格上的游標。此操作碼會將 P3 表格游標延遲搜尋到與 P1 的目前列對應的列。 這是一個延遲搜尋。在使用游標讀取記錄之前,實際上不會發生任何事情。這樣,如果沒有讀取發生,就不會發生不必要的 I/O。
P4 可以是一個整數陣列(類型 P4_INTARRAY),其中包含 P3 表格中每個欄位的一個項目。如果陣列項目 a(i) 非零,則從游標 P3 讀取欄位 a(i)-1 等同於執行延遲搜尋,然後從 P1 讀取欄位 i。此資訊儲存在 P3 中,並用於將針對 P3 的讀取重新導向到 P1,從而可能避免搜尋和讀取游標 P3 的需要。
刪除 刪除 P1 游標目前指向的記錄。 如果設定了 P5 參數的 OPFLAG_SAVEPOSITION 位元,則游標將指向表格中的下一條或前一條記錄。如果它指向下一條記錄,則下一個 下一條 (Next) 指令將是無操作。因此,在這種情況下,可以從 下一條 (Next) 迴圈中刪除記錄。如果 P5 的 OPFLAG_SAVEPOSITION 位元清除,則游標將處於未定義狀態。
如果 P5 上設定了 OPFLAG_AUXDELETE 位元,表示此刪除操作是與刪除表格列及其所有相關索引項相關聯的多個刪除操作之一。這些刪除操作中恰好有一個是「主要」刪除操作。其他刪除操作都在 OPFLAG_FORDELETE 游標上,或者標記有 AUXDELETE 旗標。
如果 P2(注意:是 P2 不是 P5)的 OPFLAG_NCHANGE (0x01) 旗標已設定,則列變更計數會遞增(否則不遞增)。
如果 P2(不是 P5!)的 OPFLAG_ISNOOP (0x40) 旗標已設定,則會執行刪除操作的更新前鉤子 (pre-update-hook),但 B 樹保持不變。這種情況發生在 刪除 操作之後緊接著使用相同鍵的 插入 操作,導致 B 樹項被覆蓋。
P1 不能是虛擬表格。它必須是一個具有多列的真實表格。
如果 P4 不為 NULL,則它指向一個表格物件。在這種情況下,可能會呼叫更新鉤子或更新前鉤子,或兩者皆呼叫。在這種情況下,在呼叫此操作碼之前,必須使用 NotFound 定位 P1 游標。具體來說,如果已設定更新前鉤子,且 P4 不為 NULL,則會呼叫更新前鉤子。如果已設定更新鉤子、P4 不為 NULL 且 P2 中設定了 OPFLAG_NCHANGE 旗標,則會呼叫更新鉤子。
如果 P2 中設定了 OPFLAG_ISUPDATE 旗標,則 P3 包含記憶體儲存格的位址,該儲存格包含列的 rowid 將由更新設定的值。
銷毀 刪除整個資料庫表格或索引,其在資料庫檔案中的根頁面由 P1 指定。 如果 P3==0,則被銷毀的表格位於主資料庫檔案中。如果 P3==1,則被銷毀的表格位於用於儲存使用 CREATE TEMPORARY TABLE 建立的表格的輔助資料庫檔案中。
如果啟用了 AUTOVACUUM,則可能會將另一個根頁面移入新刪除的根頁面中,以便將所有根頁面保持在資料庫的開頭連續。移動的根頁面的先前值(移動前的值)儲存在暫存器 P2 中。如果不需要頁面移動(因為被刪除的表格已經是資料庫中的最後一個表格),則在暫存器 P2 中儲存零。如果停用了 AUTOVACUUM,則在暫存器 P2 中儲存零。
如果在呼叫此操作碼時有任何作用中的讀取器 VM,則此操作碼會擲回錯誤。這樣做是為了避免在 AUTOVACUUM 資料庫中移動根頁面時更新現有游標的困難。即使資料庫不是 AUTOVACUUM 資料庫,也會擲回此錯誤,以避免在自動真空和非自動真空模式之間產生不相容性。
另請參閱:清除
除法 將暫存器 P1 中的值除以暫存器 P2 中的值,並將結果儲存在暫存器 P3 中 (P3=P2/P1)。如果暫存器 P1 中的值為零,則結果為 NULL。如果任一輸入為 NULL,則結果為 NULL。 刪除索引 移除描述資料庫 P1 中名為 P4 的索引的內部(記憶體中)資料結構。這是為了在從磁碟刪除索引(使用 銷毀 操作碼)後呼叫的,以便使結構描述的內部表示與磁碟上的內容保持一致。 刪除表格 移除描述資料庫 P1 中名為 P4 的表格的內部(記憶體中)資料結構。這是為了在從磁碟刪除表格(使用 銷毀 操作碼)後呼叫的,以便使結構描述的內部表示與磁碟上的內容保持一致。 刪除觸發器 移除資料庫 P1 中描述觸發器 P4 的內部(記憶體中)資料結構。這是當一個觸發器從磁碟中刪除後(使用 Destroy 操作碼),為了保持 schema 的內部表示與磁碟上一致時所呼叫的。 ElseEq(否則等於) 此操作碼必須接在 Lt 或 Gt 比較運算子之後。中間可以有零個或多個 OP_ReleaseReg 操作碼,但不允許在此指令與之前的 Lt 或 Gt 之間出現其他操作碼。 如果對與先前 Lt 或 Gt 相同的兩個運算元執行 Eq 比較的結果為真,則跳轉到 P2。如果對前兩個運算元執行 Eq 比較的結果為假或 NULL,則繼續執行下一條指令。
EndCoroutine(結束協程) 暫存器 P1 中地址的指令是 Yield。跳轉到該 Yield 的 P2 參數。跳轉後,值暫存器 P1 會保留一個值,以便後續的 OP_Yields 返回到同一個 EndCoroutine 指令。 另請參閱:InitCoroutine
Eq(等於) 比較暫存器 P1 和 P3 中的值。如果 reg(P3)==reg(P1),則跳轉到地址 P2。 P5 的 SQLITE_AFF_MASK 部分必須是一個 affinity 字元 - SQLITE_AFF_TEXT、SQLITE_AFF_INTEGER 等等。在進行比較之前,會嘗試根據此 affinity 強制轉換兩個輸入。如果 SQLITE_AFF_MASK 為 0x00,則使用數值 affinity。請注意,affinity 轉換會儲存回輸入暫存器 P1 和 P3。因此,此操作碼可能會導致暫存器 P1 和 P3 的永久性更改。
一旦進行任何轉換,且兩個值都不是 NULL,則會比較這些值。如果兩個值都是 blob,則使用 memcmp() 來確定比較的結果。如果兩個值都是文字,則使用 P4 中指定的適當排序函數來進行比較。如果未指定 P4,則使用 memcmp() 來比較文字字串。如果兩個值都是數值,則使用數值比較。如果兩個值的類型不同,則數字被認為小於字串,字串被認為小於 blob。
如果在 P5 中設定了 SQLITE_NULLEQ,則比較的結果始終為真或假,且絕不會是 NULL。如果兩個運算元都是 NULL,則比較的結果為真。如果任一運算元是 NULL,則結果為假。如果兩個運算元都不是 NULL,則結果與從 P5 中省略 SQLITE_NULLEQ 旗標時的結果相同。
此操作碼會儲存比較的結果,供新的 Jump 操作碼使用。
Expire(過期) 使預編譯的陳述式過期。當使用 sqlite3_step() 執行過期的陳述式時,它會自動重新準備自己(如果它是最初使用 sqlite3_prepare_v2() 建立的),或者它會失敗並返回 SQLITE_SCHEMA 錯誤。 如果 P1 為 0,則所有 SQL 陳述式都過期。如果 P1 不為 0,則只有目前正在執行的陳述式過期。
如果 P2 為 0,則 SQL 陳述式立即過期。如果 P2 為 1,則允許正在執行的 SQL 陳述式繼續執行到完成。P2==1 的情況發生在 CREATE INDEX 或類似的 schema 變更可能有助於陳述式更快執行但不會影響操作正確性的情況下。
Filter(過濾) 計算從暫存器 r[P3] 開始,包含在 P4 暫存器中的鍵值的雜湊值。檢查該雜湊值是否存在於暫存器 P1 所存放的布隆過濾器中。如果不存在,則可能跳轉到 P2。否則繼續執行下一條指令。 偽陰性是無害的。即使值存在於布隆過濾器中,繼續執行下一條指令總是安全的。偽陰性會導致使用更多 CPU 週期,但仍應產生正確的答案。然而,錯誤的答案很可能來自偽陽性 — 如果在應該繼續執行下一條指令時卻執行了跳轉。
FilterAdd(新增至過濾器) 計算從 r[P3] 開始的 P4 暫存器的雜湊值,並將該雜湊值新增到 r[P1] 中的布隆過濾器。 FinishSeek(完成搜尋) 如果遊標 P1 先前已透過 DeferredSeek(延遲搜尋) 移動,則立即完成該搜尋操作,不再延遲。如果遊標搜尋已經發生,則此指令無效。 FkCheck(外鍵檢查) 如果有任何未解決的外鍵約束違規,則以 SQLITE_CONSTRAINT 錯誤停止。如果沒有外鍵約束違規,則此指令無效。 在準備好的陳述式結束時也會檢查外鍵約束違規。此操作碼用於在返回結果(例如列變更計數或 RETURNING 子句的結果)之前引發外鍵約束錯誤。
FkCounter(外鍵計數器) 將「約束計數器」增加 P2(P2 可以是負數或正數)。如果 P1 非零,則資料庫約束計數器遞增(延遲外鍵約束)。否則,如果 P1 為零,則陳述式計數器遞增(立即外鍵約束)。 FkIfZero(如果外鍵計數器為零) 此操作碼測試外鍵約束計數器目前是否為零。如果是,則跳轉到指令 P2。否則,繼續執行下一條指令。 如果 P1 非零,則在資料庫約束計數器為零時執行跳轉(計算延遲約束違規的計數器)。如果 P1 為零,則在陳述式約束計數器為零時執行跳轉(立即外鍵約束違規)。
Found(找到) 如果 P4==0,則暫存器 P3 包含由 MakeRecord(建立紀錄) 建構的 blob。如果 P4>0,則暫存器 P3 是組成未封裝紀錄的 P4 個暫存器的第一個。 遊標 P1 位於索引 B 樹上。如果由 P3 和 P4 標識的紀錄是 P1 中任何項目的前綴,則跳轉到 P2,並且 P1 指向匹配的項目。
此操作使遊標處於可以向前推進的狀態。 Next(下一筆) 指令可以運作,但 Prev(上一筆) 指令則不行。
另請參閱:NotFound(找不到)、NoConflict(無衝突)、NotExists(不存在)、SeekGe(搜尋大於等於)。
Function(函數) 使用從暫存器 P2 及其後續暫存器取得的引數呼叫使用者函數(P4 是指向 sqlite3_context 物件的指標,該物件包含指向要執行的函數的指標)。引數數量位於 P4 指向的 sqlite3_context 物件中。函數的結果儲存在暫存器 P3 中。暫存器 P3 不能是函數輸入之一。 P1 是一個 32 位元位元遮罩,指示在編譯時期是否確定函數的每個引數為常數。如果第一個引數是常數,則設定 P1 的位元 0。這用於確定是否可以安全地保留使用 sqlite3_set_auxdata() API 與使用者函數引數關聯的元資料,直到下次呼叫此操作碼為止。
Ge(大於等於) 此操作碼與 Lt 操作碼的工作方式相同,不同之處在於,如果暫存器 P3 的內容大於或等於暫存器 P1 的內容,則執行跳轉。有關其他資訊,請參閱 Lt 操作碼。 GetSubtype(取得子類型) 從暫存器 P1 中提取子類型值,並將該子類型寫入暫存器 P2。如果 P1 沒有子類型,則 P1 得到 NULL。 Gosub (副程式) 將目前地址寫入暫存器 P1,然後跳轉到地址 P2。 Goto (跳轉) 無條件跳轉到地址 P2。下一個執行的指令將是程式開頭索引為 P2 的指令。 此操作碼實際上不使用 P1 參數。然而,它有時會被設為 1 而不是 0,作為給命令列 shell 的提示,表示此 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 執行期間發生的所有更改,但不回滾交易。
如果 P4 不為 null,則它是一個錯誤訊息字串。
P5 是一個介於 0 和 4 之間(含)的值,用於修改 P4 字串。
0:(無更改) 1:NOT NULL 約束失敗:P4 2:UNIQUE 約束失敗:P4 3:CHECK 約束失敗:P4 4:FOREIGN KEY 約束失敗:P4
如果 P5 不為零且 P4 為 NULL,則省略「:」之後的所有內容。
HaltIfNull (如果為空則停止) 檢查暫存器 P3 中的值。如果它為 NULL,則使用參數 P1、P2 和 P4 執行 Halt,如同這是一個 Halt 指令一樣。如果暫存器 P3 中的值不為 NULL,則此例程為無操作。P5 參數應為 1。 IdxDelete (索引刪除) 從暫存器 P2 開始的 P3 個暫存器的內容組成一個未壓縮的索引鍵。此操作碼從游標 P1 開啟的索引中移除該項目。 如果 P5 不為零,則在找不到匹配的索引項目時引發 SQLITE_CORRUPT_INDEX 錯誤。這發生在執行 UPDATE 或 DELETE 陳述式且找不到要更新或刪除的索引項目時。對於 IdxDelete 的某些用途(例如:EXCEPT 運算子),找不到匹配的項目無關緊要。在這些情況下,P5 為零。此外,如果處於 writable_schema 模式,則不要引發此(自我修正且非關鍵)錯誤。
IdxGE (索引大於等於) 從 P3 開始的 P4 暫存器值組成一個省略主鍵的未壓縮索引鍵。將此鍵值與 P1 目前指向的索引進行 比較,忽略末尾的主鍵或 ROWID 欄位。 如果 P1 索引項目大於或等於鍵值,則跳轉到 P2。否則,繼續執行下一條指令。
IdxGT (索引大於) 從 P3 開始的 P4 暫存器值組成一個省略主鍵的未壓縮索引鍵。將此鍵值與 P1 目前指向的索引進行 比較,忽略末尾的主鍵或 ROWID 欄位。 如果 P1 索引項目大於鍵值,則跳轉到 P2。否則,繼續執行下一條指令。
IdxInsert (索引插入) 暫存器 P2 包含使用 MakeRecord 指令建立的 SQL 索引鍵。此操作碼將該鍵寫入索引 P1。項目的資料為 nil。 如果 P4 不為零,則它代表 reg(P2) 中未封裝鍵值 (unpacked key) 的值數量。在這種情況下,P3 是未封裝鍵值的第一個暫存器索引。使用未封裝鍵值有時可以是一種優化方式。
如果 P5 設有 OPFLAG_APPEND 位元,則這是給 B 樹層的提示,表示這次插入很可能是附加操作。
如果 P5 設有 OPFLAG_NCHANGE 位元,則此指令會使變更計數器遞增。如果 OPFLAG_NCHANGE 位元未設定,則變更計數器保持不變。
如果 P5 的 OPFLAG_USESEEKRESULT 旗標已設定,則實作可以透過避免在游標 P1 上進行不必要的搜尋來加快執行速度。然而,只有在游標上沒有先前的搜尋,或者最近一次搜尋使用的鍵值與 P2 相等的情況下,才能設定 OPFLAG_USESEEKRESULT 旗標。
此指令僅適用於索引。表格的等效指令為 Insert。
IdxLE 從 P3 開始的 P4 暫存器值組成一個未封裝的索引鍵值,其中省略了主鍵 (PRIMARY KEY) 或列 ID (ROWID)。將此鍵值與 P1 游標目前指向的索引進行 比較,忽略 P1 索引上的主鍵或列 ID。 如果 P1 索引項目小於或等於鍵值,則跳轉到 P2。否則繼續執行下一條指令。
IdxLT 從 P3 開始的 P4 暫存器值組成一個未封裝的索引鍵值,其中省略了主鍵 (PRIMARY KEY) 或列 ID (ROWID)。將此鍵值與 P1 游標目前指向的索引進行 比較,忽略 P1 索引上的主鍵或列 ID。 如果 P1 索引項目小於鍵值,則跳轉到 P2。否則繼續執行下一條指令。
IdxRowid 將一個整數寫入暫存器 P2,該整數是游標 P1 指向的索引鍵值末尾記錄中的最後一個項目。此整數應該是此索引項目指向的表格項目的列 ID (rowid)。 另請參閱:Rowid、MakeRecord。
If 如果暫存器 P1 中的值為真,則跳轉到 P2。如果該值是數值且非零,則視為真。如果 P1 中的值為 NULL,則僅當 P3 非零時才進行跳轉。 IfNoHope 暫存器 P3 是組成未封裝記錄的 P4 個暫存器的第一個。游標 P1 是一個索引 B 樹。P2 是跳轉目的地。換句話說,此操作碼的操作數與 NotFound 和 IdxGT 的操作數相同。 此操作碼僅為優化嘗試。即使此操作碼總是略過,仍然可以獲得正確的答案,但會執行額外的工作。
游標 P1 的 seekHit 旗標值為 N 表示存在一個鍵值 P3:N,它會與索引中的某些記錄匹配。我們想知道記錄 P3:P4 是否有可能與索引中的某些記錄匹配。如果不可能,我們可以跳過一些工作。因此,如果 seekHit 小於 P4,則嘗試透過執行 NotFound 來判斷是否可能匹配。
此操作碼用於多欄鍵值的 IN 子句處理。如果 IN 子句附加到除最左邊元素以外的鍵值元素,並且在最近一次對整個鍵值進行搜尋時沒有匹配項,則可能是左側的某個鍵值元素阻止了匹配,因此無論檢查多少個 IN 子句元素,都「沒有希望」實現匹配。在這種情況下,我們使用此操作碼提前放棄 IN 子句搜尋。操作碼名稱來自於如果「沒有希望」實現匹配則會進行跳轉的事實。
IfNot 如果暫存器 P1 中的值為假,則跳轉到 P2。如果該值的數值為零,則視為假。如果 P1 中的值為 NULL,則僅當 P3 非零時才進行跳轉。 IfNotOpen 如果游標 P1 未開啟,或者使用 NullRow 操作碼將 P1 設定為 NULL 列,則跳轉到指令 P2。否則,繼續執行下一條指令。 IfNotZero 暫存器 P1 必須包含一個整數。如果暫存器 P1 的初始內容大於零,則將暫存器 P1 中的值減一。如果它是非零值(負數或正數),則跳轉到 P2。如果暫存器 P1 的初始值為零,則保持不變並繼續執行下一條指令。 IfNullRow 檢查游標 P1 是否目前指向 NULL 列。如果是,則將暫存器 P3 設定為 NULL,並立即跳轉到 P2。如果 P1 並非指向 NULL 列,則繼續執行下一條指令,不做任何更改。 如果 P1 不是開啟的游標,則此操作碼無效。
IfPos 暫存器 P1 必須包含一個整數。如果暫存器 P1 的值大於等於 1,則從 P1 的值中減去 P3,並跳轉到 P2。 如果暫存器 P1 的初始值小於 1,則該值保持不變,並繼續執行下一條指令。
IfSizeBetween 設 N 為具有游標 P1 的表格或索引中大約的行數,如果 N 為正數,則設 X 為 10*log2(N),如果 N 為零,則設 X 為 -1。 如果 X 介於 P3 和 P4 之間(含),則跳轉到 P2。
IncrVacuum 對 P1 資料庫執行單步增量真空程序。如果真空程序已完成,則跳轉到指令 P2。否則,繼續執行下一條指令。 Init 程式包含此操作碼的單一實例,作為第一個操作碼。 如果啟用了追蹤(透過 sqlite3_trace() 介面),則在追蹤回呼上發出 P4 中包含的 UTF-8 字串。或者,如果 P4 為空白,則使用 sqlite3_sql() 返回的字串。
如果 P2 不為零,則跳轉到指令 P2。
遞增 P1 的值,以便 Once 操作碼在本次執行中第一次被評估時就會跳轉。
如果 P3 不為零,則它是遇到 SQLITE_CORRUPT 錯誤時要跳轉到的地址。
InitCoroutine 設定暫存器 P1,使其 Yield 到位於地址 P3 的協程。 如果 P2!=0,則協程實作緊跟在此操作碼之後。因此跳過協程實作到地址 P2。
另請參閱:EndCoroutine
Insert 將一個項目寫入游標 P1 的表格中。如果它不存在,則會建立一個新項目,或者覆蓋現有項目的資料。資料是儲存在暫存器編號 P2 中的 MEM_Blob 值。鍵值儲存在暫存器 P3 中。鍵值必須是 MEM_Int。 如果設定了 P5 的 OPFLAG_NCHANGE 旗標,則列變更計數會遞增(否則不遞增)。如果設定了 P5 的 OPFLAG_LASTROWID 旗標,則會儲存列 ID 以供 sqlite3_last_insert_rowid() 函數後續返回(否則不修改)。
如果設定了 P5 的 OPFLAG_USESEEKRESULT 旗標,則實作可以透過避免在游標 P1 上進行不必要的搜尋來加快執行速度。但是,只有在游標上沒有先前的搜尋,或者最近的搜尋使用了與 P3 相等的鍵值時,才應該設定 OPFLAG_USESEEKRESULT 旗標。
如果設定了 OPFLAG_ISUPDATE 旗標,則此操作碼是 UPDATE 操作的一部分。否則(如果旗標清除),則此操作碼是 INSERT 操作的一部分。這種區別僅對更新鉤子很重要。
參數 P4 可以指向一個表格結構,也可以是 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 不為零,則會對輔助資料庫檔案(而非主要資料庫檔案)執行檢查。
此操作碼用於實作 integrity_check pragma。
IsNull 如果暫存器 P1 中的值為 NULL,則跳轉到 P2。 IsTrue 此操作碼實作 IS TRUE、IS FALSE、IS NOT TRUE 和 IS NOT FALSE 運算子。 將暫存器 P1 中的值解譯為布林值。將該布林值(0 或 1)儲存在暫存器 P2 中。或者,如果暫存器 P1 中的值為 NULL,則將 P3 儲存在暫存器 P2 中。如果 P4 為 1,則反轉答案。
邏輯摘要如下:
- 如果 P3==0 且 P4==0,則 r[P2] := r[P1] IS TRUE
- 如果 P3==1 且 P4==1,則 r[P2] := r[P1] IS FALSE
- 如果 P3==0 且 P4==1,則 r[P2] := r[P1] IS NOT TRUE
- 如果 P3==1 且 P4==0,則 r[P2] := r[P1] IS NOT FALSE
IsType 如果 B 樹中欄位的類型是 P5 位元遮罩指定的類型之一,則跳轉到 P2。 P1 通常是 B 樹上的游標,其列解碼快取至少對欄位 P3 有效。換句話說,應該有一個先前的 Column 指令針對欄位 P3 或更大。如果游標無效,則此操作碼可能會產生錯誤結果。如果 B 樹列的欄位少於 P3 個,則使用 P4 作為資料類型。
如果 P1 為 -1,則 P3 為暫存器編號,且資料類型取自該暫存器中的值。
P5 是資料類型的位元遮罩。SQLITE_INTEGER 是最低有效位元 (0x01)。SQLITE_FLOAT 是 0x02 位元。SQLITE_TEXT 是 0x04。SQLITE_BLOB 是 0x08。SQLITE_NULL 是 0x10。
警告:當 P1>=0 時,此操作碼無法可靠地區分 NULL 和 REAL。如果資料庫包含 NaN 值,此操作碼會認為資料類型是 REAL,而實際上應該是 NULL。當 P1<0 且值已儲存在暫存器 P3 中時,此操作碼可以可靠地區分 NULL 和 REAL。僅當 P1>=0 時才會出現此問題。
若且唯若由 P1 和 P3 確定的值的資料類型對應於 P5 位元遮罩中的一個位元時,才跳轉到地址 P2。
JournalMode 將資料庫 P1 的日誌模式變更為 P3。P3 必須是 PAGER_JOURNALMODE_XXX 值之一。如果在各種回滾模式(刪除、截斷、持久、關閉和記憶體)之間切換,則這是一個簡單的操作。不需要 IO。 如果切換到 WAL 模式或從 WAL 模式切換出來,則過程會更複雜。
將包含最終日誌模式的字串寫入暫存器 P2。
Jump 根據最近一次比較指令中 P1 向量與 P2 向量的比較結果,分別跳轉到地址 P1(小於)、P2(等於)或 P3(大於)的指令。 此操作碼必須緊接在比較操作碼之後。
Last(最後) 下一次對 P1 使用Rowid、Column或Prev指令時,將會參考資料庫表格或索引中的最後一筆資料。如果表格或索引為空且 P2>0,則立即跳轉到 P2。如果 P2 為 0 或表格或索引不為空,則繼續執行下一條指令。 Le(小於等於) 此操作碼與 Lt 操作碼的功能相同,差別在於如果暫存器 P3 的內容小於或等於暫存器 P1 的內容,則會執行跳轉。更多資訊請參考 Lt 操作碼。 LoadAnalysis(載入分析) 讀取資料庫 P1 的 sqlite_stat1 表格,並將表格內容載入到內部索引雜湊表中。這將導致在準備所有後續查詢時使用分析結果。 Lt(小於) 比較暫存器 P1 和 P3 中的值。如果 reg(P3)<reg(P1),則跳轉到地址 P2。 如果 P5 的 SQLITE_JUMPIFNULL 位元已設定,且 reg(P1) 或 reg(P3) 為 NULL,則執行跳轉。如果 SQLITE_JUMPIFNULL 位元未設定,則在任一運算元為 NULL 時繼續執行下一條指令。
P5 的 SQLITE_AFF_MASK 部分必須是一個 affinity 字元 - SQLITE_AFF_TEXT、SQLITE_AFF_INTEGER 等等。在進行比較之前,會嘗試根據此 affinity 強制轉換兩個輸入。如果 SQLITE_AFF_MASK 為 0x00,則使用數值 affinity。請注意,affinity 轉換會儲存回輸入暫存器 P1 和 P3。因此,此操作碼可能會導致暫存器 P1 和 P3 的永久性更改。
一旦進行任何轉換,且兩個值都不是 NULL,則會比較這些值。如果兩個值都是 blob,則使用 memcmp() 來確定比較的結果。如果兩個值都是文字,則使用 P4 中指定的適當排序函數來進行比較。如果未指定 P4,則使用 memcmp() 來比較文字字串。如果兩個值都是數值,則使用數值比較。如果兩個值的類型不同,則數字被認為小於字串,字串被認為小於 blob。
此操作碼會儲存比較的結果,供新的 Jump 操作碼使用。
MakeRecord(建立記錄) 將從 P1 開始的 P2 個暫存器轉換為記錄格式,以作為資料庫表格中的資料記錄或索引中的鍵值。之後可以使用Column操作碼解碼記錄。 P4 可以是一個長度為 P2 個字元的字串。字串的第 N 個字元表示索引鍵值第 N 個欄位應使用的欄位關聯性。
字元與關聯性的對應關係由 sqliteInt.h 中定義的 SQLITE_AFF_ 巨集提供。
如果 P4 為 NULL,則所有索引欄位的關聯性皆為 BLOB。
P5 的含義取決於是否啟用 SQLITE_ENABLE_NULL_TRIM 編譯時期選項。
* 如果啟用 SQLITE_ENABLE_NULL_TRIM,則 P5 是最右側可進行空值修剪的表格的索引。
* 如果省略 SQLITE_ENABLE_NULL_TRIM,則如果MakeRecord操作碼允許接受 serial_type 為 10 的未變更記錄,則 P5 的值為 OPFLAG_NOCHNG_MAGIC。此值僅在 assert() 內部使用,不會影響最終結果。
MaxPgcnt(最大頁數) 嘗試將資料庫 P1 的最大頁數設定為 P3 中的值。最大頁數不得低於目前的頁數,且如果 P3==0,則不要更改最大頁數值。 將更改後的最大頁數儲存在暫存器 P2 中。
MemMax(記憶體最大值) P1 是此 VM 根框架中的一個暫存器(如果此指令在子程式中執行,則根框架與目前框架不同)。將暫存器 P1 的值設定為其目前值與暫存器 P2 中值的最大值。 如果記憶體單元初始值不是整數,則此指令會擲出錯誤。
Move(移動) 將暫存器 P1..P1+P3-1 中的 P3 個值移動到暫存器 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 (新增記錄ID) 取得一個新的整數記錄號碼(亦稱為「rowid」),用作表格的鍵值。該記錄號碼先前未在游標 P1 指向的資料庫表格中用作鍵值。新的記錄號碼會寫入到暫存器 P2。 如果 P3>0,則 P3 是此 VDBE 根框架中的一個暫存器,其中存放先前產生的最大記錄號碼。不允許新的記錄號碼小於此值。當此值達到最大值時,會產生 SQLITE_FULL 錯誤。P3 暫存器會以產生的記錄號碼進行更新。此 P3 機制用於協助實現 AUTOINCREMENT 功能。
Next (下一個) 推進游標 P1,使其指向表格或索引中的下一個鍵值/資料對。如果沒有更多鍵值/資料對,則繼續執行下一條指令。但如果游標推進成功,則立即跳轉到 P2。 Next 操作碼僅在用於定位游標的 SeekGT、SeekGE 或 Rewind 操作碼之後才有效。Next 不允許在 SeekLT、SeekLE 或 Last 之後使用。
P1 游標必須指向真實表格,而不是虛擬表格。P1 必須在此操作碼之前已開啟,否則程式會發生區段錯誤。
P3 值是提供給 btree 實作的提示。如果 P3==1,則表示 P1 是一個 SQL 索引,並且如果該索引是唯一的,則可以省略此指令。P3 通常為 0。P3 的值始終為 0 或 1。
如果 P5 為正數且執行跳轉,則已準備好的陳述式中的事件計數器 P5-1 會遞增。
另請參閱:Prev
NoConflict (無衝突) 如果 P4==0,則暫存器 P3 包含由 MakeRecord(建立紀錄) 建構的 blob。如果 P4>0,則暫存器 P3 是組成未封裝紀錄的 P4 個暫存器的第一個。 游標 P1 位於索引 btree 上。如果由 P3 和 P4 標識的記錄包含任何 NULL 值,則立即跳轉到 P2。如果記錄的所有項皆非 NULL,則會執行檢查以確定 P1 索引 btree 中是否有任何列具有相符的鍵值前綴。如果沒有相符項,則立即跳轉到 P2。如果有相符項,則繼續執行並使 P1 游標指向相符的列。
此操作碼與 NotFound 類似,不同之處在於,如果搜尋鍵值輸入的任何部分為 NULL,則一律會執行跳轉。
Noop (無操作) 不執行任何操作。此指令通常可用作跳轉目標。 Not (非) 將暫存器 P1 中的值解譯為布林值。將布林補數儲存在暫存器 P2 中。如果暫存器 P1 中的值為 NULL,則在 P2 中儲存 NULL。 NotExists (不存在) P1 是在 SQL 表格 btree(具有整數鍵值)上開啟的游標的索引。P3 是一個整數 rowid。如果 P1 不包含 rowid 為 P3 的記錄,則立即跳轉到 P2。或者,如果 P2 為 0,則引發 SQLITE_CORRUPT 錯誤。如果 P1 包含 rowid 為 P3 的記錄,則使游標指向該記錄,並繼續執行下一條指令。 SeekRowid 指令碼執行相同的操作,但也允許 P3 暫存器包含非整數值,在這種情況下,總是會執行跳轉。此指令碼要求 P3 始終包含整數。
NotFound 指令碼對索引 B 樹(具有任意多值鍵值)執行相同的操作。
此指令碼使游標處於無法向前或向後移動的狀態。換句話說,在此指令碼之後,Next 和 Prev 指令碼將無法運作。
另請參閱:Found、NotFound、NoConflict、SeekRowid
NotFound 如果 P4==0,則暫存器 P3 包含由 MakeRecord(建立紀錄) 建構的 blob。如果 P4>0,則暫存器 P3 是組成未封裝紀錄的 P4 個暫存器的第一個。 游標 P1 位於索引 B 樹上。如果 P3 和 P4 標識的記錄不是 P1 中任何項目的前綴,則會跳轉到 P2。如果 P1 確實包含前綴與 P3/P4 記錄匹配的項目,則控制流程會繼續執行到下一條指令,並且 P1 指向匹配的項目。
此操作會使游標處於無法向前或向後推進的狀態。換句話說,在此操作之後,Next 和 Prev 操作碼將無法運作。
另請參閱:Found、NotExists、NoConflict、IfNoHope
NotNull 如果暫存器 P1 中的值不是 NULL,則跳轉到 P2。 Null 將 NULL 寫入暫存器 P2。如果 P3 大於 P2,則也將 NULL 寫入暫存器 P3 以及 P2 和 P3 之間的每個暫存器。如果 P3 小於 P2(通常 P3 為零),則只將暫存器 P2 設定為 NULL。 如果 P1 值非零,則也設定 MEM_Cleared 旗標,以便即使在 Ne 或 Eq 上設定 SQLITE_NULLEQ,NULL 值也不會比較相等。
NullRow 將游標 P1 移動到空列。當游標位於空列時,發生的任何 Column 操作都將始終寫入 NULL。 如果游標 P1 之前未開啟,則現在將其開啟為一個特殊的虛擬游標,該游標始終為每個欄位傳回 NULL。
Offset 將資料庫檔案中位元組偏移量儲存在暫存器 r[P3] 中,該偏移量是游標 P1 目前指向的記錄的有效負載的起始位置。 P2 是 sqlite_offset() 函式引數的欄位編號。此指令碼本身不使用 P2,但程式碼產生器會使用 P2 值。此指令碼的 P1、P2 和 P3 運算元與 Column 的相同。
僅當使用 -DSQLITE_ENABLE_OFFSET_SQL_FUNC 選項編譯 SQLite 時,此指令碼才可用。
OffsetLimit 此指令碼執行與 LIMIT 和 OFFSET 處理相關的常用計算。r[P1] 保存 LIMIT 計數器。r[P3] 保存 OFFSET 計數器。指令碼計算 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 在每次呼叫位元組碼程式時,第一次遇到此指令碼時,會繼續執行到下一條指令。在同一次呼叫期間,第二次及後續所有遇到此指令碼時,會跳轉到 P2。 頂層程式藉由比較 P1 運算元與程式開頭 Init 指令碼的 P1 運算元來判斷首次調用。如果 P1 值不同,則繼續執行並使此指令碼的 P1 等於 Init 的 P1。如果 P1 值相同,則執行跳轉。
對於子程式,VdbeFrame 中有一個位元遮罩,用於決定是否應該執行跳轉。需要位元遮罩,因為自我修改程式碼的技巧不適用於遞迴觸發器。
OpenAutoindex 此指令碼與 OpenEphemeral 的運作方式相同。它使用不同的名稱來區分其用途。由此指令碼建立的表格將用於在連接中自動建立的暫時索引。 OpenDup 開啟一個新的遊標 P1,指向與遊標 P2 相同的暫時表格。P2 遊標必須已由先前的 OpenEphemeral 指令碼開啟。只有暫時遊標可以被複製。 複製的暫時遊標用於實現物化視圖的自我連接。
OpenEphemeral 開啟一個新的遊標 P1,指向一個暫時表格。即使主資料庫是唯讀的,該遊標也總是開啟為讀/寫。當遊標關閉時,暫時表格會自動刪除。 如果遊標 P1 已經在一個暫時表格上開啟,則該表格將被清除(所有內容將被刪除)。
P2 是暫時表格中的欄位數。如果 P4==0,則遊標指向一個 B 樹表格;如果 P4 不為 0,則指向一個 B 樹索引。如果 P4 不為 NULL,則它指向一個 KeyInfo 結構,該結構定義索引中鍵的格式。
P5 參數可以是 btree.h 中定義的 BTREE_* 旗標的遮罩。這些旗標控制 B 樹操作的各個方面。BTREE_OMIT_JOURNAL 和 BTREE_SINGLE 旗標會自動添加。
如果 P3 為正數,則 reg[P3] 會略作修改,以便它可以用作 Insert 的零長度資料。這是一種優化,可避免額外的 Blob 指令碼來初始化該暫存器。
OpenPseudo 開啟一個新的遊標,指向一個包含單一行資料的虛擬表格。該行的內容是記憶體暫存器 P2 的內容。換句話說,遊標 P1 成為暫存器 P2 中包含的 MEM_Blob 內容的別名。 由此指令碼建立的虛擬表格用於保存排序器輸出的單一行,以便可以使用 Column 指令碼將該行分解為單獨的欄位。Column 指令碼是唯一適用於虛擬表格的遊標指令碼。
P3 是虛擬表格將儲存的記錄中的欄位數。如果 P2 為 0 或負數,則虛擬遊標將為每個欄位返回 NULL。
OpenRead 在資料庫檔案中開啟一個唯讀遊標,指向根頁面為 P2 的資料庫表格。資料庫檔案由 P3 決定。P3==0 表示主資料庫,P3==1 表示用於暫存表格的資料庫,P3>1 表示使用相應的附加資料庫。賦予新遊標 P1 的識別碼。P1 值不需要是連續的,但所有 P1 值都應該是小整數。P1 為負數是錯誤的。 允許的 P5 位元
P4 值可以是一個整數 (P4_INT32) 或一個指向 KeyInfo 結構的指標 (P4_KEYINFO)。如果是指向 KeyInfo 物件的指標,則正在開啟的表格必須是一個 索引型 B 樹 (index b-tree),其中 KeyInfo 物件定義了該索引型 B 樹的內容和排序序列。否則,如果 P4 是一個整數值,則正在開啟的表格必須是一個 資料表型 B 樹 (table b-tree),其欄位數不少於 P4 的值。
OpenWrite 在根頁面為 P2 的資料表或索引上開啟一個名為 P1 的讀/寫游標(如果在 P5 中設定了 OPFLAG_P2ISREG 位元,則根頁面會保存在暫存器 P2 中 - 詳見下方)。 P4 值可以是一個整數 (P4_INT32) 或一個指向 KeyInfo 結構的指標 (P4_KEYINFO)。如果是指向 KeyInfo 物件的指標,則正在開啟的表格必須是一個 索引型 B 樹 (index b-tree),其中 KeyInfo 物件定義了該索引型 B 樹的內容和排序序列。否則,如果 P4 是一個整數值,則正在開啟的表格必須是一個 資料表型 B 樹 (table b-tree),其欄位數不少於 P4 的值。
允許的 P5 位元
- 0x02 OPFLAG_SEEKEQ:此遊標將僅用於等值查找(實現為一對指令碼 SeekGE/IdxGT 或 SeekLE/IdxLT)
- 0x08 OPFLAG_FORDELETE:此游標僅用於在索引型 B 樹中搜尋並接著刪除項目。這是一個儲存引擎可以忽略的提示。官方的 SQLite B 樹儲存引擎不會使用此提示,但 COMDB2 會使用。
- 0x10 OPFLAG_P2ISREG:使用暫存器 P2 的內容作為根頁面,而不是使用 P2 的值本身。
此指令的功能類似於 OpenRead,但它以讀/寫模式開啟游標。
Or (或) 對暫存器 P1 和 P2 中的值進行邏輯 OR 運算,並將結果儲存在暫存器 P3 中。 如果 P1 或 P2 其中一個為非零值(真),則即使另一個輸入為 NULL,結果也為 1(真)。NULL 和 false 或兩個 NULL 會產生 NULL 輸出。
Pagecount (頁數) 將資料庫 P1 中的目前頁數寫入記憶體單元 P2。 Param (參數) 此操作碼只會出現在透過 Program 指令呼叫的子程式中。將目前儲存在呼叫(父)框架記憶體單元中的值複製到目前框架位址空間中的單元 P2。觸發程式會使用此功能來存取 new.* 和 old.* 值。 父框架中單元的位址是透過將 P1 參數的值加上呼叫 Program 指令的 P1 參數的值來確定的。
ParseSchema (解析結構描述) 讀取並解析資料庫 P1 的結構描述表格中所有符合 WHERE 子句 P4 的項目。如果 P4 是 NULL 指標,則會重新解析 P1 的整個結構描述。 此操作碼會呼叫解析器來建立新的虛擬機器,然後執行新的虛擬機器。因此,它是一個可重入的操作碼。
Permutation (排列) 設定下一個指令中 Compare 運算子所使用的排列。排列儲存在 P4 運算元中。 排列僅對下一個操作碼有效,該操作碼必須是在 P5 中設定了 OPFLAG_PERMUTE 位元的 Compare。
P4 整數陣列中的第一個整數是陣列的長度,它不會成為排列的一部分。
Prev (上一個) 將游標 P1 向後移動,使其指向其資料表或索引中的前一個鍵/資料對。如果沒有前一個鍵/值對,則會繼續執行後續指令。但如果游標成功向後移動,則會立即跳轉到 P2。 Prev 操作碼僅在用於定位游標的 SeekLT、SeekLE 或 Last 操作碼之後才有效。Prev 不允許在 SeekGT、SeekGE 或 Rewind 之後使用。
P1 游標必須用於真實資料表,而不是虛擬資料表。如果 P1 未開啟,則行為未定義。
P3 值是提供給 btree 實作的提示。如果 P3==1,則表示 P1 是一個 SQL 索引,並且如果該索引是唯一的,則可以省略此指令。P3 通常為 0。P3 的值始終為 0 或 1。
如果 P5 為正數且執行跳轉,則已準備好的陳述式中的事件計數器 P5-1 會遞增。
Program (程式) 執行作為 P4 傳遞的觸發程式(類型 P4_SUBPROGRAM)。 P1 包含記憶體儲存格的位址,該儲存格包含作為子程式參數的值陣列中的第一個記憶體儲存格。P2 包含如果子程式使用 RAISE() 函式拋出 IGNORE 例外時要跳轉到的位址。如果不可能引發 IGNORE 例外,則 P2 可能為零。暫存器 P3 包含此(父)VM 中的記憶體儲存格位址,用於在執行階段配置子 vdbe 所需的記憶體。
P4 是指向包含觸發程式之 VM 的指標。
如果 P5 不為零,則啟用遞迴程式呼叫。
PureFunc(純函式) 使用從暫存器 P2 及其後續暫存器取得的引數呼叫使用者函數(P4 是指向 sqlite3_context 物件的指標,該物件包含指向要執行的函數的指標)。引數數量位於 P4 指向的 sqlite3_context 物件中。函數的結果儲存在暫存器 P3 中。暫存器 P3 不能是函數輸入之一。 P1 是一個 32 位元位元遮罩,指示在編譯時期是否確定函數的每個引數為常數。如果第一個引數是常數,則設定 P1 的位元 0。這用於確定是否可以安全地保留使用 sqlite3_set_auxdata() API 與使用者函數引數關聯的元資料,直到下次呼叫此操作碼為止。
此操作碼的功能與 Function(函式) 完全相同。唯一的區別在於其名稱。此操作碼用於函式必須是非確定性的情況。某些內建日期/時間函式可以是確定性或非確定性的,取決於它們的參數。當這些函式以非確定性方式使用時,它們將檢查是否使用 PureFunc(純函式) 而不是 Function(函式) 呼叫它們,如果是,則拋出錯誤。
ReadCookie(讀取 Cookie) 從資料庫 P1 讀取 Cookie 編號 P3 並將其寫入暫存器 P2。P3==1 是結構描述版本。P3==2 是資料庫格式。P3==3 是建議的頁面快取大小,依此類推。P1==0 是主資料庫檔案,P1==1是用於儲存暫存資料表的資料庫檔案。 在執行此指令之前,資料庫上必須有一個讀取鎖定(必須啟動一個事務或必須有一個開啟的游標)。
Real(實數) P4是指向 64 位元浮點值的指標。將該值寫入暫存器 P2。 RealAffinity(實數親和性) 如果暫存器 P1 包含整數,則將其轉換為實數值。 此操作碼用於從具有 REAL 親和性的欄位中提取資訊。為了節省空間,此類欄位值可能仍儲存為整數,但在提取後,我們希望它們僅具有實數值。
ReleaseReg(釋放暫存器) 從服務中釋放暫存器。此操作碼完成後,暫存器中的任何內容都不可靠。 將釋放從 P1 開始的 P2 個暫存器,但如果 P3 的第 ii 位元已設定,則不釋放暫存器 P1+ii。換句話說,P3 是要保留的暫存器的遮罩。
釋放暫存器會清除 Mem.pScopyFrom 指標。這表示如果釋放的暫存器的內容是使用 SCopy(淺拷貝) 設定的,則更改 SCopy(淺拷貝) 的來源暫存器的值將不再在 sqlite3VdbeMemAboutToChange() 中產生斷言錯誤。
如果設定了 P5,則所有釋放的暫存器的類型都設定為 MEM_Undefined,以便任何後續讀取釋放的暫存器(在重新初始化之前)的嘗試都將產生斷言錯誤。
每次呼叫此操作碼時都應該設定 P5。然而,程式碼產生器中有些地方會在使用暫存器之前釋放它們,基於(有效的)假設,即在使用暫存器之前不會將它們重新分配給其他用途,因此可以安全地釋放。
此操作碼僅在測試和除錯建置中可用。它不會為發行版本產生。此操作碼的目的是幫助驗證產生的位元組碼。此操作碼實際上並未對計算答案有所貢獻。
Remainder(餘數) 計算整數暫存器 P2 除以暫存器 P1 之後的餘數,並將結果儲存在暫存器 P3 中。如果暫存器 P1 中的值為零,則結果為 NULL。如果任一運算元為 NULL,則結果為 NULL。 ReopenIdx(重新開啟索引) ReopenIdx 指令的運作方式與 OpenRead 類似,不同之處在於它會先檢查 P1 上的游標是否已開啟於同一個 B 樹上,如果是,則此指令變成無運算指令。換句話說,如果游標已經開啟,則不要重新開啟它。 ReopenIdx 指令只能在 P5==0 或 P5==OPFLAG_SEEKEQ 且 P4 為 P4_KEYINFO 物件的情況下使用。此外,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()。當此指令與 BeginSubrtn 結合使用時,P3 應設為 1,否則設為 0。
此指令不會變更暫存器 P1 中的值。
位元組碼引擎不使用 P2。但是,如果 P2 為正數且小於目前位址,則 CLI 中的「EXPLAIN」輸出格式器會縮排從 P2 指令到目前 Return 之前的所有指令。P2 應該是此指令返回的子例程中的第一個指令。因此,P2 值是位元組碼縮排提示。請參閱 wherecode.c 和 shell.c 中的 tag-20220407a。
Rewind(倒帶) P1 的下一個 Rowid 或 Column 或 Next 指令將參考資料庫表或索引中的第一個項目。如果表或索引為空,則立即跳轉到 P2。如果表或索引不為空,則繼續執行後續指令。 如果 P2 為零,則表示 P1 表永遠不為空,因此永遠不會執行跳轉。
RowCell(列儲存格) P1 和 P2 都是開啟的游標。兩者都必須開啟於相同類型的表:intkey 或索引。此指令用於將目前列從 P2 複製到 P1。如果游標開啟於 intkey 表,則暫存器 P3 包含與 P1 中的新記錄一起使用的列 ID。如果它們開啟於索引表,則不使用 P3。 此指令之後必須接著設定 OPFLAG_PREFORMAT 旗標的 Insert 或 InsertIdx 指令,才能完成插入操作。
RowData(列資料) 將游標 P1 目前指向的列的完整內容寫入暫存器 P2。此操作不涉及任何資料解析,僅將資料庫檔案中的內容原封不動地複製到 P2 暫存器。 如果游標 P1 是索引,則複製的內容是該列的鍵值。如果游標 P2 是表格,則擷取的內容是資料。
P1 游標必須指向真實表格(而非虛擬表格)的有效列(非 NULL 列)。
如果 P3!=0,則此操作碼允許建立指向資料庫頁面的臨時指標。這表示輸出暫存器的內容在游標移動後將失效,包括由其他「儲存」目前游標位置以便寫入同一表格的游標所引起的移動。如果 P3==0,則會將資料複製到記憶體中。P3!=0 的速度較快,但 P3==0 更安全。
如果 P3!=0,則 P2 暫存器的內容不適用於 OP_Result,且任何 OP_Result 都將使 P2 暫存器的內容失效。P2 暫存器的內容會因諸如 Function 之類的操作碼或使用指向同一表格的其他游標而失效。
列ID (Rowid) 將 P1 目前指向的表格項目的鍵值(整數)儲存在暫存器 P2 中。 P1 可以是普通表格或虛擬表格。過去有一個單獨的 OP_VRowid 操作碼用於虛擬表格,但現在此操作碼適用於兩種表格類型。
新增至列集合 (RowSetAdd) 將暫存器 P2 中的整數值插入到暫存器 P1 中的 RowSet 物件。 如果 P2 不是整數,則斷言失敗。
讀取列集合 (RowSetRead) 從 P1 中的 RowSet 物件擷取最小值,並將該值放入暫存器 P3 中。或者,如果 RowSet 物件 P1 最初為空,則保持 P3 不變並跳轉到指令 P2。 測試列集合 (RowSetTest) 假設暫存器 P3 包含一個 64 位元整數值。如果暫存器 P1 包含 RowSet 物件,且該 RowSet 物件包含 P3 中的值,則跳轉到暫存器 P2。否則,將 P3 中的整數插入 RowSet 並繼續執行下一個操作碼。 RowSet 物件針對以不同階段插入整數集合的情況進行了優化,每個集合都不包含重複值。每個集合都由唯一的 P4 值識別。第一個集合必須具有 P4==0,最後一個集合必須具有 P4==-1,而所有其他集合必須具有 P4>0。
這允許以下優化:(a) 當 P4==0 時,無需測試 RowSet 物件是否存在 P3,因為保證它不包含 P3,(b) 當 P4==-1 時,無需插入值,因為它永遠不會被測試,以及 (c) 當插入屬於集合 X 的值時,無需搜尋以查看相同的值是否先前已作為集合 X 的一部分插入(僅需檢查是否先前已作為其他集合的一部分插入)。
儲存點 (Savepoint) 根據 P1 的值,開啟、釋放或回滾由參數 P4 命名的儲存點。要開啟新的儲存點,請設定 P1==0 (SAVEPOINT_BEGIN)。要釋放(提交)現有的儲存點,請設定 P1==1 (SAVEPOINT_RELEASE)。要回滾現有的儲存點,請設定 P1==2 (SAVEPOINT_ROLLBACK)。 淺層複製 (SCopy) 將暫存器 P1 淺層複製到暫存器 P2。 此指令會建立值的淺拷貝。如果值是字串或 blob,則拷貝僅是指向原始值的指標,因此如果原始值發生更改,拷貝也會跟著更改。更糟的是,如果原始值被釋放,拷貝將失效。因此,程式必須保證在拷貝的生命週期內,原始值不會改變。使用 Copy 來建立完整拷貝。
SeekEnd 將游標 P1 定位在 B 樹的末端,以便附加新的項目到 B 樹上。 假設游標僅用於附加,因此如果游標有效,則游標必須已經指向 B 樹的末端,因此不會對游標進行任何更改。
SeekGE 如果游標 P1 指向 SQL 資料表(使用整數鍵的 B 樹),則使用暫存器 P3 中的值作為鍵。如果游標 P1 指向 SQL 索引,則 P3 是由 P4 個暫存器組成的陣列中的第一個,這些暫存器用作未打包的索引鍵。 重新定位游標 P1,使其指向大於或等於鍵值的最小項目。如果沒有大於或等於鍵值的記錄,且 P2 不為零,則跳轉到 P2。
如果游標 P1 是使用 OPFLAG_SEEKEQ 旗標開啟的,則此操作碼會落在與鍵值完全匹配的記錄上,否則會導致跳轉到 P2。當游標為 OPFLAG_SEEKEQ 時,此操作碼後面必須接著一個具有相同參數的 IdxLE 操作碼。如果此操作碼成功,則會跳過 IdxGT 操作碼,但在後續的迴圈迭代中會使用 IdxGT 操作碼。OPFLAG_SEEKEQ 旗標是向 B 樹層提示這是一個等值搜尋。
SeekGT 如果游標 P1 指向 SQL 資料表(使用整數鍵的 B 樹),則使用暫存器 P3 中的值作為鍵。如果游標 P1 指向 SQL 索引,則 P3 是由 P4 個暫存器組成的陣列中的第一個,這些暫存器用作未打包的索引鍵。 重新定位游標 P1,使其指向大於鍵值的最小項目。如果沒有大於鍵值的記錄,且 P2 不為零,則跳轉到 P2。
SeekHit 如有必要,增加或減少游標 P1 的 seekHit 值,使其不小於 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 操作碼,但在後續的迴圈迭代中會使用 IdxGE 操作碼。OPFLAG_SEEKEQ 旗標是向 B 樹層提示這是一個等值搜尋。
SeekLT (搜尋小於) 如果游標 P1 指向 SQL 資料表(使用整數鍵的 B 樹),則使用暫存器 P3 中的值作為鍵。如果游標 P1 指向 SQL 索引,則 P3 是由 P4 個暫存器組成的陣列中的第一個,這些暫存器用作未打包的索引鍵。 重新定位游標 P1,使其指向小於鍵值的最大項目。如果沒有記錄小於鍵值且 P2 不為零,則跳轉至 P2。
此操作碼會將游標設定為反向移動,從尾端往開頭移動。換句話說,游標會被設定為使用Prev,而不是Next。
另請參閱:Found (已找到)、NotFound (未找到)、SeekGt (搜尋大於)、SeekGe (搜尋大於等於)、SeekLe (搜尋小於等於)
SeekRowid (搜尋列 ID) P1 是在 SQL 表 btree(具有整數鍵)上開啟之游標的索引。如果暫存器 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() 陳述式檢查。 此操作碼使用後續 SeekGE 的 P1 到 P4 運算元。在下文中,後續 SeekGE 操作碼的運算元表示為 SeekOP.P1 到 SeekOP.P4。此操作碼也僅使用 P1、P2 和 P5 運算元,並稱為 This.P1、This.P2 和 This.P5。
此操作碼有助於最佳化多欄索引上的 IN 運算子,其中 IN 運算子位於索引的後續條件上,方法是避免在 btree 上進行不必要的搜尋,而是改為 stepping 至 b-tree 的下一行。如果省略此操作碼或此操作碼為無效操作,則會獲得正確的答案。
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 (索引大於等於) 操作碼之前的設定程式碼。
此操作碼的可能結果
- 如果游標最初未指向任何有效行,則會繼續執行後續的 SeekGE 操作碼。
- 如果即使在呼叫 sqlite3BtreeNext() 多達 This.P1 次之後,游標仍然指向目標行之前的行,則也會繼續執行 SeekGE。
- 如果游標指向目標行,是因為它一開始就位於目標行,或者因為一個或多個 sqlite3BtreeNext() 呼叫將游標移至目標行,則跳轉至 This.P2。
- 如果游標起始位置在目標列之前,且呼叫 sqlite3BtreeNext() 將游標移至索引末尾之外(表示目標列確定不存在於 btree 中),則跳轉至 SeekGE.P2,結束迴圈。
- 如果游標最終停留在一個有效列上,且該列在目標列之後(表示目標列不存在於 btree 中),則在 This.P5==0 時跳轉至 SeekOP.P2,或在 This.P5>0 時跳轉至 This.P2。
Sequence(序列) 尋找游標 P1 的下一個可用序列號。將序列號寫入暫存器 P2。此指令執行後,游標上的序列號會遞增。 SequenceTest(序列測試) P1 是一個排序器游標。如果序列計數器目前為零,則跳轉至 P2。無論是否執行跳轉,序列值都會遞增。 SetCookie(設定 Cookie) 將整數值 P3 寫入資料庫 P1 的 cookie 編號 P2。P2==1 為結構描述版本。P2==2 為資料庫格式。P2==3 為建議的頁面快取大小,依此類推。P1==0 為主要資料庫檔案,P1==1 為用於儲存暫存資料表的資料庫檔案。 執行此操作碼之前必須先啟動一個交易。
如果 P2 是 SCHEMA_VERSION cookie(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(軟 Null) 設定暫存器 P1 的值為 MakeRecord 指令看到的 NULL,但不釋放與該暫存器關聯的任何字串或 blob 記憶體,因此如果該值是先前使用 SCopy 複製的字串或 blob,則副本將繼續有效。 Sort(排序) 此操作碼的功能與 Rewind 完全相同,但它會遞增一個用於測試的未公開全域變數。 排序是透過將記錄寫入排序索引,然後回溯該索引並從頭到尾播放來完成的。我們使用 Sort 操作碼而不是 Rewind 來執行回溯,以便全域變數會遞增,並且迴歸測試可以確定最佳化器是否正確地最佳化了排序。
SorterCompare(排序器比較) P1 是一個排序器游標。此指令將暫存器 P3 中記錄 blob 的前綴與排序器游標目前指向的項目的前綴進行比較。僅比較 r[P3] 和排序器記錄的前 P4 個欄位。 如果 P3 或排序器在其重要欄位之一(不計忽略的最後 P4 個欄位)中包含 NULL,則假設比較結果相等。
如果兩個記錄的比較結果相等,則繼續執行下一條指令。如果它們不同,則 跳轉 至 P2。
SorterData(排序器資料) 將排序器游標 P1 的目前排序器資料寫入暫存器 P2。然後清除游標 P3 上的欄位標頭快取。 此操作碼通常用於將記錄從排序器移出,並放入一個暫存器中,該暫存器作為使用 OpenPseudo 建立的虛擬表格游標的來源。 該虛擬表格游標是由參數 P3 標識的。 作為此操作碼的一部分清除 P3 列快取,可以避免我們必須發出單獨的 NullRow 指令來清除該快取。
SorterInsert 暫存器 P2 使用 MakeRecord 指令建立一個 SQL 索引鍵。 此操作碼將該鍵寫入排序器 P1。 項目的數據為 nil。 SorterNext 此操作碼的功能與 Next 類似,不同之處在於 P1 必須是一個已對其調用 SorterSort 操作碼的排序器物件。 此操作碼將游標前進到下一個已排序的記錄,或者如果沒有更多已排序的記錄,則跳轉到 P2。 SorterOpen 此操作碼的功能類似於 OpenEphemeral,不同之處在於它會開啟一個臨時索引,該索引專門設計用於使用外部合併排序演算法對大型表格進行排序。 如果參數 P3 不為零,則表示排序器可以假設考慮每個鍵的前 P3 個欄位的穩定排序足以產生所需的結果。
SorterSort 將所有記錄插入由 P1 標識的排序器物件後,調用此操作碼以實際執行排序。 如果沒有要排序的記錄,則跳轉至 P2。 SqlExec 執行 P4 字串中指定的 SQL 陳述式(一個或多個)。 P1 參數是一個選項的位元遮罩
0x0001 在 P4 中的陳述式執行時停用 Auth 和 Trace 回呼。
0x0002 在 P4 中的陳述式執行時將 db->nAnalysisLimit 設定為 P2。
String 長度為 P1(位元組)的字串值 P4 儲存在暫存器 P2 中。 如果 P3 不為零且暫存器 P3 的內容等於 P5,則暫存器 P2 的資料類型會轉換為 BLOB。 內容是相同的位元組序列,它只是被解釋為 BLOB 而不是字串,就像它被強制轉換一樣。 換句話說
if( P3!=0 and reg[P3]==P5 ) reg[P2] := CAST(reg[P2] as BLOB)
String8 P4 指向一個以 null 結尾的 UTF-8 字串。 此操作碼在第一次執行之前會轉換為 String 操作碼。 在此轉換過程中,會計算字串 P4 的長度並將其儲存為 P1 參數。 Subtract 從暫存器 P2 的值中減去暫存器 P1 中的值,並將結果儲存在暫存器 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 異常,則會設定此旗標),則也可能開啟一個語句交易。更具體地說,如果資料庫連線目前不在自動提交模式,或者還有其他作用中的語句,則會開啟一個語句交易。語句交易允許在發生錯誤後回滾此 VDBE 所做的更改,而無需回滾整個交易。如果沒有遇到錯誤,語句交易將在 VDBE 停止時自動提交。
如果 P5!=0,則此操作碼還會根據 P3 檢查結構描述 Cookie,並根據 P4 檢查結構描述產生計數器。每當資料庫結構描述更改時,Cookie 的值就會更改。此操作用於檢測 Cookie 何時已更改以及目前處理程序何時需要重新讀取結構描述。如果 P3 中的結構描述 Cookie 與資料庫標頭中的結構描述 Cookie 不同,或者 P4 中的結構描述產生計數器與目前的產生計數器不同,則會引發 SQLITE_SCHEMA 錯誤並停止執行。然後,sqlite3_step() 包裝函式可能會重新準備語句並從頭開始重新執行。
型別檢查 將親和性套用至從 P1 開始的 P2 個暫存器範圍。從 P4 中的表格物件取得親和性。如果任何值無法強制轉換為正確的型別,則會引發錯誤。 此操作碼類似於親和性,不同之處在於此操作碼會強制暫存器型別符合表格欄位型別。這用於實作「嚴格親和性」。
只有在 P3 為零時才會檢查 GENERATED ALWAYS AS ... STATIC 欄位。當 P3 不為零時,不會對靜態產生的欄位執行型別檢查。虛擬欄位是在查詢時計算的,因此永遠不會檢查它們。
前提條件
- P2 應該是 P4 表格中非虛擬欄位的數量。
- 表格 P4 應為 STRICT 表格。
如果任何前提條件為 false,則會發生斷言錯誤。
真空 清理整個資料庫 P1。"main" 的 P1 為 0,附加資料庫的 P1 為 2 或更大。"temp" 資料庫不能清理。 如果 P2 不為零,則它是一個包含字串的暫存器,該字串是清理結果應寫入的檔案。當 P2 為零時,清理會覆蓋原始資料庫。
變數 將繫結參數 P1 的值傳輸到暫存器 P2 VBegin P4 可以是指向 sqlite3_vtab 結構的指標。如果是,則呼叫該表格的 xBegin 方法。 此外,無論是否設定 P4,請檢查這是否不是從虛擬表格 xSync() 方法的回呼內呼叫的。如果是,錯誤碼將設定為 SQLITE_LOCKED。
VCheck P4 是指向表格物件的指標,該表格物件是結構描述 P1 中支援 xIntegrity() 方法的虛擬表格。此操作碼使用 P3 作為整數引數,為該虛擬表格執行 xIntegrity() 方法。如果回報錯誤,則表格名稱會加在錯誤訊息的前面,並且該訊息會儲存在 P2 中。如果沒有看到錯誤,則暫存器 P2 設定為 NULL。 VColumn 將游標 P1 的虛擬表格的目前資料列的第 P2 個欄位的值儲存在暫存器 P3 中。 如果在 UPDATE 操作期間使用 VColumn 指令碼擷取不變欄位的值,則 P5 值為 OPFLAG_NOCHNG。這將導致 sqlite3_vtab_nochange() 函式在虛擬表格實作的 xColumn 方法內返回 true。P5 欄位可能也包含其他位元(OPFLAG_LENGTHARG 或 OPFLAG_TYPEOFARG),但這些位元未被 VColumn 使用。
VCreate P2 是一個暫存器,其中存放了資料庫 P1 中虛擬表格的名稱。呼叫該表格的 xCreate 方法。 VDestroy P4 是資料庫 P1 中虛擬表格的名稱。呼叫該表格的 xDestroy 方法。 VFilter P1 是使用 VOpen 開啟的游標。P2 是一個位址,如果過濾後的結果集為空,則跳轉到該位址。 P4 可以是 NULL 或由模組的 xBestIndex 方法產生的字串。P4 字串的解讀由模組實作決定。
此指令碼會在 P1 指定的虛擬表格上呼叫 xFilter 方法。傳遞給 xFilter 的整數查詢計畫參數儲存在暫存器 P3 中。暫存器 P3+1 儲存要傳遞給 xFilter 方法的 argc 參數。暫存器 P3+2..P3+1+argc 是傳遞給 xFilter 作為 argv 的 argc 個額外參數。當傳遞給 xFilter 時,暫存器 P3+2 成為 argv[0]。
如果過濾後的結果集為空,則會跳轉到 P2。
VInitIn 將暫存器 P2 設定為指向游標 P1 的 ValueList 物件的指標,其中快取暫存器為 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 中的值會作為 zName 參數傳遞給 xRename 方法。 VUpdate P4 是一個指向虛擬表格物件的指標,即 sqlite3_vtab 結構。此指令碼會呼叫對應的 xUpdate 方法。從 P3 開始的 P2 個連續記憶體單元的值會傳遞給 xUpdate 呼叫。暫存器 (P3+P2-1) 中的值對應於傳遞給 xUpdate 的 argv 陣列的第 p2 個元素。 xUpdate 方法將執行 DELETE 或 INSERT 或兩者。argv[0] 元素(對應於記憶體單元 P3)是要刪除的列的列 ID。如果 argv[0] 為 NULL,則不會執行刪除。argv[1] 元素是新列的列 ID。這可以是 NULL,讓虛擬表格自行選擇新的列 ID。陣列中的後續元素是新列中欄位的值。
如果 P2==1,則不執行插入。argv[0] 是要刪除的列的列 ID。
P1 是一個布林旗標。如果它被設定為 true 且 xUpdate 呼叫成功,則 sqlite3_last_insert_rowid() 返回的值會被設定為剛插入的列的列 ID 的值。
P5 是在插入或更新時發生約束失敗的情況下要套用的錯誤動作(OE_Replace、OE_Fail、OE_Ignore 等)。
Yield 將程式計數器與暫存器 P1 的值交換。這會產生讓出執行權給一個協程的效果。 如果以此指令啟動的協程以 Yield 或 Return 結束,則繼續執行下一條指令。但如果以此指令啟動的協程以 EndCoroutine 結束,則跳轉到 P2,而不是繼續執行下一條指令。
另請參閱:InitCoroutine
ZeroOrNull(零或空值) 如果暫存器 P1 和 P3 都「不是」空值 (NULL),則在暫存器 P2 中儲存零。如果暫存器 P1 或 P3 其中一個是空值 (NULL),則將空值 (NULL) 放入暫存器 P2。
本頁最後修改時間:UTC 時間 2024-05-03 13:40:22