]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Have ALTER TABLE RENAME edit column references in CREATE VIEW statements.
authordan <dan@noemail.net>
Tue, 14 Aug 2018 20:18:50 +0000 (20:18 +0000)
committerdan <dan@noemail.net>
Tue, 14 Aug 2018 20:18:50 +0000 (20:18 +0000)
FossilOrigin-Name: db829dc1a2d7afa49798a2fd32d1f070185b23e513416e65d8144fda24f23b50

14 files changed:
manifest
manifest.uuid
src/alter.c
src/build.c
src/insert.c
src/os_unix.c
src/prepare.c
src/sqliteInt.h
src/vdbe.c
src/vdbe.h
src/vdbeaux.c
test/altercol.test
test/trigger7.test
test/upsert1.test

index e874f7babdb15edfbd80d872f2b89cafd8e242ce..f9fbb7fc8116df1ec646aaa1a61b8414e6561732 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Edit\sthe\sWHEN\sand\sUPDATE\sOF\sclauses\sof\strigger\sprograms\sas\spart\sof\sALTER\sTABLE\nRENAME\sCOLUMN.
-D 2018-08-13T17:14:26.913
+C Have\sALTER\sTABLE\sRENAME\sedit\scolumn\sreferences\sin\sCREATE\sVIEW\sstatements.
+D 2018-08-14T20:18:50.875
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F Makefile.in 0a3a6c81e6fcb969ff9106e882f0a08547014ba463cb6beca4c4efaecc924ee6
@@ -432,7 +432,7 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca
 F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
 F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786
 F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a
-F src/alter.c 9f8231841d00ef09d328ecb39af46e0cd85ce4e39d416319d458f8aff7af10dd
+F src/alter.c 03a90ab39849f7f7ba3c637cc8fa663e2e0464b7fcb30c689db594b0a0db694e
 F src/analyze.c 3dc6b98cf007b005af89df165c966baaa48e8124f38c87b4d2b276fe7f0b9eb9
 F src/attach.c 4bd5b92633671d3e8ce431153ebb1893b50335818423b5373f3f27969f79769a
 F src/auth.c 32a5bbe3b755169ab6c66311c5225a3cd4f75a46c041f7fb117e0cbb68055114
@@ -442,7 +442,7 @@ F src/btmutex.c 8acc2f464ee76324bf13310df5692a262b801808984c1b79defb2503bbafadb6
 F src/btree.c 3f5e1a03db871e627bf5da21092bf7434ecfc5c5980bbd7d45eba13341340173
 F src/btree.h febb2e817be499570b7a2e32a9bbb4b607a9234f6b84bb9ae84916d4806e96f2
 F src/btreeInt.h 620ab4c7235f43572cf3ac2ac8723cbdf68073be4d29da24897c7b77dda5fd96
-F src/build.c 49cad074fcc2218f45e126375b4d8f7c3960d2fe2c3398bad862e1bfd640d7c4
+F src/build.c a2e61e716e7d90e382d71818404472207024ecb94a44431ac9fcf1ac3b8c3066
 F src/callback.c 36caff1e7eb7deb58572d59c41cee8f064a11d00297616995c5050ea0cfc1288
 F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
 F src/ctime.c b157b01081f92442f8b0218ddb93ddce8ebddad36dbddeecfdd771561dd4f387
@@ -459,7 +459,7 @@ F src/hash.c a12580e143f10301ed5166ea4964ae2853d3905a511d4e0c44497245c7ce1f7a
 F src/hash.h ab34c5c54a9e9de2e790b24349ba5aab3dbb4fd4
 F src/hwtime.h 747c1bbe9df21a92e9c50f3bbec1de841dc5e5da
 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
-F src/insert.c 894594952bcda1dc6e1549871e4022517563545ffc7a3f4e9e5f3faa788893fd
+F src/insert.c c723716f0de7aa0a679300f7d3541c89645f4a9882161cecdb3093fc07f8cc4b
 F src/legacy.c 134ab3e3fae00a0f67a5187981d6935b24b337bcf0f4b3e5c9fa5763da95bf4e
 F src/loadext.c 6aae5739198d96c51ae6eb97c4a5b1744c22ed7a5a565a5399a717780d48a36b
 F src/main.c df233667bbb6f05a8492ea93e0995abbeb816eab53e51e638a0dece1de0e83a3
