小巧。快速。可靠。
任選三項。
冒牌表

1. 簡介

冒牌表是附加到與索引相同的 B 樹 的表。冒牌表允許查詢或修改索引的內容,就好像索引是普通表一樣。

冒牌表僅用於分析和偵錯。這不是大多數應用程式開發人員應該了解或甚至知道的特性。冒牌表僅供專家使用。

不當使用冒牌表可能會導致索引損毀,不過,透過執行 REINDEX,可以修復由此產生的任何損毀。

2. 詳細資料

SQLite 中的每個表和每個索引都儲存在資料庫檔案中的一個獨立 B 樹中。每個 B 樹都由其根頁數識別。任何索引或表的根頁數都可以透過查詢 sqlite_schema 表 的「rootpage」欄位來找到。請參閱 索引教學檔案格式 文件,以進一步了解此設計的背景。

通常,表和索引的 B 樹略有不同。表 B 樹包含 64 位元整數鍵和任意資料。64 位元整數鍵是 ROWID。索引 B 樹包含任意二進位鍵,但不包含資料。因此,表 B 樹和索引 B 樹並不直接相容。

不過,WITHOUT ROWID 表的 B 樹與索引 B 樹的格式相同。因此,可以存取索引 B 樹,就好像它是 WITHOUT ROWID 表一樣。

2.1. 手動建立的冒牌表

建立冒充表的其中一種方法是直接編輯 sqlite_schema 表格,以插入描述該表格的新列。例如,假設架構如下

CREATE TABLE t1(a INTEGER PRIMARY KEY,b TEXT,c INT, d INT);
CREATE INDEX t1bc ON t1(b,c);

與 t1bc 索引具有相同結構的 WITHOUT ROWID 表格如下所示

CREATE TABLE t2(b TEXT,c INT,a INT, PRIMARY KEY(b,c,a)) WITHOUT ROWID;

若要針對索引「t1bc」建立永久冒充表「t2」,應先透過執行「PRAGMA writable_schema=ON」來啟用 sqlite_schema 表格的編輯。(請務必注意此 PRAGMA 附帶的警告。錯誤可能會導致資料庫嚴重損毀。)然後在 sqlite_schema 表格中插入新項目,如下所示

INSERT INTO sqlite_schema(type,name,tbl_name,rootpage,sql)
 SELECT 'table','t2','t2',rootpage,
   'CREATE TABLE t2(b,c,a,PRIMARY KEY(b,c,a))WITHOUT ROWID'
   FROM sqlite_schema
  WHERE name='t1bc';

上述 INSERT 陳述式會在 sqlite_schema 表格中新增一個新列,定義一個表格「t2」,其磁碟格式與索引「t1bc」相同,並指向相同的 b 樹。在新增此 sqlite_schema 表格項目後,必須關閉並重新開啟資料庫,才能讓 SQLite 重新讀取架構。然後可以查詢「t2」表格,以查看「t1bc」索引的內容。

2.1.1. 損毀的資料庫

上述手動冒充表方法的一個嚴重問題是,在將新「t2」項目新增至「sqlite_schema」表格後,資料庫檔案在技術上會損毀。「t1bc」索引和「t2」表格都會指向相同的 b 樹。雖然這不會造成任何立即的問題,但應避免執行 VACUUM

可以寫入「t2」表格,從而變更索引的內容。但這樣做會讓「t1bc」索引與其父表格「t1」不同步。不同步的索引可能會導致查詢結果不正確。

由於「t2」冒充表是一種資料庫損毀,因此不建議手動建立冒充表。實際上,除了專家開發人員之外,不鼓勵任何人使用冒充表,但特別不鼓勵手動建立冒充表,因為它們是永久性的。

2.2. 暫時冒充表

建立冒充表的另一種(較安全的)方法,是在不更新磁碟上的「sqlite_schema」表的狀況下,將冒充表的項目加入 SQLite 的內部符號表。這樣一來,冒充表只會存在於單一資料庫連線中,而且只要重新載入架構,就會自動移除。

建立暫時冒充表需要一個特殊的 sqlite3_test_control() 呼叫。與所有其他 SQLite API 不同,sqlite3_test_control() 介面可能會在不同的版本間進行不相容的變更,因此以下所述的機制無法保證在未來的 SQLite 版本中仍然有效。SQLite 開發人員並不認為這是一個問題,因為應用程式不應該使用冒充表。冒充表只用於分析和測試。

若要建立暫時冒充表,請先呼叫 sqlite3_test_control() 如下所示

sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, db, "main", 1, tnum);

「db」參數是指向 資料庫連線 的指標。「main」參數是要建立冒充表的架構名稱。「1」參數會啟用冒充表機制。「tnum」是冒充表應鏡像的索引的根頁面。

在上述 sqlite3_test_control() 呼叫之後,執行 CREATE TABLE 陳述式,定義冒充表。在啟用冒充機制的情況下,這個 CREATE TABLE 陳述式不會建立真正的表,而是只在 SQLite 的內部符號表中加入一個項目。請注意,CREATE TABLE 陳述式必須符合索引的正確格式。如果冒充表的欄位數目不正確,或者不是 WITHOUT ROWID 表,或者與索引 B 樹不相容,則在使用冒充表時會產生 SQLITE_CORRUPT 錯誤。

執行 CREATE TABLE 陳述式後,請依下列方式停用冒充機制

sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, db, "main", 0, 0);

換句話說,執行相同的 sqlite3_test_control() 呼叫,但將最後兩個參數變更為零。

如上所述,將冒充表格載入 SQLite 的內部架構後,冒充表格便可用作任何其他表格。但冒充表格只會對建立它的資料庫連線可見。不會對磁碟上的資料庫檔案進行任何變更。而冒充表格會在下次載入架構時消失。

2.3. .imposter Shell 指令

自 SQLite 3.16.0 (2017-01-02) 起,命令列 shell 包含一個點指令「.imposter」,可執行設定暫時冒充表格的所有工作。暫時冒充表格可依下列方式建構,而非執行多個 sqlite3_test_control() 呼叫,並找出並呼叫相容的 CREATE TABLE 陳述式

.imposter t1bc t2

當然,將範例中顯示的「t1bc」和「t2」替換為所需的索引和冒充表格名稱。.imposter 指令會讀取「t1bc」索引的架構,使用該資訊為冒充表格建構相容的 CREATE TABLE 陳述式,然後執行所有必要的呼叫,以自動建立暫時冒充表格。

3. 摘要和最後警告

冒充表格機制是 SQLite 的威力分析和偵錯工具。但與所有鋒利的工具一樣,如果使用不當,它也可能很危險,並可能導致資料庫檔案損毀。請勿嘗試在應用程式中使用冒充表格。冒充表格旨在供專家在實驗室中使用。

此頁面最後修改於 2022-01-08 05:02:57 UTC