Index: src/main.c ================================================================== --- src/main.c +++ src/main.c @@ -1504,13 +1504,17 @@ ** If this routine returns non-zero, the lock is retried. If it ** returns 0, the operation aborts with an SQLITE_BUSY error. */ int sqlite3InvokeBusyHandler(BusyHandler *p){ int rc; - if( NEVER(p==0) || p->xFunc==0 || p->nBusy<0 ) return 0; + if( p->xFunc==0 || p->nBusy<0 ){ + p->nTimeout++; + return 0; + } rc = p->xFunc(p->pArg, p->nBusy); if( rc==0 ){ + p->nTimeout++; p->nBusy = -1; }else{ p->nBusy++; } return rc; Index: src/shell.c ================================================================== --- src/shell.c +++ src/shell.c @@ -3615,11 +3615,11 @@ ShellState *pArg, /* Pointer to ShellState */ char **pzErrMsg /* Error msg written here */ ){ sqlite3_stmt *pStmt = NULL; /* Statement to execute. */ int rc = SQLITE_OK; /* Return Code */ - int rc2; + int rc2, rc3; const char *zLeftover; /* Tail of unprocessed SQL */ if( pzErrMsg ){ *pzErrMsg = NULL; } @@ -3716,17 +3716,27 @@ } /* Finalize the statement just executed. If this fails, save a ** copy of the error message. Otherwise, set zSql to point to the ** next statement to execute. */ + rc3 = sqlite3_stmt_retryable(pStmt); rc2 = sqlite3_finalize(pStmt); if( rc!=SQLITE_NOMEM ) rc = rc2; if( rc==SQLITE_OK ){ zSql = zLeftover; while( IsSpace(zSql[0]) ) zSql++; - }else if( pzErrMsg ){ - *pzErrMsg = save_err_msg(db); + }else{ + if( rc==SQLITE_BUSY ){ + if( rc3 ){ + raw_printf(pArg->out, "Busy: might succeed on retry\n"); + }else{ + raw_printf(pArg->out, "Busy: cannot succeed - rollback required\n"); + } + } + if( pzErrMsg ){ + *pzErrMsg = save_err_msg(db); + } } /* clear saved stmt handle */ if( pArg ){ pArg->pStmt = NULL; Index: src/shell.c.in ================================================================== --- src/shell.c.in +++ src/shell.c.in @@ -2255,11 +2255,11 @@ ShellState *pArg, /* Pointer to ShellState */ char **pzErrMsg /* Error msg written here */ ){ sqlite3_stmt *pStmt = NULL; /* Statement to execute. */ int rc = SQLITE_OK; /* Return Code */ - int rc2; + int rc2, rc3; const char *zLeftover; /* Tail of unprocessed SQL */ if( pzErrMsg ){ *pzErrMsg = NULL; } @@ -2356,17 +2356,27 @@ } /* Finalize the statement just executed. If this fails, save a ** copy of the error message. Otherwise, set zSql to point to the ** next statement to execute. */ + rc3 = sqlite3_stmt_retryable(pStmt); rc2 = sqlite3_finalize(pStmt); if( rc!=SQLITE_NOMEM ) rc = rc2; if( rc==SQLITE_OK ){ zSql = zLeftover; while( IsSpace(zSql[0]) ) zSql++; - }else if( pzErrMsg ){ - *pzErrMsg = save_err_msg(db); + }else{ + if( rc==SQLITE_BUSY ){ + if( rc3 ){ + raw_printf(pArg->out, "Busy: might succeed on retry\n"); + }else{ + raw_printf(pArg->out, "Busy: cannot succeed - rollback required\n"); + } + } + if( pzErrMsg ){ + *pzErrMsg = save_err_msg(db); + } } /* clear saved stmt handle */ if( pArg ){ pArg->pStmt = NULL; Index: src/sqlite.h.in ================================================================== --- src/sqlite.h.in +++ src/sqlite.h.in @@ -3762,10 +3762,22 @@ ** [BEGIN|BEGIN EXCLUSIVE] commands do touch the database and so ** sqlite3_stmt_readonly() returns false for those commands. */ int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); +/* +** CAPI3REF: Determine If An SQL Statement Can Be Retried After SQLITE_BUSY +** METHOD: sqlite3_stmt +** +** ^The sqlite3_stmt_retryable(X) interface returns true (non-zero) if +** the previous run of [prepared statement] X failed with an +** [SQLITE_BUSY] error but might succeed if run again. If X has not been +** previously run or if the previous run of X did not return SQLITE_BUSY, +** then the result of this function is a meaningless boolean. +*/ +int sqlite3_stmt_retryable(sqlite3_stmt *pStmt); + /* ** CAPI3REF: Determine If A Prepared Statement Has Been Reset ** METHOD: sqlite3_stmt ** ** ^The sqlite3_stmt_busy(S) interface returns true (non-zero) if the Index: src/sqliteInt.h ================================================================== --- src/sqliteInt.h +++ src/sqliteInt.h @@ -947,10 +947,11 @@ typedef struct BusyHandler BusyHandler; struct BusyHandler { int (*xFunc)(void *,int); /* The busy callback */ void *pArg; /* First arg to busy callback */ int nBusy; /* Incremented with each busy call */ + u32 nTimeout; /* Number of timeouts */ }; /* ** Name of the master database table. The master database table ** is a special table that holds the names and attributes of all Index: src/vdbeInt.h ================================================================== --- src/vdbeInt.h +++ src/vdbeInt.h @@ -391,10 +391,11 @@ bft changeCntOn:1; /* True to update the change-counter */ bft runOnlyOnce:1; /* Automatically expire on reset */ bft usesStmtJournal:1; /* True if uses a statement journal */ bft readOnly:1; /* True for statements that do not write */ bft bIsReader:1; /* True for statements that read */ + bft retryable:1; /* True if the statement might succeed next time */ yDbMask btreeMask; /* Bitmask of db->aDb[] entries referenced */ yDbMask lockMask; /* Subset of btreeMask that requires a lock */ u32 aCounter[7]; /* Counters used by sqlite3_stmt_status() */ char *zSql; /* Text of the SQL statement that generated this */ void *pFree; /* Free this when deleting the vdbe */ Index: src/vdbeapi.c ================================================================== --- src/vdbeapi.c +++ src/vdbeapi.c @@ -1610,10 +1610,18 @@ ** database. */ int sqlite3_stmt_readonly(sqlite3_stmt *pStmt){ return pStmt ? ((Vdbe*)pStmt)->readOnly : 1; } + +/* +** Return true if the prepared statement failed with SQLITE_BUSY on its +** previous run but might succeed if tried again. +*/ +int sqlite3_stmt_retryable(sqlite3_stmt *pStmt){ + return pStmt ? ((Vdbe*)pStmt)->retryable : 0; +} /* ** Return true if the prepared statement is in need of being reset. */ int sqlite3_stmt_busy(sqlite3_stmt *pStmt){ Index: src/vdbeaux.c ================================================================== --- src/vdbeaux.c +++ src/vdbeaux.c @@ -2700,11 +2700,13 @@ }else{ /* The auto-commit flag is true, the vdbe program was successful ** or hit an 'OR FAIL' constraint and there are no deferred foreign ** key constraints to hold up the transaction. This means a commit ** is required. */ + db->busyHandler.nTimeout = 0; rc = vdbeCommit(db, p); + p->retryable = db->busyHandler.nTimeout>0; } if( rc==SQLITE_BUSY && p->readOnly ){ sqlite3VdbeLeave(p); return SQLITE_BUSY; }else if( rc!=SQLITE_OK ){