@@ -482,7 +482,7 @@ F src/os.c 8aeb0b0f40f8f5b0da03fe49706695adaf42d2f516ab95abc72e86c245e119de
 F src/os.h 48388821692e87da174ea198bf96b1b2d9d83be5dfc908f673ee21fafbe0d432
 F src/os_common.h b2f4707a603e36811d9b1a13278bffd757857b85
 F src/os_setup.h 0dbaea40a7d36bf311613d31342e0b99e2536586
-F src/os_unix.c d9cf5ae0c79f31019d8325e8736c83914aeed64d8327a8d91a62b6439b748948
+F src/os_unix.c e681b2a3ab1085be3eb2e81254449782ca0bd0c38b73c48cb0c2480b8f2f25b9
 F src/os_win.c 070cdbb400097c6cda54aa005356095afdc2f3ee691d17192c54724ef146a971
 F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
 F src/pager.c 76d29b8a960dcb8b67210f095899d91e4a90673a6674ea58cfd1115b705a7fb9
@@ -493,7 +493,7 @@ F src/pcache.h 072f94d29281cffd99e46c1539849f248c4b56ae7684c1f36626797fee375170
 F src/pcache1.c 716975564c15eb6679e97f734cec1bfd6c16ac3d4010f05f1f8e509fc7d19880
 F src/pragma.c 873b767f233932e97cbffd094aa61928be90aca03f946a94bb29ce5695e4885b
 F src/pragma.h bb83728944b42f6d409c77f5838a8edbdb0fe83046c5496ffc9602b40340a324
-F src/prepare.c 540a24af1b579d9c3f9d37c381f84a1a91fbac656d16c88496a0b73c3e064a11
+F src/prepare.c 117e27c6826a83f461986c0cfbb09c31fe004922ce23a61bf78d82a46b0958d9
 F src/printf.c 7f6f3cba8e0c49c19e30a1ff4e9aeda6e06814dcbad4b664a69e1b6cb6e7e365
 F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
 F src/resolve.c b51a48f33da36e0c2dd1ea5f0d10197c3e938a54086a69efce064ae41e2254e1
@@ -503,7 +503,7 @@ F src/shell.c.in 6e0aad854be738a5d0368940459399be211e9ac43aebe92bb9ed46cfe38d0e1
 F src/sqlite.h.in c6451bb876adced3aba5b1682c6317d215c5eceaba21a6ce979e71a0b8d0bf95
 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
 F src/sqlite3ext.h 9887b27e69c01e79c2cbe74ef73bf01af5b5703d6a7f0a4371e386d7249cb1c7
-F src/sqliteInt.h 6a5d65117068808e2f37035d97a4cea6d55d679e02f9673b2b71ae6d5a0b1c8d
+F src/sqliteInt.h c3bca346f053c0d2fe08f0813b0521597bbab11ff8195348372005606c309f84
 F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b
 F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e
 F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
@@ -569,11 +569,11 @@ F src/upsert.c 47edd408cc73f8d3c00a140550d1ad180b407c146285947969dd09874802bf88
 F src/utf.c 810fbfebe12359f10bc2a011520a6e10879ab2a163bcb26c74768eab82ea62a5
 F src/util.c d9eb0a6c4aae1b00a7369eadd7ca0bbe946cb4c953b6751aa20d357c2f482157
 F src/vacuum.c 36e7d21a20c0bf6ef4ef7c399d192b5239410b7c4d3c1070fba4e30810d0b855
-F src/vdbe.c a8dbb4cea2582818f8e253c904fec13ef1f1b6ff50517f7b8962a2ce9e5aad02
-F src/vdbe.h d93abdc8bc9295e0a256e582c19f548c545dc498319d108bbc9dd29de31c48a2
+F src/vdbe.c 18482b9a1ac73c62487dd034da342d464ab9b59cae2abfb4cb234ba8fa7ba16f
+F src/vdbe.h 5081dcc497777efe5e9ebe7330d283a044a005e4bdda2e2e984f03bf89a0d907
 F src/vdbeInt.h 8ea493d994c6697cf7bccc60583a80a0222560490410f60f1113e90d36643ce0
 F src/vdbeapi.c 2ba821c5929a2769e4b217dd85843479c718b8989d414723ec8af0616a83d611
