aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/alter.c181
-rw-r--r--src/build.c10
-rw-r--r--src/insert.c115
-rw-r--r--src/os_unix.c104
-rw-r--r--src/prepare.c21
-rw-r--r--src/sqliteInt.h8
-rw-r--r--src/vdbe.c7
-rw-r--r--src/vdbe.h3
-rw-r--r--src/vdbeaux.c13
9 files changed, 284 insertions, 178 deletions
diff --git a/src/alter.c b/src/alter.c
index 5dbd42a28..29ef9e602 100644
--- a/src/alter.c
+++ b/src/alter.c
@@ -844,11 +844,14 @@ void sqlite3AlterRenameColumn(
bQuote = sqlite3Isquote(pNew->z[0]);
sqlite3NestedParse(pParse,
"UPDATE \"%w\".%s SET "
- "sql = sqlite_rename_column(sql, %d, %d, %Q, %Q, %Q) "
+ "sql = sqlite_rename_column(sql, %Q, %Q, %d, %Q, %d) "
"WHERE name NOT LIKE 'sqlite_%%' AND ("
- " type = 'table' OR (type IN ('index', 'trigger') AND tbl_name = %Q)"
+ " type IN ('table', 'view') "
+ " OR (type IN ('index', 'trigger') AND tbl_name = %Q)"
")",
- zDb, MASTER_NAME, iCol, bQuote, zNew, pTab->zName, zOld, pTab->zName
+ zDb, MASTER_NAME,
+ zDb, pTab->zName, iCol, zNew, bQuote,
+ pTab->zName
);
/* Drop and reload the database schema. */
@@ -896,6 +899,7 @@ struct RenameCtx {
RenameToken *pList; /* List of tokens to overwrite */
int nList; /* Number of tokens in pList */
int iCol; /* Index of column being renamed */
+ Table *pTab; /* Table being ALTERed */
const char *zOld; /* Old column name */
};
@@ -942,7 +946,13 @@ static void renameTokenFree(sqlite3 *db, RenameToken *pToken){
}
}
-static void renameTokenFind(Parse *pParse, RenameCtx *pCtx, void *pPtr){
+/*
+** Search the Parse object passed as the first argument for a RenameToken
+** object associated with parse tree element pPtr. If found, remove it
+** from the Parse object and add it to the list maintained by the
+** RenameCtx object passed as the second argument.
+*/
+static void renameTokenFind(Parse *pParse, struct RenameCtx *pCtx, void *pPtr){
RenameToken **pp;
for(pp=&pParse->pRename; (*pp); pp=&(*pp)->pNext){
if( (*pp)->p==pPtr ){
@@ -956,6 +966,20 @@ static void renameTokenFind(Parse *pParse, RenameCtx *pCtx, void *pPtr){
}
}
+static int renameColumnSelectCb(Walker *pWalker, Select *p){
+ return WRC_Continue;
+}
+
+
+/*
+** This is a Walker expression callback.
+**
+** For every TK_COLUMN node in the expression tree, search to see
+** if the column being references is the column being renamed by an
+** ALTER TABLE statement. If it is, then attach its associated
+** RenameToken object to the list of RenameToken objects being
+** constructed in RenameCtx object at pWalker->u.pRename.
+*/
static int renameColumnExprCb(Walker *pWalker, Expr *pExpr){
RenameCtx *p = pWalker->u.pRename;
if( p->zOld && pExpr->op==TK_DOT ){
@@ -969,12 +993,23 @@ static int renameColumnExprCb(Walker *pWalker, Expr *pExpr){
renameTokenFind(pWalker->pParse, p, (void*)pRight);
}
}
- }else if( pExpr->op==TK_COLUMN && pExpr->iColumn==p->iCol ){
+ }else if( pExpr->op==TK_COLUMN && pExpr->iColumn==p->iCol
+ && (p->pTab==0 || p->pTab==pExpr->pTab)
+ ){
renameTokenFind(pWalker->pParse, p, (void*)pExpr);
}
return WRC_Continue;
}
+/*
+** The RenameCtx contains a list of tokens that reference a column that
+** is being renamed by an ALTER TABLE statement. Return the "first"
+** RenameToken in the RenameCtx and remove that RenameToken from the
+** RenameContext. "First" means the first RenameToken encountered when
+** the input SQL from left to right. Repeated calls to this routine
+** return all column name tokens in the order that they are encountered
+** in the SQL statement.
+*/
static RenameToken *renameColumnTokenNext(RenameCtx *pCtx){
RenameToken *pBest = pCtx->pList;
RenameToken *pToken;
@@ -990,7 +1025,31 @@ static RenameToken *renameColumnTokenNext(RenameCtx *pCtx){
}
/*
-** sqlite_rename_column(SQL, iCol, bQuote, zNew, zTable, zOld)
+** SQL function:
+**
+** sqlite_rename_column(zSql, iCol, bQuote, zNew, zTable, zOld)
+**
+** 0. zSql: SQL statement to rewrite
+** 1. Database: Database name (e.g. "main")
+** 2. Table: Table name
+** 3. iCol: Index of column to rename
+** 4. zNew: New column name
+** 5. bQuote: True if the new column name should be quoted
+**
+** Do a column rename operation on the CREATE statement given in zSql.
+** The iCol-th column (left-most is 0) of table zTable is renamed from zCol
+** into zNew. The name should be quoted if bQuote is true.
+**
+** This function is used internally by the ALTER TABLE RENAME COLUMN command.
+** Though accessible to application code, it is not intended for use by
+** applications. The existance of this function, and the way it works,
+** is subject to change without notice.
+**
+** If any of the parameters are out-of-bounds, then simply return NULL.
+** An out-of-bounds parameter can only occur when the application calls
+** this function directly. The parameters will always be well-formed when
+** this routine is invoked by the bytecode for a legitimate ALTER TABLE
+** statement.
*/
static void renameColumnFunc(
sqlite3_context *context,
@@ -1001,11 +1060,13 @@ static void renameColumnFunc(
RenameCtx sCtx;
const char *zSql = (const char*)sqlite3_value_text(argv[0]);
int nSql = sqlite3_value_bytes(argv[0]);
- int bQuote = sqlite3_value_int(argv[2]);
- const char *zNew = (const char*)sqlite3_value_text(argv[3]);
- int nNew = sqlite3_value_bytes(argv[3]);
- const char *zTable = (const char*)sqlite3_value_text(argv[4]);
- const char *zOld = (const char*)sqlite3_value_text(argv[5]);
+ const char *zDb = (const char*)sqlite3_value_text(argv[1]);
+ const char *zTable = (const char*)sqlite3_value_text(argv[2]);
+ int iCol = sqlite3_value_int(argv[3]);
+ const char *zNew = (const char*)sqlite3_value_text(argv[4]);
+ int nNew = sqlite3_value_bytes(argv[4]);
+ int bQuote = sqlite3_value_int(argv[5]);
+ const char *zOld;
int rc;
char *zErr = 0;
@@ -1017,9 +1078,17 @@ static void renameColumnFunc(
char *zQuot = 0; /* Quoted version of zNew */
int nQuot = 0; /* Length of zQuot in bytes */
int i;
+ Table *pTab;
+ if( zSql==0 ) return;
+ if( zNew==0 ) return;
+ if( zTable==0 ) return;
+ if( iCol<0 ) return;
+ pTab = sqlite3FindTable(db, zTable, zDb);
+ if( pTab==0 || iCol>=pTab->nCol ) return;
+ zOld = pTab->aCol[iCol].zName;
memset(&sCtx, 0, sizeof(sCtx));
- sCtx.iCol = sqlite3_value_int(argv[1]);
+ sCtx.iCol = ((iCol==pTab->iPKey) ? -1 : iCol);
memset(&sParse, 0, sizeof(sParse));
sParse.eParseMode = PARSE_MODE_RENAME_COLUMN;
@@ -1043,16 +1112,6 @@ static void renameColumnFunc(
}
}
- if( rc!=SQLITE_OK ){
- if( zErr ){
- sqlite3_result_error(context, zErr, -1);
- }else{
- sqlite3_result_error_code(context, rc);
- }
- sqlite3DbFree(db, zErr);
- goto renameColumnFunc_done;
- }
-
if( bQuote ){
zNew = zQuot;
nNew = nQuot;
@@ -1060,7 +1119,7 @@ static void renameColumnFunc(
#ifdef SQLITE_DEBUG
assert( sqlite3Strlen30(zSql)==nSql );
- {
+ if( rc==SQLITE_OK ){
RenameToken *pToken;
for(pToken=sParse.pRename; pToken; pToken=pToken->pNext){
assert( pToken->t.z>=zSql && &pToken->t.z[pToken->t.n]<=&zSql[nSql] );
@@ -1072,34 +1131,55 @@ static void renameColumnFunc(
memset(&sWalker, 0, sizeof(Walker));
sWalker.pParse = &sParse;
sWalker.xExprCallback = renameColumnExprCb;
+ sWalker.xSelectCallback = renameColumnSelectCb;
sWalker.u.pRename = &sCtx;
+ if( rc!=SQLITE_OK ) goto renameColumnFunc_done;
if( sParse.pNewTable ){
- int bFKOnly = sqlite3_stricmp(zTable, sParse.pNewTable->zName);
- FKey *pFKey;
- if( bFKOnly==0 ){
- renameTokenFind(
- &sParse, &sCtx, (void*)sParse.pNewTable->aCol[sCtx.iCol].zName
- );
- assert( sCtx.iCol>=0 );
- if( sParse.pNewTable->iPKey==sCtx.iCol ){
- sCtx.iCol = -1;
+ Select *pSelect = sParse.pNewTable->pSelect;
+ if( pSelect ){
+ sCtx.pTab = pTab;
+ sParse.rc = SQLITE_OK;
+ sqlite3SelectPrep(&sParse, sParse.pNewTable->pSelect, 0);
+ rc = (db->mallocFailed ? SQLITE_NOMEM : sParse.rc);
+ if( rc==SQLITE_OK ){
+ sqlite3WalkSelect(&sWalker, pSelect);
+ }else if( rc==SQLITE_ERROR ){
+ /* Failed to resolve all symboles in the view. This is not an
+ ** error, but it will not be edited. */
+ sqlite3DbFree(db, sParse.zErrMsg);
+ sParse.zErrMsg = 0;
+ rc = SQLITE_OK;
}
- sqlite3WalkExprList(&sWalker, sParse.pNewTable->pCheck);
- for(pIdx=sParse.pNewTable->pIndex; pIdx; pIdx=pIdx->pNext){
- sqlite3WalkExprList(&sWalker, pIdx->aColExpr);
+ if( rc!=SQLITE_OK ) goto renameColumnFunc_done;
+ }else{
+ /* A regular table */
+ int bFKOnly = sqlite3_stricmp(zTable, sParse.pNewTable->zName);
+ FKey *pFKey;
+ assert( sParse.pNewTable->pSelect==0 );
+ if( bFKOnly==0 ){
+ renameTokenFind(
+ &sParse, &sCtx, (void*)sParse.pNewTable->aCol[iCol].zName
+ );
+ if( sCtx.iCol<0 ){
+ renameTokenFind(&sParse, &sCtx, (void*)&sParse.pNewTable->iPKey);
+ }
+ sqlite3WalkExprList(&sWalker, sParse.pNewTable->pCheck);
+ for(pIdx=sParse.pNewTable->pIndex; pIdx; pIdx=pIdx->pNext){
+ sqlite3WalkExprList(&sWalker, pIdx->aColExpr);
+ }
}
- }
- for(pFKey=sParse.pNewTable->pFKey; pFKey; pFKey=pFKey->pNextFrom){
- for(i=0; i<pFKey->nCol; i++){
- if( bFKOnly==0 && pFKey->aCol[i].iFrom==sCtx.iCol ){
- renameTokenFind(&sParse, &sCtx, (void*)&pFKey->aCol[i]);
- }
- if( 0==sqlite3_stricmp(pFKey->zTo, zTable)
- && 0==sqlite3_stricmp(pFKey->aCol[i].zCol, zOld)
- ){
- renameTokenFind(&sParse, &sCtx, (void*)pFKey->aCol[i].zCol);
+ for(pFKey=sParse.pNewTable->pFKey; pFKey; pFKey=pFKey->pNextFrom){
+ for(i=0; i<pFKey->nCol; i++){
+ if( bFKOnly==0 && pFKey->aCol[i].iFrom==sCtx.iCol ){
+ renameTokenFind(&sParse, &sCtx, (void*)&pFKey->aCol[i]);
+ }
+ if( 0==sqlite3_stricmp(pFKey->zTo, zTable)
+ && 0==sqlite3_stricmp(pFKey->aCol[i].zCol, zOld)
+ ){
+ renameTokenFind(&sParse, &sCtx, (void*)pFKey->aCol[i].zCol);
+ }
}
}
}
@@ -1119,6 +1199,7 @@ static void renameColumnFunc(
}
}
+ assert( rc==SQLITE_OK );
assert( nQuot>=nNew );
zOut = sqlite3DbMallocZero(db, nSql + sCtx.nList*nQuot + 1);
if( zOut ){
@@ -1152,9 +1233,19 @@ static void renameColumnFunc(
sqlite3_result_text(context, zOut, -1, SQLITE_TRANSIENT);
sqlite3DbFree(db, zOut);
+ }else{
+ rc = SQLITE_NOMEM;
}
renameColumnFunc_done:
+ if( rc!=SQLITE_OK ){
+ if( zErr ){
+ sqlite3_result_error(context, zErr, -1);
+ }else{
+ sqlite3_result_error_code(context, rc);
+ }
+ }
+
if( sParse.pVdbe ){
sqlite3VdbeFinalize(sParse.pVdbe);
}
diff --git a/src/build.c b/src/build.c
index 66004294e..68fecd103 100644
--- a/src/build.c
+++ b/src/build.c
@@ -1370,6 +1370,9 @@ void sqlite3AddPrimaryKey(
&& sqlite3StrICmp(sqlite3ColumnType(pCol,""), "INTEGER")==0
&& sortOrder!=SQLITE_SO_DESC
){
+ if( IN_RENAME_COLUMN && pList ){
+ sqlite3MoveRenameToken(pParse, &pTab->iPKey, pList->a[0].pExpr);
+ }
pTab->iPKey = iCol;
pTab->keyConf = (u8)onError;
assert( autoInc==0 || autoInc==1 );
@@ -2172,7 +2175,12 @@ void sqlite3CreateView(
** allocated rather than point to the input string - which means that
** they will persist after the current sqlite3_exec() call returns.
*/
- p->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE);
+ if( IN_RENAME_COLUMN ){
+ p->pSelect = pSelect;
+ pSelect = 0;
+ }else{
+ p->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE);
+ }
p->pCheck = sqlite3ExprListDup(db, pCNames, EXPRDUP_REDUCE);
if( db->mallocFailed ) goto create_view_fail;
diff --git a/src/insert.c b/src/insert.c
index 16971a044..12a6bb805 100644
--- a/src/insert.c
+++ b/src/insert.c
@@ -1179,44 +1179,6 @@ static int checkConstraintUnchanged(Expr *pExpr, int *aiChng, int chngRowid){
}
/*
-** An instance of the ConstraintAddr object remembers the byte-code addresses
-** for sections of the constraint checks that deal with uniqueness constraints
-** on the rowid and on the upsert constraint.
-**
-** This information is passed into checkReorderConstraintChecks() to insert
-** some OP_Goto operations so that the rowid and upsert constraints occur
-** in the correct order relative to other constraints.
-*/
-typedef struct ConstraintAddr ConstraintAddr;
-struct ConstraintAddr {
- int ipkTop; /* Subroutine for rowid constraint check */
- int upsertTop; /* Label for upsert constraint check subroutine */
- int upsertTop2; /* Copy of upsertTop not cleared by the call */
- int upsertBtm; /* upsert constraint returns to this label */
- int ipkBtm; /* Return opcode rowid constraint check */
-};
-
-/*
-** Generate any OP_Goto operations needed to cause constraints to be
-** run that haven't already been run.
-*/
-static void reorderConstraintChecks(Vdbe *v, ConstraintAddr *p){
- if( p->upsertTop ){
- testcase( sqlite3VdbeLabelHasBeenResolved(v, p->upsertTop) );
- sqlite3VdbeGoto(v, p->upsertTop);
- VdbeComment((v, "call upsert subroutine"));
- sqlite3VdbeResolveLabel(v, p->upsertBtm);
- p->upsertTop = 0;
- }
- if( p->ipkTop ){
- sqlite3VdbeGoto(v, p->ipkTop);
- VdbeComment((v, "call rowid unique-check subroutine"));
- sqlite3VdbeJumpHere(v, p->ipkBtm);
- p->ipkTop = 0;
- }
-}
-
-/*
** Generate code to do constraint checks prior to an INSERT or an UPDATE
** on table pTab.
**
@@ -1325,11 +1287,13 @@ void sqlite3GenerateConstraintChecks(
int addr1; /* Address of jump instruction */
int seenReplace = 0; /* True if REPLACE is used to resolve INT PK conflict */
int nPkField; /* Number of fields in PRIMARY KEY. 1 for ROWID tables */
- ConstraintAddr sAddr;/* Address information for constraint reordering */
Index *pUpIdx = 0; /* Index to which to apply the upsert */
u8 isUpdate; /* True if this is an UPDATE operation */
u8 bAffinityDone = 0; /* True if the OP_Affinity operation has been run */
int upsertBypass = 0; /* Address of Goto to bypass upsert subroutine */
+ int upsertJump = 0; /* Address of Goto that jumps into upsert subroutine */
+ int ipkTop = 0; /* Top of the IPK uniqueness check */
+ int ipkBottom = 0; /* OP_Goto at the end of the IPK uniqueness check */
isUpdate = regOldData!=0;
db = pParse->db;
@@ -1337,7 +1301,6 @@ void sqlite3GenerateConstraintChecks(
assert( v!=0 );
assert( pTab->pSelect==0 ); /* This table is not a VIEW */
nCol = pTab->nCol;
- memset(&sAddr, 0, sizeof(sAddr));
/* pPk is the PRIMARY KEY index for WITHOUT ROWID tables and NULL for
** normal rowid tables. nPkField is the number of key fields in the
@@ -1441,8 +1404,8 @@ void sqlite3GenerateConstraintChecks(
/* UNIQUE and PRIMARY KEY constraints should be handled in the following
** order:
**
- ** (1) OE_Abort, OE_Fail, OE_Rollback, OE_Ignore
- ** (2) OE_Update
+ ** (1) OE_Update
+ ** (2) OE_Abort, OE_Fail, OE_Rollback, OE_Ignore
** (3) OE_Replace
**
** OE_Fail and OE_Ignore must happen before any changes are made.
@@ -1451,6 +1414,11 @@ void sqlite3GenerateConstraintChecks(
** could happen in any order, but they are grouped up front for
** convenience.
**
+ ** 2018-08-14: Ticket https://www.sqlite.org/src/info/908f001483982c43
+ ** The order of constraints used to have OE_Update as (2) and OE_Abort
+ ** and so forth as (1). But apparently PostgreSQL checks the OE_Update
+ ** constraint before any others, so it had to be moved.
+ **
** Constraint checking code is generated in this order:
** (A) The rowid constraint
** (B) Unique index constraints that do not have OE_Replace as their
@@ -1470,11 +1438,10 @@ void sqlite3GenerateConstraintChecks(
overrideError = OE_Ignore;
pUpsert = 0;
}else if( (pUpIdx = pUpsert->pUpsertIdx)!=0 ){
- /* If the constraint-target is on some column other than
- ** then ROWID, then we might need to move the UPSERT around
- ** so that it occurs in the correct order. */
- sAddr.upsertTop = sAddr.upsertTop2 = sqlite3VdbeMakeLabel(v);
- sAddr.upsertBtm = sqlite3VdbeMakeLabel(v);
+ /* If the constraint-target uniqueness check must be run first.
+ ** Jump to that uniqueness check now */
+ upsertJump = sqlite3VdbeAddOp0(v, OP_Goto);
+ VdbeComment((v, "UPSERT constraint goes first"));
}
}
@@ -1506,16 +1473,12 @@ void sqlite3GenerateConstraintChecks(
** to defer the running of the rowid conflict checking until after
** the UNIQUE constraints have run.
*/
- assert( OE_Update>OE_Replace );
- assert( OE_Ignore<OE_Replace );
- assert( OE_Fail<OE_Replace );
- assert( OE_Abort<OE_Replace );
- assert( OE_Rollback<OE_Replace );
- if( onError>=OE_Replace
- && (pUpsert || onError!=overrideError)
- && pTab->pIndex
+ if( onError==OE_Replace /* IPK rule is REPLACE */
+ && onError!=overrideError /* Rules for other contraints are different */
+ && pTab->pIndex /* There exist other constraints */
){
- sAddr.ipkTop = sqlite3VdbeAddOp0(v, OP_Goto)+1;
+ ipkTop = sqlite3VdbeAddOp0(v, OP_Goto)+1;
+ VdbeComment((v, "defer IPK REPLACE until last"));
}
if( isUpdate ){
@@ -1610,9 +1573,9 @@ void sqlite3GenerateConstraintChecks(
}
}
sqlite3VdbeResolveLabel(v, addrRowidOk);
- if( sAddr.ipkTop ){
- sAddr.ipkBtm = sqlite3VdbeAddOp0(v, OP_Goto);
- sqlite3VdbeJumpHere(v, sAddr.ipkTop-1);
+ if( ipkTop ){
+ ipkBottom = sqlite3VdbeAddOp0(v, OP_Goto);
+ sqlite3VdbeJumpHere(v, ipkTop-1);
}
}
@@ -1630,18 +1593,18 @@ void sqlite3GenerateConstraintChecks(
int addrUniqueOk; /* Jump here if the UNIQUE constraint is satisfied */
if( aRegIdx[ix]==0 ) continue; /* Skip indices that do not change */
- if( bAffinityDone==0 ){
- sqlite3TableAffinity(v, pTab, regNewData+1);
- bAffinityDone = 1;
- }
if( pUpIdx==pIdx ){
- addrUniqueOk = sAddr.upsertBtm;
+ addrUniqueOk = upsertJump+1;
upsertBypass = sqlite3VdbeGoto(v, 0);
VdbeComment((v, "Skip upsert subroutine"));
- sqlite3VdbeResolveLabel(v, sAddr.upsertTop2);
+ sqlite3VdbeJumpHere(v, upsertJump);
}else{
addrUniqueOk = sqlite3VdbeMakeLabel(v);
}
+ if( bAffinityDone==0 && (pUpIdx==0 || pUpIdx==pIdx) ){
+ sqlite3TableAffinity(v, pTab, regNewData+1);
+ bAffinityDone = 1;
+ }
VdbeNoopComment((v, "uniqueness check for %s", pIdx->zName));
iThisCur = iIdxCur+ix;
@@ -1713,15 +1676,6 @@ void sqlite3GenerateConstraintChecks(
}
}
- /* Invoke subroutines to handle IPK replace and upsert prior to running
- ** the first REPLACE constraint check. */
- if( onError==OE_Replace ){
- testcase( sAddr.ipkTop );
- testcase( sAddr.upsertTop
- && sqlite3VdbeLabelHasBeenResolved(v,sAddr.upsertTop) );
- reorderConstraintChecks(v, &sAddr);
- }
-
/* Collision detection may be omitted if all of the following are true:
** (1) The conflict resolution algorithm is REPLACE
** (2) The table is a WITHOUT ROWID table
@@ -1843,18 +1797,21 @@ void sqlite3GenerateConstraintChecks(
}
}
if( pUpIdx==pIdx ){
+ sqlite3VdbeGoto(v, upsertJump+1);
sqlite3VdbeJumpHere(v, upsertBypass);
}else{
sqlite3VdbeResolveLabel(v, addrUniqueOk);
}
if( regR!=regIdx ) sqlite3ReleaseTempRange(pParse, regR, nPkField);
+ }
+ /* If the IPK constraint is a REPLACE, run it last */
+ if( ipkTop ){
+ sqlite3VdbeGoto(v, ipkTop+1);
+ VdbeComment((v, "Do IPK REPLACE"));
+ sqlite3VdbeJumpHere(v, ipkBottom);
}
- testcase( sAddr.ipkTop!=0 );
- testcase( sAddr.upsertTop
- && sqlite3VdbeLabelHasBeenResolved(v,sAddr.upsertTop) );
- reorderConstraintChecks(v, &sAddr);
-
+
*pbMayReplace = seenReplace;
VdbeModuleComment((v, "END: GenCnstCks(%d)", seenReplace));
}
diff --git a/src/os_unix.c b/src/os_unix.c
index aacca93d1..ab0a5d919 100644
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -702,12 +702,25 @@ static int robust_open(const char *z, int f, mode_t m){
** unixEnterMutex()
** assert( unixMutexHeld() );
** unixEnterLeave()
+**
+** To prevent deadlock, the global unixBigLock must must be acquired
+** before the unixInodeInfo.pLockMutex mutex, if both are held. It is
+** OK to get the pLockMutex without holding unixBigLock first, but if
+** that happens, the unixBigLock mutex must not be acquired until after
+** pLockMutex is released.
+**
+** OK: enter(unixBigLock), enter(pLockInfo)
+** OK: enter(unixBigLock)
+** OK: enter(pLockInfo)
+** ERROR: enter(pLockInfo), enter(unixBigLock)
*/
static sqlite3_mutex *unixBigLock = 0;
static void unixEnterMutex(void){
+ assert( sqlite3_mutex_notheld(unixBigLock) ); /* Not a recursive mutex */
sqlite3_mutex_enter(unixBigLock);
}
static void unixLeaveMutex(void){
+ assert( sqlite3_mutex_held(unixBigLock) );
sqlite3_mutex_leave(unixBigLock);
}
#ifdef SQLITE_DEBUG
@@ -1111,9 +1124,9 @@ struct unixFileId {
**
** Mutex rules:
**
-** (1) The pLockMutex mutex must be held in order to read or write
+** (1) Only the pLockMutex mutex must be held in order to read or write
** any of the locking fields:
-** nShared, nLock, eFileLock, or bProcessLock
+** nShared, nLock, eFileLock, bProcessLock, pUnused
**
** (2) When nRef>0, then the following fields are unchanging and can
** be read (but not written) without holding any mutex:
@@ -1121,6 +1134,10 @@ struct unixFileId {
**
** (3) With the exceptions above, all the fields may only be read
** or written while holding the global unixBigLock mutex.
+**
+** Deadlock prevention: The global unixBigLock mutex may not
+** be acquired while holding the pLockMutex mutex. If both unixBigLock
+** and pLockMutex are needed, then unixBigLock must be acquired first.
*/
struct unixInodeInfo {
struct unixFileId fileId; /* The lookup key */
@@ -1129,9 +1146,9 @@ struct unixInodeInfo {
int nLock; /* Number of outstanding file locks */
unsigned char eFileLock; /* One of SHARED_LOCK, RESERVED_LOCK etc. */
unsigned char bProcessLock; /* An exclusive process lock is held */
+ UnixUnusedFd *pUnused; /* Unused file descriptors to close */
int nRef; /* Number of pointers to this structure */
unixShmNode *pShmNode; /* Shared memory associated with this inode */
- UnixUnusedFd *pUnused; /* Unused file descriptors to close */
unixInodeInfo *pNext; /* List of all unixInodeInfo objects */
unixInodeInfo *pPrev; /* .... doubly linked */
#if SQLITE_ENABLE_LOCKING_STYLE
@@ -1147,7 +1164,21 @@ struct unixInodeInfo {
** A lists of all unixInodeInfo objects.
*/
static unixInodeInfo *inodeList = 0; /* All unixInodeInfo objects */
-static unsigned int nUnusedFd = 0; /* Total unused file descriptors */
+
+#ifdef SQLITE_DEBUG
+/*
+** True if the inode mutex is held, or not. Used only within assert()
+** to help verify correct mutex usage.
+*/
+int unixFileMutexHeld(unixFile *pFile){
+ assert( pFile->pInode );
+ return sqlite3_mutex_held(pFile->pInode->pLockMutex);
+}
+int unixFileMutexNotheld(unixFile *pFile){
+ assert( pFile->pInode );
+ return sqlite3_mutex_notheld(pFile->pInode->pLockMutex);
+}
+#endif
/*
**
@@ -1253,11 +1284,11 @@ static void closePendingFds(unixFile *pFile){
unixInodeInfo *pInode = pFile->pInode;
UnixUnusedFd *p;
UnixUnusedFd *pNext;
+ assert( unixFileMutexHeld(pFile) );
for(p=pInode->pUnused; p; p=pNext){
pNext = p->pNext;
robust_close(pFile, p->fd, __LINE__);
sqlite3_free(p);
- nUnusedFd--;
}
pInode->pUnused = 0;
}
@@ -1271,11 +1302,14 @@ static void closePendingFds(unixFile *pFile){
static void releaseInodeInfo(unixFile *pFile){
unixInodeInfo *pInode = pFile->pInode;
assert( unixMutexHeld() );
+ assert( unixFileMutexNotheld(pFile) );
if( ALWAYS(pInode) ){
pInode->nRef--;
if( pInode->nRef==0 ){
assert( pInode->pShmNode==0 );
+ sqlite3_mutex_enter(pInode->pLockMutex);
closePendingFds(pFile);
+ sqlite3_mutex_leave(pInode->pLockMutex);
if( pInode->pPrev ){
assert( pInode->pPrev->pNext==pInode );
pInode->pPrev->pNext = pInode->pNext;
@@ -1291,7 +1325,6 @@ static void releaseInodeInfo(unixFile *pFile){
sqlite3_free(pInode);
}
}
- assert( inodeList!=0 || nUnusedFd==0 );
}
/*
@@ -1361,7 +1394,6 @@ static int findInodeInfo(
#else
fileId.ino = (u64)statbuf.st_ino;
#endif
- assert( inodeList!=0 || nUnusedFd==0 );
pInode = inodeList;
while( pInode && memcmp(&fileId, &pInode->fileId, sizeof(fileId)) ){
pInode = pInode->pNext;
@@ -1826,11 +1858,11 @@ end_lock:
static void setPendingFd(unixFile *pFile){
unixInodeInfo *pInode = pFile->pInode;
UnixUnusedFd *p = pFile->pPreallocatedUnused;
+ assert( unixFileMutexHeld(pFile) );
p->pNext = pInode->pUnused;
pInode->pUnused = p;
pFile->h = -1;
pFile->pPreallocatedUnused = 0;
- nUnusedFd++;
}
/*
@@ -1988,14 +2020,14 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){
*/
pInode->nLock--;
assert( pInode->nLock>=0 );
- if( pInode->nLock==0 ){
- closePendingFds(pFile);
- }
+ if( pInode->nLock==0 ) closePendingFds(pFile);
}
end_unlock:
sqlite3_mutex_leave(pInode->pLockMutex);
- if( rc==SQLITE_OK ) pFile->eFileLock = eFileLock;
+ if( rc==SQLITE_OK ){
+ pFile->eFileLock = eFileLock;
+ }
return rc;
}
@@ -2066,15 +2098,20 @@ static int closeUnixFile(sqlite3_file *id){
static int unixClose(sqlite3_file *id){
int rc = SQLITE_OK;
unixFile *pFile = (unixFile *)id;
+ unixInodeInfo *pInode = pFile->pInode;
+
+ assert( pInode!=0 );
verifyDbFile(pFile);
unixUnlock(id, NO_LOCK);
+ assert( unixFileMutexNotheld(pFile) );
unixEnterMutex();
/* unixFile.pInode is always valid here. Otherwise, a different close
** routine (e.g. nolockClose()) would be called instead.
*/
assert( pFile->pInode->nLock>0 || pFile->pInode->bProcessLock==0 );
- if( ALWAYS(pFile->pInode) && pFile->pInode->nLock ){
+ sqlite3_mutex_enter(pInode->pLockMutex);
+ if( pFile->pInode->nLock ){
/* If there are outstanding locks, do not actually close the file just
** yet because that would clear those locks. Instead, add the file
** descriptor to pInode->pUnused list. It will be automatically closed
@@ -2082,6 +2119,7 @@ static int unixClose(sqlite3_file *id){
*/
setPendingFd(pFile);
}
+ sqlite3_mutex_leave(pInode->pLockMutex);
releaseInodeInfo(pFile);
rc = closeUnixFile(id);
unixLeaveMutex();
@@ -2679,6 +2717,7 @@ static int semXClose(sqlite3_file *id) {
unixFile *pFile = (unixFile*)id;
semXUnlock(id, NO_LOCK);
assert( pFile );
+ assert( unixFileMutexNotheld(pFile) );
unixEnterMutex();
releaseInodeInfo(pFile);
unixLeaveMutex();
@@ -3119,14 +3158,14 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) {
if( rc==SQLITE_OK ){
pInode->nLock--;
assert( pInode->nLock>=0 );
- if( pInode->nLock==0 ){
- closePendingFds(pFile);
- }
+ if( pInode->nLock==0 ) closePendingFds(pFile);
}
}
sqlite3_mutex_leave(pInode->pLockMutex);
- if( rc==SQLITE_OK ) pFile->eFileLock = eFileLock;
+ if( rc==SQLITE_OK ){
+ pFile->eFileLock = eFileLock;
+ }
return rc;
}
@@ -3138,14 +3177,20 @@ static int afpClose(sqlite3_file *id) {
unixFile *pFile = (unixFile*)id;
assert( id!=0 );
afpUnlock(id, NO_LOCK);
+ assert( unixFileMutexNotheld(pFile) );
unixEnterMutex();
- if( pFile->pInode && pFile->pInode->nLock ){
- /* If there are outstanding locks, do not actually close the file just
- ** yet because that would clear those locks. Instead, add the file
- ** descriptor to pInode->aPending. It will be automatically closed when
- ** the last lock is cleared.
- */
- setPendingFd(pFile);
+ if( pFile->pInode ){
+ unixInodeInfo *pInode = pFile->pInode;
+ sqlite3_mutex_enter(pInode->pLockMutex);
+ if( pFile->pInode->nLock ){
+ /* If there are outstanding locks, do not actually close the file just
+ ** yet because that would clear those locks. Instead, add the file
+ ** descriptor to pInode->aPending. It will be automatically closed when
+ ** the last lock is cleared.
+ */
+ setPendingFd(pFile);
+ }
+ sqlite3_mutex_leave(pInode->pLockMutex);
}
releaseInodeInfo(pFile);
sqlite3_free(pFile->lockingContext);
@@ -4451,6 +4496,7 @@ static int unixOpenSharedMemory(unixFile *pDbFd){
/* Check to see if a unixShmNode object already exists. Reuse an existing
** one if present. Create a new one if necessary.
*/
+ assert( unixFileMutexNotheld(pDbFd) );
unixEnterMutex();
pInode = pDbFd->pInode;
pShmNode = pInode->pShmNode;
@@ -4833,6 +4879,7 @@ static void unixShmBarrier(
){
UNUSED_PARAMETER(fd);
sqlite3MemoryBarrier(); /* compiler-defined memory barrier */
+ assert( unixFileMutexNotheld((unixFile*)fd) );
unixEnterMutex(); /* Also mutex, for redundancy */
unixLeaveMutex();
}
@@ -4874,6 +4921,7 @@ static int unixShmUnmap(
/* If pShmNode->nRef has reached 0, then close the underlying
** shared-memory file, too */
+ assert( unixFileMutexNotheld(pDbFd) );
unixEnterMutex();
assert( pShmNode->nRef>0 );
pShmNode->nRef--;
@@ -5200,7 +5248,7 @@ IOMETHODS(
IOMETHODS(
nolockIoFinder, /* Finder function name */
nolockIoMethods, /* sqlite3_io_methods object name */
- 3, /* shared memory is disabled */
+ 3, /* shared memory and mmap are enabled */
nolockClose, /* xClose method */
nolockLock, /* xLock method */
nolockUnlock, /* xUnlock method */
@@ -5696,7 +5744,7 @@ static UnixUnusedFd *findReusableFd(const char *zPath, int flags){
**
** Even if a subsequent open() call does succeed, the consequences of
** not searching for a reusable file descriptor are not dire. */
- if( nUnusedFd>0 && 0==osStat(zPath, &sStat) ){
+ if( inodeList!=0 && 0==osStat(zPath, &sStat) ){
unixInodeInfo *pInode;
pInode = inodeList;
@@ -5706,12 +5754,14 @@ static UnixUnusedFd *findReusableFd(const char *zPath, int flags){
}
if( pInode ){
UnixUnusedFd **pp;
+ assert( sqlite3_mutex_notheld(pInode->pLockMutex) );
+ sqlite3_mutex_enter(pInode->pLockMutex);
for(pp=&pInode->pUnused; *pp && (*pp)->flags!=flags; pp=&((*pp)->pNext));
pUnused = *pp;
if( pUnused ){
- nUnusedFd--;
*pp = pUnused->pNext;
}
+ sqlite3_mutex_leave(pInode->pLockMutex);
}
}
unixLeaveMutex();
diff --git a/src/prepare.c b/src/prepare.c
index 14017d879..4d4b98d8a 100644
--- a/src/prepare.c
+++ b/src/prepare.c
@@ -25,15 +25,23 @@ static void corruptSchema(
const char *zExtra /* Error information */
){
sqlite3 *db = pData->db;
- if( !db->mallocFailed && (db->flags & SQLITE_WriteSchema)==0 ){
+ if( db->mallocFailed ){
+ pData->rc = SQLITE_NOMEM_BKPT;
+ }else if( pData->pzErrMsg[0]!=0 ){
+ /* A error message has already been generated. Do not overwrite it */
+ }else if( pData->mInitFlags & INITFLAG_AlterTable ){
+ *pData->pzErrMsg = sqlite3DbStrDup(db, zExtra);
+ pData->rc = SQLITE_ERROR;
+ }else if( db->flags & SQLITE_WriteSchema ){
+ pData->rc = SQLITE_CORRUPT_BKPT;
+ }else{
char *z;
if( zObj==0 ) zObj = "?";
z = sqlite3MPrintf(db, "malformed database schema (%s)", zObj);
if( zExtra && zExtra[0] ) z = sqlite3MPrintf(db, "%z - %s", z, zExtra);
- sqlite3DbFree(db, *pData->pzErrMsg);
*pData->pzErrMsg = z;
+ pData->rc = SQLITE_CORRUPT_BKPT;
}
- pData->rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_CORRUPT_BKPT;
}
/*
@@ -132,7 +140,7 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
** auxiliary databases. Return one of the SQLITE_ error codes to
** indicate success or failure.
*/
-int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
+int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFlags){
int rc;
int i;
#ifndef SQLITE_OMIT_DEPRECATED
@@ -167,6 +175,7 @@ int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
initData.iDb = iDb;
initData.rc = SQLITE_OK;
initData.pzErrMsg = pzErrMsg;
+ initData.mInitFlags = mFlags;
sqlite3InitCallback(&initData, 3, (char **)azArg, 0);
if( initData.rc ){
rc = initData.rc;
@@ -373,14 +382,14 @@ int sqlite3Init(sqlite3 *db, char **pzErrMsg){
assert( db->nDb>0 );
/* Do the main schema first */
if( !DbHasProperty(db, 0, DB_SchemaLoaded) ){
- rc = sqlite3InitOne(db, 0, pzErrMsg);
+ rc = sqlite3InitOne(db, 0, pzErrMsg, 0);
if( rc ) return rc;
}
/* All other schemas after the main schema. The "temp" schema must be last */
for(i=db->nDb-1; i>0; i--){
assert( i==1 || sqlite3BtreeHoldsMutex(db->aDb[i].pBt) );
if( !DbHasProperty(db, i, DB_SchemaLoaded) ){
- rc = sqlite3InitOne(db, i, pzErrMsg);
+ rc = sqlite3InitOne(db, i, pzErrMsg, 0);
if( rc ) return rc;
}
}
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 1c7ae4ddd..f2e6121de 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -3329,9 +3329,15 @@ typedef struct {
char **pzErrMsg; /* Error message stored here */
int iDb; /* 0 for main database. 1 for TEMP, 2.. for ATTACHed */
int rc; /* Result code stored here */
+ u32 mInitFlags; /* Flags controlling error messages */
} InitData;
/*
+** Allowed values for mInitFlags
+*/
+#define INITFLAG_AlterTable 0x0001 /* This is a reparse after ALTER TABLE */
+
+/*
** Structure containing global configuration data for the SQLite library.
**
** This structure also contains some state information.
@@ -3801,7 +3807,7 @@ void sqlite3ExprListDelete(sqlite3*, ExprList*);
u32 sqlite3ExprListFlags(const ExprList*);
int sqlite3Init(sqlite3*, char**);
int sqlite3InitCallback(void*, int, char**, char**);
-int sqlite3InitOne(sqlite3*, int, char**);
+int sqlite3InitOne(sqlite3*, int, char**, u32);
void sqlite3Pragma(Parse*,Token*,Token*,Token*,int);
#ifndef SQLITE_OMIT_VIRTUALTABLE
Module *sqlite3PragmaVtabRegister(sqlite3*,const char *zName);
diff --git a/src/vdbe.c b/src/vdbe.c
index 62abaa301..c8ee9860f 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -5722,7 +5722,8 @@ case OP_SqlExec: {
/* Opcode: ParseSchema P1 * * P4 *
**
** Read and parse all entries from the SQLITE_MASTER table of database P1
-** that match the WHERE clause P4.
+** that match the WHERE clause P4. If P4 is a NULL pointer, then the
+** entire schema for P1 is reparsed.
**
** This opcode invokes the parser to create a new virtual machine,
** then runs the new virtual machine. It is thus a re-entrant opcode.
@@ -5751,11 +5752,11 @@ case OP_ParseSchema: {
if( pOp->p4.z==0 ){
sqlite3SchemaClear(db->aDb[iDb].pSchema);
db->mDbFlags &= ~DBFLAG_SchemaKnownOk;
- rc = sqlite3InitOne(db, iDb, &p->zErrMsg);
+ rc = sqlite3InitOne(db, iDb, &p->zErrMsg, INITFLAG_AlterTable);
db->mDbFlags |= DBFLAG_SchemaChange;
}else
#endif
- /* Used to be a conditional */ {
+ {
zMaster = MASTER_NAME;
initData.db = db;
initData.iDb = pOp->p1;
diff --git a/src/vdbe.h b/src/vdbe.h
index 183242a7b..d42acc0cc 100644
--- a/src/vdbe.h
+++ b/src/vdbe.h
@@ -238,9 +238,6 @@ void sqlite3VdbeClearObject(sqlite3*,Vdbe*);
void sqlite3VdbeMakeReady(Vdbe*,Parse*);
int sqlite3VdbeFinalize(Vdbe*);
void sqlite3VdbeResolveLabel(Vdbe*, int);
-#ifdef SQLITE_COVERAGE_TEST
- int sqlite3VdbeLabelHasBeenResolved(Vdbe*,int);
-#endif
int sqlite3VdbeCurrentAddr(Vdbe*);
#ifdef SQLITE_DEBUG
int sqlite3VdbeAssertMayAbort(Vdbe *, int);
diff --git a/src/vdbeaux.c b/src/vdbeaux.c
index c8b61ba22..68784d5fa 100644
--- a/src/vdbeaux.c
+++ b/src/vdbeaux.c
@@ -437,19 +437,6 @@ void sqlite3VdbeResolveLabel(Vdbe *v, int x){
}
}
-#ifdef SQLITE_COVERAGE_TEST
-/*
-** Return TRUE if and only if the label x has already been resolved.
-** Return FALSE (zero) if label x is still unresolved.
-**
-** This routine is only used inside of testcase() macros, and so it
-** only exists when measuring test coverage.
-*/
-int sqlite3VdbeLabelHasBeenResolved(Vdbe *v, int x){
- return v->pParse->aLabel && v->pParse->aLabel[ADDR(x)]>=0;
-}
-#endif /* SQLITE_COVERAGE_TEST */
-
/*
** Mark the VDBE as one that can only be run one time.
*/