此頁面定義了 SQLite 工作階段擴充套件 的 C 語言介面。這不是教學文件。這些頁面旨在精確描述,而非易於閱讀。教學文件另行提供。
此頁面以單個 HTML 檔案包含所有 C 語言介面資訊。如果您願意,也可以將相同資訊拆分到幾個較小的頁面中,以便於檢視。
此文件是由一個掃描 sqlite3session.h 原始程式碼檔案中註釋的腳本建立的。
#define SQLITE_CHANGESETSTART_INVERT 0x0002
以下旗標可以透過 sqlite3changeset_start_v2 和 sqlite3changeset_start_v2_strm 的第四個參數傳遞。
#define SQLITE_SESSION_CONFIG_STRMSIZE 1
typedef struct sqlite3_changegroup sqlite3_changegroup;
解構函式:sqlite3changegroup_delete()
方法:sqlite3changegroup_add()、sqlite3changegroup_add_change()、sqlite3changegroup_output()
typedef struct sqlite3_changeset_iter sqlite3_changeset_iter;
此物件的實例作為一個游標,用於迭代變更集或修補程式集的元素。
建構函式:sqlite3changeset_start()、sqlite3changeset_start_v2()
typedef struct sqlite3_rebaser sqlite3_rebaser;
**重要提示:**此介面處於實驗階段,如有變更,恕不另行通知。
假設有一個網站託管著狀態為 S0 的資料庫。當對資料庫進行修改使其狀態變為 S1 並記錄一個變更集(「本地」變更集)。然後,從另一個網站收到基於 S0 的變更集(「遠端」變更集)並應用於資料庫。此時資料庫處於 (S1+"遠端") 狀態,確切狀態取決於應用「遠端」變更集時所做的任何衝突解決決策(OMIT 或 REPLACE)。重整變更集即更新變更集以考慮這些衝突解決決策,以便在網路中的其他地方不必再次解決相同的衝突。
例如,如果本地和遠端變更集都包含在 "CREATE TABLE t1(a PRIMARY KEY, b)" 上插入相同鍵值的 INSERT 語句
本地:INSERT INTO t1 VALUES(1, 'v1'); 遠端:INSERT INTO t1 VALUES(1, 'v2');
且衝突解決方式為 REPLACE,則從本地變更集中移除 INSERT 變更(它已被覆蓋)。或者,如果衝突解決方式為 "OMIT",則修改本地變更集以包含
UPDATE t1 SET b = 'v2' WHERE a=1;
本地變更集中的變更重整方式如下:
如果與遠端 UPDATE 衝突且解決方式為 OMIT,則使用遠端變更中的 new.* 值重整 old.* 值。或者,如果解決方式為 REPLACE,則將變更複製到重整後的變更集中,並移除也由衝突的遠端 UPDATE 更新的欄位的更新。如果這表示沒有欄位會被更新,則省略該變更。
本地變更可以同時針對多個遠端變更進行重整。如果單個鍵被多個遠端變更集修改,則在重整本地變更集之前,它們會按如下方式組合:
請注意,來自多個遠端變更集的衝突解析是基於每個欄位組合的,而不是基於每行。這表示在多個遠端 UPDATE 操作的情況下,單個本地變更的某些欄位可能會針對 REPLACE 進行重整,而其他欄位可能會針對 OMIT 進行重整。
為了重整本地變更集,必須先使用 sqlite3changeset_apply_v2() 將遠端變更集應用於本地資料庫,並擷取重整資訊的緩衝區。然後
typedef struct sqlite3_session sqlite3_session;
這個物件的實例是一個可以用來記錄資料庫變更的session。
int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData);
將緩衝區 pData(大小為 nData 位元組)中變更集(或補丁集)内的所有變更新增至變更群組。
如果緩衝區包含一個補丁集,則在同一個變更群組物件上先前呼叫此函式的動作也必須指定補丁集。或者,如果緩衝區包含一個變更集,則先前呼叫此函式的動作也必須如此。否則,將會傳回 SQLITE_ERROR,並且不會將任何變更新增至變更群組。
變更集和變更群組中的資料列是透過其 PRIMARY KEY 欄位中的值來識別。如果變更集中的變更與變更群組中已存在的變更具有相同的 primary key,則該變更會被視為套用至同一資料列。
變更群組中尚未出現的資料列變更將會直接複製到其中。或者,如果新的變更集和變更群組都包含套用至單一資料列的變更,則變更群組的最終內容取決於每種變更的類型,如下所示:
現有變更 | 新變更 | 輸出變更 |
---|---|---|
INSERT | INSERT | 新變更將會被忽略。如果新的變更集是在已新增至變更群組的變更集之後立即記錄的,則不會發生這種情況。 |
INSERT | UPDATE | INSERT 變更將會保留在變更群組中。INSERT 變更中的值將會被修改,如同資料列是由現有變更插入,然後再根據新變更進行更新一樣。 |
INSERT | DELETE | 現有的 INSERT 將會從變更群組中移除。DELETE 不會被新增。 |
UPDATE | INSERT | 新變更將會被忽略。如果新的變更集是在已新增至變更群組的變更集之後立即記錄的,則不會發生這種情況。 |
UPDATE | UPDATE | 現有的 UPDATE 將會保留在變更群組中。它將會被修改,使其伴隨的值如同資料列先由現有變更更新,然後再由新變更更新一樣。 |
UPDATE | DELETE | 現有的 UPDATE 將會在變更群組中由新的 DELETE 取代。 |
DELETE | INSERT | 如果新變更插入的資料列中有一個或多個欄位值與現有變更刪除的資料列中的值不同,則現有的 DELETE 將會在變更群組中由 UPDATE 取代。否則,如果插入的資料列與刪除的資料列完全相同,則現有的 DELETE 將會被捨棄。 |
DELETE | UPDATE | 新變更將會被忽略。如果新的變更集是在已新增至變更群組的變更集之後立即記錄的,則不會發生這種情況。 |
DELETE | DELETE | 新變更將會被忽略。如果新的變更集是在已新增至變更群組的變更集之後立即記錄的,則不會發生這種情況。 |
如果新的變更集包含對變更群組中已存在表格的變更,則表格的欄位數和 primary key 欄位的位置必須一致。如果不是這種情況,此函式將會因 SQLITE_SCHEMA 而失敗。例外情況是,如果已使用 sqlite3changegroup_schema() API 為變更群組物件設定了資料庫結構描述,則可以合併單一表格中不同欄位數的變更集,前提是它們在其他方面相容。
如果輸入的變更集看起來已損毀且偵測到損毀,則會傳回 SQLITE_CORRUPT。或者,如果在處理過程中發生記憶體不足的情況,此函式將會傳回 SQLITE_NOMEM。
在所有情況下,如果發生錯誤,則變更群組最終內容的狀態未定義。如果沒有發生錯誤,則會傳回 SQLITE_OK。
int sqlite3changegroup_add_change( sqlite3_changegroup*, sqlite3_changeset_iter* );
此函式將作為第二個引數傳遞的迭代器目前指示的單一變更新增至變更群組物件。新增變更的規則與 sqlite3changegroup_add() 的說明相同。
如果變更已成功新增至變更群組,則會傳回 SQLITE_OK。否則,將會傳回 SQLite 錯誤碼。
呼叫此函式時,迭代器必須指向有效的項目。如果不是,則會傳回 SQLITE_ERROR,並且不會將任何變更新增至變更群組。此外,迭代器不得使用 SQLITE_CHANGESETAPPLY_INVERT 旗標開啟。在這種情況下,也會傳回 SQLITE_ERROR。
void sqlite3changegroup_delete(sqlite3_changegroup*);
int sqlite3changegroup_new(sqlite3_changegroup **pp);
sqlite3_changegroup 物件用於將兩個或多個變更集(或修補程式集)組合成單個變更集(或修補程式集)。單個 changegroup 物件可以組合變更集或修補程式集,但不能同時組合兩者。輸出格式始終與輸入格式相同。
如果成功,此函式會返回 SQLITE_OK,並在返回之前使用指向新的 sqlite3_changegroup 物件的指標填充 (*pp)。呼叫者最終應使用 sqlite3changegroup_delete() 呼叫來釋放返回的物件。如果發生錯誤,則會返回 SQLite 錯誤代碼(即 SQLITE_NOMEM),並且 *pp 設定為 NULL。
sqlite3_changegroup 物件的常見使用模式如下:
在 new() 和 delete() 呼叫之間可以進行任意數量的 add() 和 output() 呼叫,並且可以按任意順序進行。
除了常規的 sqlite3changegroup_add() 和 sqlite3changegroup_output() 函式之外,還有串流版本 sqlite3changegroup_add_strm() 和 sqlite3changegroup_output_strm() 可用。
int sqlite3changegroup_output( sqlite3_changegroup*, int *pnData, /* OUT: Size of output buffer in bytes */ void **ppData /* OUT: Pointer to output buffer */ );
獲取包含表示 changegroup 目前內容的變更集(或修補程式集)的緩衝區。如果 changegroup 的輸入本身是變更集,則輸出是變更集。或者,如果輸入是修補程式集,則輸出也是修補程式集。
與 sqlite3session_changeset() 和 sqlite3session_patchset() 函式的輸出一樣,此函式的輸出中,所有與單個表格相關的變更都組合在一起。表格的顯示順序與添加到 changegroup 的第一個變更集相同。如果添加到 changegroup 的第二個或後續變更集包含第一個變更集中未出現的表格的變更,則它們會附加到輸出變更集的末尾,同樣按照它們首次遇到的順序排列。
如果發生錯誤,則會返回 SQLite 錯誤代碼,並且輸出變數 (*pnData) 和 (*ppData) 設定為 0。否則,返回 SQLITE_OK,並且輸出變數分別設定為輸出緩衝區的大小和指向輸出緩衝區的指標。在這種情況下,呼叫者有責任最終使用 sqlite3_free() 呼叫釋放緩衝區。
int sqlite3changegroup_schema(sqlite3_changegroup*, sqlite3*, const char *zDb);
此方法可用於選擇性地強制執行規則,即添加到 changegroup 的變更集必須與資料庫 zDb(「main」、「temp」或附加資料庫的名稱)的結構描述相符。如果呼叫 sqlite3changegroup_add() 來添加與已配置結構描述不相容的變更集,則會返回 SQLITE_SCHEMA,並且 changegroup 物件將處於未定義狀態。
變更集結構描述與資料庫結構描述的相容性判斷方式與 sqlite3changeset_apply() 相同。具體來說,對於變更集中的每個表格,都存在一個具有以下條件的資料庫表格:
changegroup 物件的輸出始終與使用此函式指定的資料庫具有相同的結構描述。如果傳遞給 sqlite3changegroup_add() 的變更集的欄位數量少於資料庫結構描述中相應表格的欄位數量,則使用資料庫結構描述中的預設欄位值填充這些欄位。這使得在 changegroup 中組合對單個表格具有不同欄位數量的變更集成為可能,前提是它們在其他方面相容。
int sqlite3changeset_concat( int nA, /* Number of bytes in buffer pA */ void *pA, /* Pointer to buffer containing changeset A */ int nB, /* Number of bytes in buffer pB */ void *pB, /* Pointer to buffer containing changeset B */ int *pnOut, /* OUT: Number of bytes in output changeset */ void **ppOut /* OUT: Buffer containing output changeset */ );
此函數用於將兩個變更集 A 和 B 串接成單個變更集。結果變更集等同於先應用變更集 A,再應用變更集 B。
此函數使用 sqlite3_changegroup 物件來合併兩個輸入變更集。呼叫它的結果類似於以下程式碼片段:
sqlite3_changegroup *pGrp; rc = sqlite3_changegroup_new(&pGrp); if( rc==SQLITE_OK ) rc = sqlite3changegroup_add(pGrp, nA, pA); if( rc==SQLITE_OK ) rc = sqlite3changegroup_add(pGrp, nB, pB); if( rc==SQLITE_OK ){ rc = sqlite3changegroup_output(pGrp, pnOut, ppOut); }else{ *ppOut = 0; *pnOut = 0; }
有關詳細資訊,請參閱下面的 sqlite3_changegroup 文件。
int sqlite3changeset_conflict( sqlite3_changeset_iter *pIter, /* Changeset iterator */ int iVal, /* Column number */ sqlite3_value **ppValue /* OUT: Value from conflicting row */ );
此函數應僅與傳遞給衝突處理器回呼函數的迭代器物件搭配使用,該回呼函數由 sqlite3changeset_apply() 以 SQLITE_CHANGESET_DATA 或 SQLITE_CHANGESET_CONFLICT 呼叫。如果在任何其他迭代器上呼叫此函數,則會返回 SQLITE_MISUSE,並且 *ppValue 會被設為 NULL。
參數 iVal 必須大於或等於 0,且小於受目前變更影響的表格中的欄位數。否則,會返回 SQLITE_RANGE,並且 *ppValue 會被設為 NULL。
如果成功,此函數會將 *ppValue 設為指向一個受保護的 sqlite3_value 物件,該物件包含與目前衝突處理器回呼關聯的「衝突列」中的第 iVal 個值,並返回 SQLITE_OK。
如果發生其他錯誤(例如 OOM 狀況),則會返回 SQLite 錯誤碼,並且 *ppValue 會被設為 NULL。
int sqlite3changeset_finalize(sqlite3_changeset_iter *pIter);
此函數用於完成以 sqlite3changeset_start() 配置的迭代器。
此函數應僅在使用 sqlite3changeset_start() 函數建立的迭代器上呼叫。如果應用程式使用傳遞給衝突處理器的迭代器(由 sqlite3changeset_apply() 傳遞)呼叫此函數,則會立即返回 SQLITE_MISUSE,且呼叫無效。
如果在呼叫 sqlite3changeset_xxx() 函數時遇到錯誤(例如 sqlite3changeset_next() 中的 SQLITE_CORRUPT 或 sqlite3changeset_new() 中的 SQLITE_NOMEM),則此函數會返回對應於該錯誤的錯誤碼。否則,會返回 SQLITE_OK。這是為了允許以下模式(虛擬碼):
sqlite3changeset_start(); while( SQLITE_ROW==sqlite3changeset_next() ){ // Do something with change. } rc = sqlite3changeset_finalize(); if( rc!=SQLITE_OK ){ // An error has occurred }
int sqlite3changeset_fk_conflicts( sqlite3_changeset_iter *pIter, /* Changeset iterator */ int *pnOut /* OUT: Number of FK violations */ );
此函數只能與傳遞給 SQLITE_CHANGESET_FOREIGN_KEY 衝突處理器回呼的迭代器一起呼叫。在這種情況下,它會將輸出變數設定為目標資料庫中已知外鍵違規的總數,並返回 SQLITE_OK。
在所有其他情況下,此函數會返回 SQLITE_MISUSE。
int sqlite3changeset_invert( int nIn, const void *pIn, /* Input changeset */ int *pnOut, void **ppOut /* OUT: Inverse of input */ );
此函數用於「反轉」變更集物件。將反轉的變更集應用於資料庫會反轉應用未反轉變更集的效果。具體來說:
此函數不會改變變更在變更集內出現的順序。它僅反轉每個個別變更的意義。
如果成功,則會將指向包含反轉變更集的緩衝區的指標儲存在 *ppOut 中,將相同緩衝區的大小儲存在 *pnOut 中,並返回 SQLITE_OK。如果發生錯誤,*pnOut 和 *ppOut 都會歸零,並返回 SQLite 錯誤碼。
呼叫者有責任在此函數成功呼叫後最終呼叫 sqlite3_free() 來釋放 *ppOut 指標的緩衝區配置。
警告/待辦事項:此函數目前假設輸入是有效的變更集。如果不是,則結果未定義。
int sqlite3changeset_new( sqlite3_changeset_iter *pIter, /* Changeset iterator */ int iVal, /* Column number */ sqlite3_value **ppValue /* OUT: New value (or NULL pointer) */ );
傳遞給此函式的 pIter 參數可以是透過 sqlite3changeset_apply() 傳遞給衝突處理器的迭代器,也可以是透過 sqlite3changeset_start() 建立的迭代器。在後一種情況下,最近一次呼叫 sqlite3changeset_next() 必須返回 SQLITE_ROW。此外,只有當迭代器目前指向的變更類型是 SQLITE_UPDATE 或 SQLITE_INSERT 時才能呼叫此函式。否則,此函式會返回 SQLITE_MISUSE 並將 *ppValue 設為 NULL。
參數 iVal 必須大於或等於 0,且小於受目前變更影響的表格中的欄位數。否則,會返回 SQLITE_RANGE,並且 *ppValue 會被設為 NULL。
如果成功,此函式會將 *ppValue 設為指向一個受保護的 sqlite3_value 物件,該物件包含儲存在 UPDATE 或 INSERT 變更中的新資料列值向量中的第 iVal 個值,並返回 SQLITE_OK。如果變更是 UPDATE 且不包含請求欄位的新值,則 *ppValue 會設為 NULL 並返回 SQLITE_OK。此函式的名稱來自於它類似於更新或刪除觸發器中可用的「new.*」欄位。
如果發生其他錯誤(例如 OOM 狀況),則會返回 SQLite 錯誤碼,並且 *ppValue 會被設為 NULL。
int sqlite3changeset_next(sqlite3_changeset_iter *pIter);
此函式只能與透過 sqlite3changeset_start() 函式建立的迭代器一起使用。如果在 sqlite3changeset_apply() 傳遞給衝突處理器回呼的迭代器上呼叫此函式,則會返回 SQLITE_MISUSE,且呼叫無效。
在透過 sqlite3changeset_start() 建立迭代器之後,它不會立即指向變更集中的任何變更。假設變更集不是空的,則第一次呼叫此函式會將迭代器推進至指向變更集中的第一個變更。後續每次呼叫都會將迭代器推進至指向變更集中的下一個變更(如果有的話)。如果沒有發生錯誤,且在呼叫 sqlite3changeset_next() 推進迭代器後,迭代器指向一個有效的變更,則會返回 SQLITE_ROW。否則,如果已訪問變更集中的所有變更,則會返回 SQLITE_DONE。
如果發生錯誤,則會返回 SQLite 錯誤碼。可能的錯誤碼包括 SQLITE_CORRUPT(如果變更集緩衝區損壞)或 SQLITE_NOMEM(記憶體不足)。
int sqlite3changeset_old( sqlite3_changeset_iter *pIter, /* Changeset iterator */ int iVal, /* Column number */ sqlite3_value **ppValue /* OUT: Old value (or NULL pointer) */ );
傳遞給此函式的 pIter 參數可以是透過 sqlite3changeset_apply() 傳遞給衝突處理器的迭代器,也可以是透過 sqlite3changeset_start() 建立的迭代器。在後一種情況下,最近一次呼叫 sqlite3changeset_next() 必須返回 SQLITE_ROW。此外,只有當迭代器目前指向的變更類型是 SQLITE_DELETE 或 SQLITE_UPDATE 時才能呼叫此函式。否則,此函式會返回 SQLITE_MISUSE 並將 *ppValue 設為 NULL。
參數 iVal 必須大於或等於 0,且小於受目前變更影響的表格中的欄位數。否則,會返回 SQLITE_RANGE,並且 *ppValue 會被設為 NULL。
如果成功,此函式會將 *ppValue 設為指向一個受保護的 sqlite3_value 物件,該物件包含儲存在 UPDATE 或 DELETE 變更中的原始資料列值向量中的第 iVal 個值,並返回 SQLITE_OK。此函式的名稱來自於它類似於更新或刪除觸發器中可用的「old.*」欄位。
如果發生其他錯誤(例如 OOM 狀況),則會返回 SQLite 錯誤碼,並且 *ppValue 會被設為 NULL。
int sqlite3changeset_op( sqlite3_changeset_iter *pIter, /* Iterator object */ const char **pzTab, /* OUT: Pointer to table name */ int *pnCol, /* OUT: Number of columns in table */ int *pOp, /* OUT: SQLITE_INSERT, DELETE or UPDATE */ int *pbIndirect /* OUT: True for an 'indirect' change */ );
傳遞給此函式的 pIter 參數可以是透過 sqlite3changeset_apply() 傳遞給衝突處理器的迭代器,也可以是透過 sqlite3changeset_start() 建立的迭代器。在後一種情況下,最近一次呼叫 sqlite3changeset_next() 必須返回 SQLITE_ROW。如果不是這種情況,此函式會返回 SQLITE_MISUSE。
參數 pOp、pnCol 和 pzTab 不可為 NULL。返回時,會透過這些指標設定三個輸出。
*pOp 會被設定為 SQLITE_INSERT、SQLITE_DELETE 或 SQLITE_UPDATE 其中之一,取決於迭代器目前指向的變更類型;
*pnCol 會被設定為受變更影響的表格中的欄位數;以及
*pzTab 會被設定為指向一個以 nul 結尾的 utf-8 編碼字串,該字串包含受目前變更影響的表格名稱。該緩衝區在對迭代器呼叫 sqlite3changeset_next() 或衝突處理函式返回之前一直有效。
如果 pbIndirect 不為 NULL,則 *pbIndirect 會被設定為 true (1)(如果變更是間接變更),或 false (0)(否則)。有關直接和間接變更的說明,請參閱 sqlite3session_indirect() 的文件。
如果沒有發生錯誤,則返回 SQLITE_OK。如果發生錯誤,則返回 SQLite 錯誤碼。在這種情況下,輸出變數的值可能不可信。
int sqlite3changeset_pk( sqlite3_changeset_iter *pIter, /* Iterator object */ unsigned char **pabPK, /* OUT: Array of boolean - true for PK cols */ int *pnCol /* OUT: Number of entries in output array */ );
對於每個修改過的表格,變更集包含以下內容
此函式用於查找哪些欄位組成了迭代器 pIter 目前指向的變更所修改的表格的主鍵。如果成功,*pabPK 會被設定為指向一個包含 nCol 個項目的陣列,其中 nCol 是表格中的欄位數。如果對應的欄位是表格主鍵的一部分,則 *pabPK 的元素會被設定為 0x01,否則設定為 0x00。
如果參數 pnCol 不為 NULL,則 *pnCol 會被設定為表格中的欄位數。
如果在迭代器未指向有效項目時呼叫此函式,則返回 SQLITE_MISUSE,並將輸出變數歸零。否則,返回 SQLITE_OK,並按照上述說明填充輸出變數。
int sqlite3changeset_upgrade( sqlite3 *db, const char *zDb, int nIn, const void *pIn, /* Input changeset */ int *pnOut, void **ppOut /* OUT: Inverse of input */ );
int sqlite3rebaser_configure( sqlite3_rebaser*, int nRebase, const void *pRebase );
**重要提示:**此介面處於實驗階段,如有變更,恕不另行通知。
設定變更集重定基底物件,根據緩衝區 pRebase(大小為 nRebase 位元組)所描述的衝突解決方案來重定變更集的基底,該緩衝區必須從先前呼叫 sqlite3changeset_apply_v2() 取得。
int sqlite3rebaser_create(sqlite3_rebaser **ppNew);
**重要提示:**此介面處於實驗階段,如有變更,恕不另行通知。
配置一個新的變更集重定基底物件。如果成功,將 (*ppNew) 設定為指向新物件,並返回 SQLITE_OK。否則,如果發生錯誤,則返回 SQLite 錯誤碼(例如 SQLITE_NOMEM),並將 (*ppNew) 設定為 NULL。
void sqlite3rebaser_delete(sqlite3_rebaser *p);
**重要提示:**此介面處於實驗階段,如有變更,恕不另行通知。
刪除變更集重定基底物件和所有相關資源。對於每次成功呼叫 sqlite3rebaser_create(),都應該呼叫一次此函式。
int sqlite3rebaser_rebase( sqlite3_rebaser*, int nIn, const void *pIn, int *pnOut, void **ppOut );
**重要提示:**此介面處於實驗階段,如有變更,恕不另行通知。
參數 pIn 必須指向一個包含大小為 nIn 位元組的變更集的緩衝區。此函式會配置一個緩衝區,並根據作為第一個參數傳遞的重定基底物件的設定,使用重定基底後的變更集副本填充該緩衝區。如果成功,(*ppOut) 會被設定為指向包含重定基底後變更集的新緩衝區,(*pnOut) 會被設定為其大小(以位元組為單位),並返回 SQLITE_OK。呼叫者有責任最終使用 sqlite3_free() 釋放新緩衝區。否則,如果發生錯誤,(*ppOut) 和 (*pnOut) 會被設定為零,並返回 SQLite 錯誤碼。
int sqlite3session_attach( sqlite3_session *pSession, /* Session object */ const char *zTab /* Table name */ );
如果參數 zTab 不為 NULL,則它是附加到作為第一個參數傳遞的工作階段物件的表格的名稱。在啟用工作階段物件時對表格進行的所有後續變更都將被記錄。有關更多詳細資訊,請參閱 sqlite3session_changeset() 的文件。
或者,如果參數 zTab 為 NULL,則會記錄資料庫中所有表格的變更。如果在此呼叫之後將其他表格添加到資料庫(通過執行「CREATE TABLE」語句),則也會記錄新表格的變更。
只有在 CREATE TABLE 陳述式中明確定義了 PRIMARY KEY 的資料表,才會記錄其變更。無論 PRIMARY KEY 是否為「INTEGER PRIMARY KEY」(rowid 別名),都沒有影響。PRIMARY KEY 可以由單個欄位組成,也可以是複合鍵。
如果指定的資料表不存在於資料庫中,並不會產生錯誤。如果指定的資料表沒有 PRIMARY KEY,也不會產生錯誤。然而,在這兩種情況下都不會記錄任何變更。
對於在一個或多個 PRIMARY KEY 欄位中儲存了 NULL 值的個別資料列,不會記錄變更。
如果呼叫順利完成,則會返回 SQLITE_OK。或者,如果發生錯誤,則會返回 SQLite 錯誤代碼(例如 SQLITE_NOMEM)。
從 SQLite 3.22.0 版開始,「sqlite_stat1」資料表是上述某些規則的例外。在 SQLite 中,sqlite_stat1 的結構如下:
CREATE TABLE sqlite_stat1(tbl,idx,stat)
即使 sqlite_stat1 沒有 PRIMARY KEY,也會如同 PRIMARY KEY 為 (tbl,idx) 一樣記錄其變更。此外,也會記錄 (idx IS NULL) 為 true 的資料列的變更。然而,對於此類資料列,在變更集或修補程式集中儲存的是零長度 blob(SQL 值 X''),而不是 NULL 值。這允許此類變更集由 sqlite3changeset_invert()、concat() 和類似函數的舊版實作操作。
sqlite3changeset_apply() 函數在更新 sqlite_stat1 資料表時,會自動將零長度 blob 轉換回 NULL 值。但是,如果應用程式直接在變更集迭代器上呼叫 sqlite3changeset_new()、sqlite3changeset_old() 或 sqlite3changeset_conflict(包括傳遞給衝突處理器回呼的變更集迭代器),則會返回 X'' 值。應用程式必須根據需要自行將 X'' 轉換為 NULL。
舊版(早於 3.22.0)的 sessions 模組無法擷取對 sqlite_stat1 資料表所做的變更。舊版的 sqlite3changeset_apply() 函數會靜默忽略變更集或修補程式集中任何對 sqlite_stat1 資料表的修改。
int sqlite3session_changeset( sqlite3_session *pSession, /* Session object */ int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */ void **ppChangeset /* OUT: Buffer containing changeset */ );
取得包含附加到作為第一個引數傳遞的 session 物件之資料表變更的變更集。如果成功,則將 *ppChangeset 設定為指向包含變更集的緩衝區,並將 *pnChangeset 設定為變更集的大小(以位元組為單位),然後返回 SQLITE_OK。如果發生錯誤,則將 *ppChangeset 和 *pnChangeset 都設定為零,並返回 SQLite 錯誤代碼。
變更集由零個或多個 INSERT、UPDATE 和/或 DELETE 變更組成,每個變更代表對附加資料表單一資料列的變更。INSERT 變更包含新資料庫資料列中每個欄位的值。DELETE 變更包含已刪除資料庫資料列中每個欄位的原始值。UPDATE 變更包含已更新資料庫資料列中每個欄位的原始值,以及每個已更新非主鍵欄位的更新值。UPDATE 變更無法表示修改主鍵欄位值的變更。如果進行此類變更,則在變更集中會以 DELETE 後接 INSERT 的方式表示。
對於在一個或多個 PRIMARY KEY 欄位中儲存 NULL 值的資料列,不會記錄變更。如果插入或刪除此類資料列,則此函數返回的變更集中不會出現對應的變更。如果更新現有的、在 PRIMARY KEY 欄位中儲存一個或多個 NULL 值的資料列,使其所有 PRIMARY KEY 欄位皆非 NULL,則變更集中只會出現 INSERT。同樣地,如果更新現有的、具有非 NULL PRIMARY KEY 值的資料列,使其一個或多個 PRIMARY KEY 欄位設定為 NULL,則產生的變更集僅包含 DELETE 變更。
可以使用透過 sqlite3changeset_start() API 建立的迭代器來遍歷變更集的內容。可以使用 sqlite3changeset_apply() API 將變更集套用到具有相容結構描述的資料庫。
在此函式產生的變更集中,所有與單一表格相關的變更都會被歸類在一起。換句話說,當迭代變更集或將變更集套用到資料庫時,所有與單一表格相關的變更都會在處理下一個表格之前先處理完畢。表格的排序順序與它們附加(或自動附加)到 sqlite3_session 物件的順序相同。與單一表格相關的變更儲存順序未定義。
成功呼叫此函式後,呼叫者有責任最終使用 sqlite3_free() 釋放 *ppChangeset 指向的緩衝區。
一旦表格附加到 session 物件,session 物件就會記錄插入表格中所有新行的主鍵值。它也會記錄任何已刪除或已更新行的原始主鍵值和其他欄位值。對於每個唯一的主鍵值,資料只會記錄一次 - 在 session 的生命週期中,第一次具有該主鍵的行被插入、更新或刪除時。
前一段有一個例外:當插入、更新或刪除一行時,如果其一個或多個主鍵欄位包含 NULL 值,則不會記錄變更。
因此,session 物件會累積兩種类型的記錄 - 僅包含主鍵值的記錄(在使用者插入新記錄時建立)和包含主鍵值以及其他表格欄位原始值的記錄(在使用者刪除或更新記錄時建立)。
呼叫此函式時,會使用累積的記錄和資料庫檔案的目前內容來建立請求的變更集。具體來說:
這意味著,除其他外,如果在 session 物件處於活動狀態時插入一行,然後再將其刪除,則變更集中既不會出現插入也不會出現刪除。或者,如果在 session 物件處於活動狀態時刪除一行,然後再插入具有相同主鍵值的行,則產生的變更集將包含 UPDATE 變更,而不是 DELETE 和 INSERT。
當 session 物件被停用時(請參閱 sqlite3session_enable() API),它在插入、更新或刪除行時不會累積記錄。如果在 session 期間多次寫入同一行,這可能會產生一些反直覺的效果。例如,如果在 session 物件啟用時插入一行,然後在同一個 session 物件停用時刪除該行,則即使刪除發生在 session 停用時,變更集中也不會出現 INSERT 記錄。或者,如果在 session 停用時更新一行的某個欄位,然後在 session 啟用時更新同一行的另一個欄位,則產生的變更集將包含更新兩個欄位的 UPDATE 變更。
sqlite3_int64 sqlite3session_changeset_size(sqlite3_session *pSession);
預設情況下,此函式始終返回 0。為了使其返回有用的結果,必須使用帶有 SQLITE_SESSION_OBJCONFIG_SIZE 動詞的 sqlite3session_object_config() 將 sqlite3_session 物件配置為啟用此 API。
啟用後,此函式會傳回一個上限值(以位元組為單位),表示若呼叫 sqlite3session_changeset() 可能產生的變更集大小。最終的變更集大小可能會等於或小於此函式傳回的位元組大小。
int sqlite3session_config(int op, void *pArg);
sqlite3session_config() 介面用於對 sessions 模組進行全域設定變更,以便根據應用程式的特定需求進行調整。
sqlite3session_config() 介面並非執行緒安全的。如果在任何其他執行緒位於任何其他 sessions 方法內時呼叫它,則結果未定義。此外,如果在建立任何與 sessions 相關的物件後呼叫它,結果也未定義。
sqlite3session_config() 函式的第一個參數必須是以下定義的 SQLITE_SESSION_CONFIG_XXX 常數之一。 作為第二個參數傳遞的 (void*) 值的解釋以及呼叫此函式的效果取決於第一個參數的值。
如果成功,此函式會傳回 SQLITE_OK,否則會傳回 SQLite 錯誤碼。
int sqlite3session_create( sqlite3 *db, /* Database handle */ const char *zDb, /* Name of db (e.g. "main") */ sqlite3_session **ppSession /* OUT: New session object */ );
建立附加到資料庫控制代碼 db 的新 session 物件。如果成功,則會將指向新物件的指標寫入 *ppSession 並傳回 SQLITE_OK。如果發生錯誤,*ppSession 會被設定為 NULL 並傳回 SQLite 錯誤碼(例如 SQLITE_NOMEM)。
可以建立多個附加到單一資料庫控制代碼的 session 物件。
使用此函式建立的 Session 物件應該在關閉其附加的資料庫控制代碼之前,使用 sqlite3session_delete() 函式刪除。如果在刪除 session 物件之前關閉了資料庫控制代碼,則在 session 物件上呼叫任何 session 模組函式(包括 sqlite3session_delete())的結果未定義。
由於 session 模組使用 sqlite3_preupdate_hook() API,因此應用程式無法在已附加一個或多個 session 物件的資料庫控制代碼上註冊 pre-update hook。也無法為已定義 pre-update hook 的資料庫控制代碼建立附加的 session 物件。嘗試執行這兩件事的結果未定義。
session 物件將用於建立資料庫 zDb 中表格的變更集,其中 zDb 是 "main"、"temp" 或附加資料庫的名稱。建立 session 物件時,如果資料庫 zDb 未附加到資料庫,則不是錯誤。
void sqlite3session_delete(sqlite3_session *pSession);
刪除先前使用 sqlite3session_create() 分配的 session 物件。刪除 session 物件後,嘗試將 pSession 與任何其他 session 模組函式一起使用的結果未定義。
必須在關閉 session 物件附加的資料庫控制代碼之前刪除 session 物件。有關詳細資訊,請參閱 sqlite3session_create() 的說明文件。
int sqlite3session_diff( sqlite3_session *pSession, const char *zFromDb, const char *zTbl, char **pzErrMsg );
如果尚未附加到作為第一個參數傳遞的 session 物件,此函式會以與 sqlite3session_attach() 函式相同的方式附加表格 zTbl。如果 zTbl 不存在,或者它沒有主鍵,則此函式為無運算(但不返回錯誤)。
引數 zFromDb 必須是附加到與包含與此函數附加到工作階段的表格相容的表格的相同資料庫控制代碼的資料庫名稱(「main」、「temp」等)。如果表格符合以下條件,則視為相容:
如果表格不相容,則返回 SQLITE_SCHEMA。如果表格相容但沒有任何 PRIMARY KEY 欄位,則這不是錯誤,但不會將任何變更新增到工作階段物件。與其他工作階段 API 一樣,沒有 PRIMARY KEY 的表格將被忽略。
此函數將一組變更新增到工作階段物件,可用於更新資料庫 zFrom 中的表格(稱為「來源表格」),使其內容與附加到工作階段物件的表格(稱為「目標表格」)相同。具體來說:
更明確地說,如果呼叫此函數,然後使用 sqlite3session_changeset() 建構變更集,則在將該變更集應用於資料庫 zFrom 之後,兩個相容表格的內容將會相同。
如果資料庫 zFrom 不存在或不包含所需的相容表格,則會發生錯誤。
如果操作成功,則返回 SQLITE_OK。否則,返回 SQLite 錯誤碼。在這種情況下,如果引數 pzErrMsg 不為 NULL,則 *pzErrMsg 可能會被設定為指向包含英文錯誤訊息的緩衝區。呼叫者有責任使用 sqlite3_free() 釋放此緩衝區。
int sqlite3session_enable(sqlite3_session *pSession, int bEnable);
啟用或停用工作階段物件的變更記錄。啟用後,工作階段物件會記錄對資料庫所做的變更。停用後,則不會記錄。新建立的工作階段物件是啟用的。有關啟用和停用工作階段物件如何影響最終變更集的更多詳細資訊,請參閱 sqlite3session_changeset() 的文件。
將零傳遞給此函數會停用工作階段。傳遞大於零的值會啟用它。傳遞小於零的值是無操作的,可用於查詢工作階段的目前狀態。
返回值表示工作階段物件的最終狀態:如果工作階段被停用,則為 0;如果工作階段被啟用,則為 1。
int sqlite3session_indirect(sqlite3_session *pSession, int bIndirect);
工作階段物件記錄的每個變更都被標記為直接或間接。如果滿足以下任一條件,則變更會被標記為間接:
如果單列受工作階段中多個操作影響,則如果所有操作都符合上述間接變更的條件,則該變更被視為間接變更,否則視為直接變更。
此函數用於設定、清除或查詢工作階段物件的間接標記。如果傳遞給此函數的第二個引數為零,則清除間接標記。如果它大於零,則設定間接標記。傳遞小於零的值不會修改間接標記的目前值,並且可用於查詢指定工作階段物件的間接標記的目前狀態。
回傳值表示 indirect flag 的最終狀態:0 表示清除,1 表示設定。
int sqlite3session_isempty(sqlite3_session *pSession);
如果第一個參數傳入的 session 物件沒有記錄任何附加資料表的變更,則返回非零值。否則,如果已記錄一或多個變更,則返回零。
即使此函數返回零,在 session handle 上呼叫 sqlite3session_changeset() 仍有可能返回一個不包含任何變更的變更集。當附加資料表中的一列被修改,然後稍後恢復原始值時,就會發生這種情況。但是,如果此函數返回非零值,則保證呼叫 sqlite3session_changeset() 將返回一個包含零個變更的變更集。
sqlite3_int64 sqlite3session_memory_used(sqlite3_session *pSession);
此 API 返回目前由作為唯一參數傳入的 session 物件使用的堆積記憶體總量(以位元組為單位)。
int sqlite3session_object_config(sqlite3_session*, int op, void *pArg);
此方法用於在建立 session 物件後配置它。目前第二個參數的有效值只有 SQLITE_SESSION_OBJCONFIG_SIZE 和 SQLITE_SESSION_OBJCONFIG_ROWID。
int sqlite3session_patchset( sqlite3_session *pSession, /* Session object */ int *pnPatchset, /* OUT: Size of buffer at *ppPatchset */ void **ppPatchset /* OUT: Buffer containing patchset */ );
Patchset 和 Changeset 的區別在於:
Patchset blob 可以與所有 sqlite3changeset_xxx API 函數的最新版本一起使用,但 sqlite3changeset_invert() 例外,如果傳入 patchset,它會返回 SQLITE_CORRUPT。同樣地,嘗試將 patchset blob 與舊版本的 sqlite3changeset_xxx API 一起使用也會引發 SQLITE_CORRUPT 錯誤。
由於非主鍵的 "old.*" 欄位被省略,如果將 patchset 傳遞給 sqlite3changeset_apply() API,則不會偵測或報告 SQLITE_CHANGESET_DATA 衝突。其他衝突類型的運作方式與 changeset 相同。
Patchset 中的變更排序方式與 sqlite3session_changeset() 函數產生的 changeset 相同(即單一資料表的所有變更都分組在一起,資料表按附加到 session 物件的順序顯示)。
void sqlite3session_table_filter( sqlite3_session *pSession, /* Session object */ int(*xFilter)( void *pCtx, /* Copy of third arg to _filter_table() */ const char *zTab /* Table name */ ), void *pCtx /* First argument passed to xFilter */ );
第二個參數 (xFilter) 是「篩選器回呼」。對於未附加到 Session 物件的資料表中的列的變更,將呼叫篩選器以決定是否應該追蹤對該資料表列的變更。如果 xFilter 返回 0,則不會追蹤變更。請注意,一旦附加了資料表,就不會再次呼叫 xFilter。
#define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001 #define SQLITE_CHANGESETAPPLY_INVERT 0x0002 #define SQLITE_CHANGESETAPPLY_IGNORENOOP 0x0004 #define SQLITE_CHANGESETAPPLY_FKNOACTION 0x0008
以下標誌可以透過第九個參數傳遞給 sqlite3changeset_apply_v2 和 sqlite3changeset_apply_v2_strm
#define SQLITE_CHANGESET_OMIT 0 #define SQLITE_CHANGESET_REPLACE 1 #define SQLITE_CHANGESET_ABORT 2
衝突處理常式回呼必須返回以下三個值之一。
如果 SQLITE_CHANGESET_DATA 衝突處理常式返回 CHANGESET_REPLACE,則會根據變更的類型更新或刪除衝突列。
如果 SQLITE_CHANGESET_CONFLICT 衝突處理常式返回 CHANGESET_REPLACE,則會從資料庫中移除衝突列,並再次嘗試套用變更。如果第二次嘗試失敗,則在繼續之前會將原始列恢復到資料庫。
#define SQLITE_CHANGESET_DATA 1 #define SQLITE_CHANGESET_NOTFOUND 2 #define SQLITE_CHANGESET_CONFLICT 3 #define SQLITE_CHANGESET_CONSTRAINT 4 #define SQLITE_CHANGESET_FOREIGN_KEY 5
可以作為第二個參數傳遞給衝突處理常式的值。
在這種情況下,衝突列是具有匹配主鍵的資料庫列。
在這種情況下沒有衝突列。呼叫 sqlite3changeset_conflict() API 的結果未定義。
在這種情況下,衝突列是具有匹配主鍵的資料庫列。
不提供目前或衝突列資訊。在提供的 sqlite3_changeset_iter 控制代碼上可以呼叫的唯一函數是 sqlite3changeset_fk_conflicts()。
在這種情況下沒有衝突列。呼叫 sqlite3changeset_conflict() API 的結果未定義。
#define SQLITE_SESSION_OBJCONFIG_SIZE 1 #define SQLITE_SESSION_OBJCONFIG_ROWID 2
以下值可以作為第二個參數傳遞給 sqlite3session_object_config()。
在第一個表格附加到工作階段物件後嘗試修改此設定是錯誤的 (SQLITE_MISUSE)。
通常,沒有明確 PRIMARY KEY 的表格會被工作階段模組忽略。但是,如果設定了此旗標,它的行為就像在這些表格的最左邊欄位插入了 "_rowid_ INTEGER PRIMARY KEY" 欄位一樣。
在第一個表格附加到工作階段物件後嘗試修改此設定是錯誤的 (SQLITE_MISUSE)。
int sqlite3changeset_apply_strm( sqlite3 *db, /* Apply change to "main" db of this handle */ int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */ void *pIn, /* First arg for xInput */ int(*xFilter)( void *pCtx, /* Copy of sixth arg to _apply() */ const char *zTab /* Table name */ ), int(*xConflict)( void *pCtx, /* Copy of sixth arg to _apply() */ int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */ sqlite3_changeset_iter *p /* Handle describing change and conflict */ ), void *pCtx /* First argument passed to xConflict */ ); int sqlite3changeset_apply_v2_strm( sqlite3 *db, /* Apply change to "main" db of this handle */ int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */ void *pIn, /* First arg for xInput */ int(*xFilter)( void *pCtx, /* Copy of sixth arg to _apply() */ const char *zTab /* Table name */ ), int(*xConflict)( void *pCtx, /* Copy of sixth arg to _apply() */ int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */ sqlite3_changeset_iter *p /* Handle describing change and conflict */ ), void *pCtx, /* First argument passed to xConflict */ void **ppRebase, int *pnRebase, int flags ); int sqlite3changeset_concat_strm( int (*xInputA)(void *pIn, void *pData, int *pnData), void *pInA, int (*xInputB)(void *pIn, void *pData, int *pnData), void *pInB, int (*xOutput)(void *pOut, const void *pData, int nData), void *pOut ); int sqlite3changeset_invert_strm( int (*xInput)(void *pIn, void *pData, int *pnData), void *pIn, int (*xOutput)(void *pOut, const void *pData, int nData), void *pOut ); int sqlite3changeset_start_strm( sqlite3_changeset_iter **pp, int (*xInput)(void *pIn, void *pData, int *pnData), void *pIn ); int sqlite3changeset_start_v2_strm( sqlite3_changeset_iter **pp, int (*xInput)(void *pIn, void *pData, int *pnData), void *pIn, int flags ); int sqlite3session_changeset_strm( sqlite3_session *pSession, int (*xOutput)(void *pOut, const void *pData, int nData), void *pOut ); int sqlite3session_patchset_strm( sqlite3_session *pSession, int (*xOutput)(void *pOut, const void *pData, int nData), void *pOut ); int sqlite3changegroup_add_strm(sqlite3_changegroup*, int (*xInput)(void *pIn, void *pData, int *pnData), void *pIn ); int sqlite3changegroup_output_strm(sqlite3_changegroup*, int (*xOutput)(void *pOut, const void *pData, int nData), void *pOut ); int sqlite3rebaser_rebase_strm( sqlite3_rebaser *pRebaser, int (*xInput)(void *pIn, void *pData, int *pnData), void *pIn, int (*xOutput)(void *pOut, const void *pData, int nData), void *pOut );
六個串流 API xxx_strm() 函式與對應的非串流 API 函式具有類似的用途
串流函式 | 非串流等效項 |
---|---|
sqlite3changeset_apply_strm | sqlite3changeset_apply |
sqlite3changeset_apply_strm_v2 | sqlite3changeset_apply_v2 |
sqlite3changeset_concat_strm | sqlite3changeset_concat |
sqlite3changeset_invert_strm | sqlite3changeset_invert |
sqlite3changeset_start_strm | sqlite3changeset_start |
sqlite3session_changeset_strm | sqlite3session_changeset |
sqlite3session_patchset_strm | sqlite3session_patchset |
接受變更集(或修補程式集)作為輸入的非串流函式要求整個變更集儲存在記憶體中的單個緩衝區中。同樣,傳回變更集或修補程式集的函式會透過傳回指向使用 sqlite3_malloc() 分配的單個大型緩衝區的指標來執行此操作。通常這樣很方便。但是,如果在低記憶體環境中執行的應用程式需要處理非常大的變更集,則所需的大量連續記憶體分配可能會變得繁重。
為了避免這個問題,輸入不是透過單個大型緩衝區,而是透過回呼函式傳遞給串流 API 函式,工作階段模組會呼叫該回呼函式以在需要時逐步請求輸入資料。在所有情況下,一對 API 函式參數,例如
int nChangeset, void *pChangeset,
由以下取代
int (*xInput)(void *pIn, void *pData, int *pnData), void *pIn,
每次工作階段模組呼叫 xInput 回呼時,傳遞的第一個參數是提供的 pIn 上下文指標的副本。第二個參數 pData 指向大小為 (*pnData) 位元組的緩衝區。假設沒有發生錯誤,xInput 方法應將最多 (*pnData) 位元組的資料複製到緩衝區中,並在傳回 SQLITE_OK 之前將 (*pnData) 設定為複製的實際位元組數。如果輸入完全耗盡,則應將 (*pnData) 設定為零以指示此情況。或者,如果發生錯誤,則應傳回 SQLite 錯誤碼。在所有情況下,如果 xInput 回呼傳回錯誤,則所有處理都將放棄,並且串流 API 函式會將錯誤碼的副本傳回給呼叫者。
對於 sqlite3changeset_start_strm(),工作階段模組可以在迭代器生命週期的任何時間點呼叫 xInput 回呼。如果此類 xInput 回呼傳回錯誤,則迭代器將進入錯誤狀態,所有後續對迭代器函式的呼叫都將立即失敗,並顯示與 xInput 傳回的相同的錯誤碼。
同樣,傳回變更集(或修補程式集)的串流 API 函式透過回呼函式以區塊形式傳回它們,而不是透過指向單個大型緩衝區的指標。在這種情況下,一對參數,例如
int *pnChangeset, void **ppChangeset,
由以下取代
int (*xOutput)(void *pOut, const void *pData, int nData), void *pOut
xOutput 回呼函式會被呼叫零次或多次,以將資料返回應用程式。每次呼叫傳遞的第一個參數是應用程式提供的 pOut 指標的副本。第二個參數 pData 指向一個大小為 nData 位元組的緩衝區,其中包含要返回的輸出資料區塊。如果 xOutput 回呼函式成功處理提供的資料,它應該返回 SQLITE_OK 表示成功。否則,它應該返回其他 SQLite 錯誤碼。在這種情況下,處理會立即中止,並且串流 API 函式會將 xOutput 錯誤碼的副本返回給應用程式。
sessions 模組永遠不會以小於或等於零的第三個參數呼叫 xOutput 回呼函式。除此之外,不保證返回的資料區塊的大小。
int sqlite3changeset_apply( sqlite3 *db, /* Apply change to "main" db of this handle */ int nChangeset, /* Size of changeset in bytes */ void *pChangeset, /* Changeset blob */ int(*xFilter)( void *pCtx, /* Copy of sixth arg to _apply() */ const char *zTab /* Table name */ ), int(*xConflict)( void *pCtx, /* Copy of sixth arg to _apply() */ int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */ sqlite3_changeset_iter *p /* Handle describing change and conflict */ ), void *pCtx /* First argument passed to xConflict */ ); int sqlite3changeset_apply_v2( sqlite3 *db, /* Apply change to "main" db of this handle */ int nChangeset, /* Size of changeset in bytes */ void *pChangeset, /* Changeset blob */ int(*xFilter)( void *pCtx, /* Copy of sixth arg to _apply() */ const char *zTab /* Table name */ ), int(*xConflict)( void *pCtx, /* Copy of sixth arg to _apply() */ int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */ sqlite3_changeset_iter *p /* Handle describing change and conflict */ ), void *pCtx, /* First argument passed to xConflict */ void **ppRebase, int *pnRebase, /* OUT: Rebase data */ int flags /* SESSION_CHANGESETAPPLY_* flags */ );
將變更集或修補程式集應用於資料庫。這些函式嘗試使用第二個和第三個參數傳遞的變更集中找到的變更來更新附加到控制代碼 db 的「主」資料庫。
傳遞給這些函式的第四個參數 (xFilter) 是「過濾器回呼函式」。如果它不是 NULL,則對於變更集中至少有一個變更影響的每個表格,都會使用表格名稱作為第二個參數呼叫過濾器回呼函式,並將作為第六個參數傳遞的上下文指標的副本作為第一個參數。如果「過濾器回呼函式」返回零,則不會嘗試對表格應用任何變更。否則,如果返回值非零或 xFilter 參數為 NULL,則會嘗試所有與表格相關的變更。
對於未被過濾器回呼函式排除的每個表格,此函式會測試目標資料庫是否包含相容的表格。如果滿足以下所有條件,則表格被視為相容:
如果沒有相容的表格,則不是錯誤,但不會應用與表格相關聯的任何變更。會透過 sqlite3_log() 機制發出錯誤碼 SQLITE_SCHEMA 的警告訊息。變更集中每個表格最多發出一個此類警告。
對於存在相容表格的每個變更,會嘗試根據 UPDATE、INSERT 或 DELETE 變更來修改表格內容。如果無法順利應用變更,則可能會呼叫作為第五個參數傳遞給 sqlite3changeset_apply() 的衝突處理函式。以下說明何時針對每種類型的變更呼叫衝突處理函式。
與 xFilter 參數不同,xConflict 不能傳遞 NULL。傳遞除有效函式指標以外的任何內容作為 xConflict 參數的結果未定義。
每次呼叫衝突處理函式時,它必須返回 SQLITE_CHANGESET_OMIT、SQLITE_CHANGESET_ABORT 或 SQLITE_CHANGESET_REPLACE 其中之一。僅當傳遞給衝突處理函式的第二個參數是 SQLITE_CHANGESET_DATA 或 SQLITE_CHANGESET_CONFLICT 時,才能返回 SQLITE_CHANGESET_REPLACE。如果衝突處理函式返回非法值,則所有已進行的變更都會被回滾,並且對 sqlite3changeset_apply() 的呼叫會返回 SQLITE_MISUSE。sqlite3changeset_apply() 會根據衝突處理函式的每次呼叫返回的值採取不同的動作。有關詳細資訊,請參閱三個可用返回值的說明文件。
如果找到主鍵值相符的資料列,但一個或多個非主鍵欄位的值與變更集中儲存的原始資料列值不同,則會呼叫衝突處理器函式,並將 SQLITE_CHANGESET_DATA 作為第二個引數。如果資料庫表格的欄位數多於變更集中記錄的欄位數,則只會將這些非主鍵欄位的值與目前的資料庫內容進行比較 - 任何尾隨的資料庫表格欄位都會被忽略。
如果在資料庫中找不到主鍵值相符的資料列,則會呼叫衝突處理器函式,並將 SQLITE_CHANGESET_NOTFOUND 作為第二個引數傳遞。
如果嘗試執行 DELETE 操作,但 SQLite 傳回 SQLITE_CONSTRAINT(只有在違反外鍵約束時才會發生),則會呼叫衝突處理器函式,並將 SQLITE_CHANGESET_CONSTRAINT 作為第二個引數傳遞。這包含嘗試執行 DELETE 操作是因為先前呼叫衝突處理器函式傳回 SQLITE_CHANGESET_REPLACE 的情況。
如果嘗試插入資料列失敗,因為資料庫已包含具有相同主鍵值的資料列,則會呼叫衝突處理器函式,並將第二個引數設定為 SQLITE_CHANGESET_CONFLICT。
如果嘗試插入資料列失敗,因為違反了其他約束(例如 NOT NULL 或 UNIQUE),則會呼叫衝突處理器函式,並將第二個引數設定為 SQLITE_CHANGESET_CONSTRAINT。這包含由於先前呼叫衝突處理器函式傳回 SQLITE_CHANGESET_REPLACE 而重新嘗試 INSERT 操作的情況。
如果找到主鍵值相符的資料列,但一個或多個已修改的非主鍵欄位包含的值與變更集中儲存的原始資料列值不同,則會呼叫衝突處理器函式,並將 SQLITE_CHANGESET_DATA 作為第二個引數。由於 UPDATE 變更只包含要修改的非主鍵欄位的值,因此只有這些欄位需要與原始值相符才能避免 SQLITE_CHANGESET_DATA 衝突處理器回呼。
如果在資料庫中找不到主鍵值相符的資料列,則會呼叫衝突處理器函式,並將 SQLITE_CHANGESET_NOTFOUND 作為第二個引數傳遞。
如果嘗試執行 UPDATE 操作,但 SQLite 傳回 SQLITE_CONSTRAINT,則會呼叫衝突處理器函式,並將 SQLITE_CHANGESET_CONSTRAINT 作為第二個引數傳遞。這包含在先前呼叫衝突處理器函式傳回 SQLITE_CHANGESET_REPLACE 之後嘗試執行 UPDATE 操作的情況。
在 xConflict 回呼內執行 SQL 陳述式(包括寫入與回呼相關的表格的陳述式)是安全的。這可以用於進一步自訂應用程式的衝突解決策略。
這些函式所做的所有變更都包含在一個儲存點交易中。如果發生任何其他錯誤(除了嘗試寫入目標資料庫時發生約束失敗之外),則會回復儲存點交易,將目標資料庫還原到其原始狀態,並傳回 SQLite 錯誤碼。
如果輸出參數 (ppRebase) 和 (pnRebase) 不為 NULL,且輸入是一個變更集(而非修補集),則 sqlite3changeset_apply_v2() 可能會在返回之前將 (*ppRebase) 設定為指向一個「rebase」,此 rebase 可與 sqlite3_rebaser API 緩衝區一起使用。在這種情況下,(*pnRebase) 會被設定為緩衝區的大小(以位元組為單位)。呼叫者有責任最終使用 sqlite3_free() 釋放任何此類緩衝區。僅當應用修補集時遇到一個或多個衝突時,才會配置和填入緩衝區。有關更多詳細資訊,請參閱 sqlite3_rebaser API 周圍的註釋。
sqlite3changeset_apply_v2() 及其串流等效項的行為可以透過將支援的標誌的組合作為第 9 個參數傳遞來修改。
請注意,sqlite3changeset_apply_v2() API 仍處於實驗階段,因此可能會有所變動。
int sqlite3changeset_start( sqlite3_changeset_iter **pp, /* OUT: New changeset iterator handle */ int nChangeset, /* Size of changeset blob in bytes */ void *pChangeset /* Pointer to blob containing changeset */ ); int sqlite3changeset_start_v2( sqlite3_changeset_iter **pp, /* OUT: New changeset iterator handle */ int nChangeset, /* Size of changeset blob in bytes */ void *pChangeset, /* Pointer to blob containing changeset */ int flags /* SESSION_CHANGESETSTART_* flags */ );
建立一個用於迭代變更集內容的迭代器。如果成功,*pp 會被設定為指向迭代器控制代碼,並返回 SQLITE_OK。否則,如果發生錯誤,*pp 會被設定為零,並返回 SQLite 錯誤代碼。
以下函式可用於推進和查詢由此函式建立的變更集迭代器
呼叫者有責任最終透過將迭代器傳遞給 sqlite3changeset_finalize() 來銷毀迭代器。包含變更集的緩衝區 (pChangeset) 必須在迭代器被銷毀之前保持有效。
假設變更集 blob 是由 sqlite3session_changeset()、sqlite3changeset_concat() 或 sqlite3changeset_invert() 函式之一建立的,則變更集中所有應用於單一表格的變更都會被分組在一起。這表示當應用程式使用由此函式建立的迭代器迭代變更集時,所有與單一表格相關的變更都會被連續訪問。迭代器不會訪問應用於表格 X 的變更,然後訪問表格 Y 的變更,然後再訪問表格 X 的其他變更。
sqlite3changeset_start_v2() 及其串流等效項的行為可以透過將支援的標誌的組合作為第 4 個參數傳遞來修改。
請注意,sqlite3changeset_start_v2() API 仍處於實驗階段,因此可能會有所變動。