-F src/vdbeaux.c b610cef3d8d381c9287d02c2e61590acc0a1b4de1cc0188d560f5ef345527a24
+F src/vdbeaux.c f03d4a1961ec282abaec5dbf7e5576ddb1eb01e6157335a232d8d9e57fd5eca1
 F src/vdbeblob.c f5c70f973ea3a9e915d1693278a5f890dc78594300cf4d54e64f2b0917c94191
 F src/vdbemem.c 720df42ad8e5c7cb883573de40a185afef4a214903098a16f2bb14b62b2399b7
 F src/vdbesort.c 731a09e5cb9e96b70c394c1b7cf3860fbe84acca7682e178615eb941a3a0ef2f
@@ -599,7 +599,7 @@ F test/alter.test b820ab9dcf85f8e3a65bc8326accb2f0c7be64ef
 F test/alter2.test 7ea05c7d92ac99349a802ef7ada17294dd647060
 F test/alter3.test 4d79934d812eaeacc6f22781a080f8cfe012fdc3
 F test/alter4.test b6d7b86860111864f6cddb54af313f5862dda23b
-F test/altercol.test b3263a3d663e57e35fcd888614cb2256df42ac1be07d40282c5c6023a47a431b
+F test/altercol.test 98f1c88a9012ce2dbb8765f55557cac12473731a3bce31a8016e133407d235f5
 F test/altermalloc.test e81ac9657ed25c6c5bb09bebfa5a047cd8e4acfc
 F test/amatch1.test b5ae7065f042b7f4c1c922933f4700add50cdb9f
 F test/analyze.test b3a9c67d00e1df7588a5b7be9a0292899f94fe8cac1f94a017277474ca2e59df
@@ -1502,7 +1502,7 @@ F test/trigger3.test aa640bb2bbb03edd5ff69c055117ea088f121945
 F test/trigger4.test 74700b76ebf3947b2f7a92405141eb2cf2a5d359
 F test/trigger5.test 619391a3e9fc194081d22cefd830d811e7badf83
 F test/trigger6.test 0e411654f122552da6590f0b4e6f781048a4a9b9
-F test/trigger7.test 799c9d2561facef70340cc9e0dee98aee9b5bdfe
+F test/trigger7.test 93cfa9b48ab9104b2b3d87bc544ac8021405643e36f23ee84635fbfaf9b8fef5
 F test/trigger8.test 30cb0530bd7c4728055420e3f739aa00412eafa4
 F test/trigger9.test 2226ec795a33b0460ab5cf8891e9054cc7edef41
 F test/triggerA.test fe5597f47ee21bacb4936dc827994ed94161e332
@@ -1528,7 +1528,7 @@ F test/unixexcl.test d936ba2b06794018e136418addd59a2354eeae97
 F test/unordered.test ffeea7747d5ba962a8009a20b7e53d68cbae05b063604c68702c5998eb50c981
 F test/update.test 1148de8d913e9817717990603aadeca07aab9ddbb10a30f167cbfd8d3a3ccb60
 F test/update2.test 5e67667e1c54017d964e626db765cf8bedcf87483c184f4c575bdb8c1dd2313e
-F test/upsert1.test ecd8f69e20183f298464992762fee24750508b3cbefc5badba8a259a3fc887ec
+F test/upsert1.test 994bde41800bb77dbe32fcd2e1f6c4b49cc9f2c6cd345731c774dff02b51c110
 F test/upsert2.test 9c3cdbb1a890227f6504ce4b0e3de68f4cdfa16bb21d8641208a9239896c5a09
 F test/upsert3.test 88d7d590a1948a9cb6eac1b54b0642f67a9f35a1fc0f19b200e97d5d39e3179c
 F test/upsert4.test 25d2a1da92f149331ae0c51ca6e3eee78189577585eab92de149900d62994fa5
@@ -1756,11 +1756,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 32edc8920376aabb84ebe1900eaa9512d23f1b44d6459e4916dc6b07db66e27c
-R 4f99214f849ec616e294c625bebc2173
-T *branch * edit-trigger-wrapper
-T *sym-edit-trigger-wrapper *
-T +closed 43c3d2d707a7c54dcb75660be9347d6bd79c4844f81f6ed73446570876f5f72b
-T -sym-alter-table-rename-column *
+P 5fdb6b0aafba727139e1937ef5950e4434a77f95a10fc46f8010ca2de3922326
+R 9599d9a5f29aa1850ea700a8d90d37c9
+T +closed 4b575b8050cdb703df26d28228a60b88f60e5279453ba71c65e0739575609a0b
 U dan
