原文標題:Register A Callback To Handle SQLITE_BUSY Errors
int sqlite3_busy_handle(sqlite3*, int(*)(void *, int), void *);
這個函數設定了一個回呼函數,當試圖開啟一個在另一個線程或進程中被鎖住的表時,該回呼函數可能會被調用。 (譯註:這裡用的是"可能"被調用,後面會有解譯)
當遇到資料庫被鎖時,如果回呼函數是NULL,那麼SQLITE_BUSY或者SQLITE_IOERR_BLOCKED會立刻返回;如果回呼函數不為空白時,回呼函數可能會被調用並傳遞給它兩個參數。
第一個參數是當你調用sqlite_busy_handle函數時傳遞給它的一個void*的參數的拷貝;第二個參數是因此這次鎖事件,該回呼函數被調用的次數。
如果回呼函數返回0時,將不再嘗試再次訪問資料庫而返回SQLITE_BUSY或者SQLITE_IOERR_BLOCKED。
如果回呼函數返回非0, 將會不斷嘗試操作資料庫。
當發生鎖爭奪時,回呼函數不保證每次一定會被調用。當SQLITE檢測到調用回呼函數會引發死結現象時,將會直接返回SQLITE_BUSY或者SQLITE_IOERR_BLOCKED而不是調用回呼函數。試想下面的情況,操作A擁有一個可讀鎖並嘗試提升為保留鎖,而操作B擁有一個保留鎖並嘗試提升為獨佔鎖,A無法繼續操作,因為B的保留鎖阻止建立新的保留鎖;而B也無法繼續操作因為A的可讀鎖未釋放,保留鎖就無法提升為獨佔鎖。如果這時兩個操作都調用回呼函數,那麼將都無法繼續處理。因此,在這樣的情況下,操作A將會返回SQLITE_BUSY,以釋放它擁有的可讀鎖,從而使得操作B可以繼續進行。
預設的回呼函數為空白。
當sqlite處於一個大的事務中,以至於記憶體緩衝區無法保留所有修改,此時發生的SQLITE_BUSY錯誤將被轉換成SQLITE_IOERR_BLOCKED錯誤。sqlite此時已經擁有保留鎖,並嘗試提升為獨佔鎖後將緩衝頁面重新整理到資料庫檔案中從而不影響其它並發的讀取操作,如果擷取獨佔鎖失敗,那麼記憶體緩衝區中的資料將處於一個不一致的狀態,此時錯誤碼從相對良好的SQLITE_BUSY提升到為嚴重的SQLITE_IOERR_BLOCKED。錯誤的提升會造成強制的復原。可從CorruptionFollowingBusyError維基頁面上查看這樣處理的重要性的相關討論。(譯註:對於緩衝區不夠的大的事務,sqlite會將部分修改寫入復原日誌中,可參閱資料庫檔案完整性相關的文章)
一個資料庫連接只能有一個回呼函數,設定新的回呼函數將會覆蓋原來的。需要注意,調用sqlite3_busy_timeout()也可能會設定或清除當前的回呼函數。
回呼函數中不要對資料庫連接做任何操作,所做的任何操作,可能帶來未定義的結果。
回呼函數中一定不要關閉資料庫連接,或者關閉引發回呼函數的語句對象。