-Z e89da0f8ef0add6d1c95dbb889527ec9
+Z 1ded9b508d80d9b5a23b7f7d1204945e
index d9332dde879575dca61f53aa373345b49c71c9ab..7b219479fca03a4432969f7bdbeb48fa8883a3bf 100644 (file)
@@ -1 +1 @@
-5fdb6b0aafba727139e1937ef5950e4434a77f95a10fc46f8010ca2de3922326
\ No newline at end of file
+db829dc1a2d7afa49798a2fd32d1f070185b23e513416e65d8144fda24f23b50
\ No newline at end of file
index 5dbd42a28c6906cde345ae9700488f5ac1650017..29ef9e602e96393dd06d80dcb6b32d2cf8c91f6a 100644 (file)
@@ -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);
   }
index 66004294e1d9c20ff3f3e7e128bc2180c3983505..68fecd1039a0e64c16a5af9d5985fe290a4dec90 100644 (file)
@@ -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;
 
index 16971a044b40a3b3886c79949f120daa7e791949..12a6bb805ec7c78ac85eb2c047b2b23eace064c0 100644 (file)
@@ -1178,44 +1178,6 @@ static int checkConstraintUnchanged(Expr *pExpr, int *aiChng, int chngRowid){
   return !w.eCode;
 }
 
-/*
-** 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));
 }
index aacca93d1479ed0bfd13989d17c246106b9c2190..ab0a5d919a78a6d5a5dc925139b65927ce602abc 100644 (file)
@@ -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();
index 14017d87921d69de4039882e9a9903b5dbc1239f..4d4b98d8a99513c0928dac5db0997ea9aa826a25 100644 (file)
@@ -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;
     }
   }
index 1c7ae4ddd2387df0b1101a28b3311075dd315cbb..f2e6121de8919b4673c011c36b4e14faef6b0e22 100644 (file)
@@ -3329,8 +3329,14 @@ 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.
 **
@@ -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);
index 62abaa3013faa8c3fd0d3e8e5d8b0912df699c2d..c8ee9860f830ea779db1269b406122752bacce53 100644 (file)
@@ -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;
index 183242a7bed05eeea2b3429cc5579c53da44d4b0..d42acc0cca2b44fd1fa3c6a1874c397d31f88860 100644 (file)
@@ -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);
index c8b61ba22285ad4d582f497f1528268be24d895c..68784d5fa94ebc424d844ac63a01ab811456ee7b 100644 (file)
@@ -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.
 */
index b146ef9ef50249487d97d326b037db0083fa2870..bd8e245cbd64ada9347c8aa63143e46ec801d8b9 100644 (file)
@@ -66,6 +66,21 @@ foreach {tn before after} {
  13 {CREATE TABLE t1(a, b, c, FOREIGN KEY (b) REFERENCES t2)}
     {CREATE TABLE t1(a, d, c, FOREIGN KEY (d) REFERENCES t2)}
 
+ 15 {CREATE TABLE t1(a INTEGER, b INTEGER, c BLOB, PRIMARY KEY(b))}
+    {CREATE TABLE t1(a INTEGER, d INTEGER, c BLOB, PRIMARY KEY(d))}
+
+ 16 {CREATE TABLE t1(a INTEGER, b INTEGER PRIMARY KEY, c BLOB)}
+    {CREATE TABLE t1(a INTEGER, d INTEGER PRIMARY KEY, c BLOB)}
+
+ 14 {CREATE TABLE t1(a INTEGER, b TEXT, c BLOB, PRIMARY KEY(b))}
+    {CREATE TABLE t1(a INTEGER, d TEXT, c BLOB, PRIMARY KEY(d))}
+
+ 15 {CREATE TABLE t1(a INTEGER, b INTEGER, c BLOB, PRIMARY KEY(b))}
+    {CREATE TABLE t1(a INTEGER, d INTEGER, c BLOB, PRIMARY KEY(d))}
+
+ 16 {CREATE TABLE t1(a INTEGER, b INTEGER PRIMARY KEY, c BLOB)}
+    {CREATE TABLE t1(a INTEGER, d INTEGER PRIMARY KEY, c BLOB)}
+
 } {
   reset_db
   do_execsql_test 1.$tn.0 $before
@@ -209,6 +224,7 @@ do_execsql_test 6.3 {
 } {}
 
 #-------------------------------------------------------------------------
+# Triggers.
 #
 reset_db
 do_execsql_test 7.0 {
@@ -235,5 +251,78 @@ do_execsql_test 7.3 {
   SELECT * FROM c;
 } {2}
 
-finish_test
+#-------------------------------------------------------------------------
+# Views.
+#
+reset_db
+do_execsql_test 8.0 {
+  CREATE TABLE a1(x INTEGER, y TEXT, z BLOB, PRIMARY KEY(x));
+  CREATE TABLE a2(a, b, c);
+  CREATE VIEW v1 AS SELECT x, y, z FROM a1;
+}
+
+do_execsql_test 8.1 {
+  ALTER TABLE a1 RENAME y TO yyy;
+  SELECT sql FROM sqlite_master WHERE type='view';
+} {{CREATE VIEW v1 AS SELECT x, yyy, z FROM a1}}
 
+do_execsql_test 8.2.1 {
+  DROP VIEW v1;
+  CREATE VIEW v2 AS SELECT x, x+x, a, a+a FROM a1, a2;
+} {}
+do_execsql_test 8.2.2 {
+  ALTER TABLE a1 RENAME x TO xxx;
+}
+do_execsql_test 8.2.3 {
+  SELECT sql FROM sqlite_master WHERE type='view';
+} {{CREATE VIEW v2 AS SELECT xxx, xxx+xxx, a, a+a FROM a1, a2}}
+
+do_execsql_test 8.3.1 {
+  DROP TABLE a2;
+  DROP VIEW v2;
+  CREATE TABLE a2(a INTEGER PRIMARY KEY, b, c);
+  CREATE VIEW v2 AS SELECT xxx, xxx+xxx, a, a+a FROM a1, a2;
+} {}
+do_execsql_test 8.3.2 {
+  ALTER TABLE a1 RENAME xxx TO x;
+}
+do_execsql_test 8.3.3 {
+  SELECT sql FROM sqlite_master WHERE type='view';
+} {{CREATE VIEW v2 AS SELECT x, x+x, a, a+a FROM a1, a2}}
+
+do_execsql_test 8.4.0 {
+  CREATE TABLE b1(a, b, c);
+  CREATE TABLE b2(x, y, z);
+}
+
+do_execsql_test 8.4.1 {
+  CREATE VIEW vvv AS SELECT c+c || coalesce(c, c) FROM b1, b2 WHERE x=c GROUP BY c HAVING c>0;
+  ALTER TABLE b1 RENAME c TO "a;b";
+  SELECT sql FROM sqlite_master WHERE name='vvv';
+} {{CREATE VIEW vvv AS SELECT "a;b"+"a;b" || coalesce("a;b", "a;b") FROM b1, b2 WHERE x="a;b" GROUP BY "a;b" HAVING "a;b">0}}
+
+do_execsql_test 8.4.2 {
+  CREATE VIEW www AS SELECT b FROM b1 UNION ALL SELECT y FROM b2;
+  ALTER TABLE b1 RENAME b TO bbb;
+  SELECT sql FROM sqlite_master WHERE name='www';
+} {{CREATE VIEW www AS SELECT bbb FROM b1 UNION ALL SELECT y FROM b2}}
+
+db collate nocase {string compare}
+
+do_execsql_test 8.4.3 {
+  CREATE VIEW xxx AS SELECT a FROM b1 UNION SELECT x FROM b2 ORDER BY 1 COLLATE nocase;
+}
+
+do_execsql_test 8.4.4 {
+  ALTER TABLE b2 RENAME x TO hello;
+  SELECT sql FROM sqlite_master WHERE name='xxx';
+} {{CREATE VIEW xxx AS SELECT a FROM b1 UNION SELECT hello FROM b2 ORDER BY 1 COLLATE nocase}}
+
+do_execsql_test 8.4.5 {
+  CREATE VIEW zzz AS SELECT george, ringo FROM b1;
+  ALTER TABLE b1 RENAME a TO aaa;
+  SELECT sql FROM sqlite_master WHERE name = 'zzz'
+} {{CREATE VIEW zzz AS SELECT george, ringo FROM b1}}
+
+
+finish_test
index 847d78cd7a50945c5235e932e67f46a8f0663157..5fa7638f8e6ded0a33c9da0cd4d8e911a6a00d8d 100644 (file)
@@ -113,6 +113,6 @@ do_test trigger7-99.1 {
   db close
   catch { sqlite3 db test.db }
   catchsql { DROP TRIGGER t2r5 }
-} {1 {malformed database schema (t2r12)}}
+} {/1 {malformed database schema .*}/}
 
 finish_test
index 13bb2f81b47a2637cc10335d0896c8d9545b0d58..0d13cd85434e70a73c68c1395d85355b2c94d1c3 100644 (file)
@@ -128,4 +128,87 @@ do_execsql_test upsert1-610 {
   PRAGMA integrity_check;
 } {ok}
 
+# 2018-08-14
+# Ticket https://www.sqlite.org/src/info/908f001483982c43
+# If there are multiple uniqueness contraints, the UPSERT should fire
+# if the one constraint it targets fails, regardless of whether or not
+# the other constraints pass or fail.  In other words, the UPSERT constraint
+# should be tested first.
+#
+do_execsql_test upsert1-700 {
+  DROP TABLE t1;
+  CREATE TABLE t1(a INTEGER PRIMARY KEY, b INT, c INT, d INT, e INT);
+  CREATE UNIQUE INDEX t1b ON t1(b);
+  CREATE UNIQUE INDEX t1e ON t1(e);
+  INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+  INSERT INTO t1(a,b,c,d,e) VALUES(1,2,33,44,5)
+    ON CONFLICT(e) DO UPDATE SET c=excluded.c;
+  SELECT * FROM t1;
+} {1 2 33 4 5}
+do_execsql_test upsert1-710 {
+  DELETE FROM t1;
+  INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+  INSERT INTO t1(a,b,c,d,e) VALUES(1,2,33,44,5)
+    ON CONFLICT(a) DO UPDATE SET c=excluded.c;
+  SELECT * FROM t1;
+} {1 2 33 4 5}
+do_execsql_test upsert1-720 {
+  DELETE FROM t1;
+  INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+  INSERT INTO t1(a,b,c,d,e) VALUES(1,2,33,44,5)
+    ON CONFLICT(b) DO UPDATE SET c=excluded.c;
+  SELECT * FROM t1;
+} {1 2 33 4 5}
+do_execsql_test upsert1-730 {
+  DROP TABLE t1;
+  CREATE TABLE t1(a INT, b INT, c INT, d INT, e INT);
+  CREATE UNIQUE INDEX t1a ON t1(a);
+  CREATE UNIQUE INDEX t1b ON t1(b);
+  CREATE UNIQUE INDEX t1e ON t1(e);
+  INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+  INSERT INTO t1(a,b,c,d,e) VALUES(1,2,33,44,5)
+    ON CONFLICT(e) DO UPDATE SET c=excluded.c;
+  SELECT * FROM t1;
+} {1 2 33 4 5}
+do_execsql_test upsert1-740 {
+  DELETE FROM t1;
+  INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+  INSERT INTO t1(a,b,c,d,e) VALUES(1,2,33,44,5)
+    ON CONFLICT(a) DO UPDATE SET c=excluded.c;
+  SELECT * FROM t1;
+} {1 2 33 4 5}
+do_execsql_test upsert1-750 {
+  DELETE FROM t1;
+  INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+  INSERT INTO t1(a,b,c,d,e) VALUES(1,2,33,44,5)
+    ON CONFLICT(b) DO UPDATE SET c=excluded.c;
+  SELECT * FROM t1;
+} {1 2 33 4 5}
+do_execsql_test upsert1-760 {
+  DROP TABLE t1;
+  CREATE TABLE t1(a INT PRIMARY KEY, b INT, c INT, d INT, e INT) WITHOUT ROWID;
+  CREATE UNIQUE INDEX t1a ON t1(a);
+  CREATE UNIQUE INDEX t1b ON t1(b);
+  CREATE UNIQUE INDEX t1e ON t1(e);
+  INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+  INSERT INTO t1(a,b,c,d,e) VALUES(1,2,33,44,5)
+    ON CONFLICT(e) DO UPDATE SET c=excluded.c;
+  SELECT * FROM t1;
+} {1 2 33 4 5}
+do_execsql_test upsert1-770 {
+  DELETE FROM t1;
+  INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+  INSERT INTO t1(a,b,c,d,e) VALUES(1,2,33,44,5)
+    ON CONFLICT(a) DO UPDATE SET c=excluded.c;
+  SELECT * FROM t1;
+} {1 2 33 4 5}
+do_execsql_test upsert1-780 {
+  DELETE FROM t1;
+  INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+  INSERT INTO t1(a,b,c,d,e) VALUES(1,2,33,44,5)
+    ON CONFLICT(b) DO UPDATE SET c=excluded.c;
+  SELECT * FROM t1;
+} {1 2 33 4 5}
+
+
 finish_test