-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
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
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
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
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
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
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
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
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
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
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
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
-5fdb6b0aafba727139e1937ef5950e4434a77f95a10fc46f8010ca2de3922326
\ No newline at end of file
+db829dc1a2d7afa49798a2fd32d1f070185b23e513416e65d8144fda24f23b50
\ No newline at end of file
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. */
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 */
};
}
}
-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 ){
}
}
+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 ){
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;
}
/*
-** 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,
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;
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;
}
}
- 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;
#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] );
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);
+ }
}
}
}
}
}
+ assert( rc==SQLITE_OK );
assert( nQuot>=nNew );
zOut = sqlite3DbMallocZero(db, nSql + sCtx.nList*nQuot + 1);
if( zOut ){
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);
}
&& 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 );
** 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;
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.
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;
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
/* 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.
** 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
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"));
}
}
** 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 ){
}
}
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);
}
}
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;
}
}
- /* 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
}
}
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));
}
** 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
**
** 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:
**
** (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 */
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
** 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
/*
**
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;
}
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;
sqlite3_free(pInode);
}
}
- assert( inodeList!=0 || nUnusedFd==0 );
}
/*
#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;
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++;
}
/*
*/
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;
}
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
*/
setPendingFd(pFile);
}
+ sqlite3_mutex_leave(pInode->pLockMutex);
releaseInodeInfo(pFile);
rc = closeUnixFile(id);
unixLeaveMutex();
unixFile *pFile = (unixFile*)id;
semXUnlock(id, NO_LOCK);
assert( pFile );
+ assert( unixFileMutexNotheld(pFile) );
unixEnterMutex();
releaseInodeInfo(pFile);
unixLeaveMutex();
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;
}
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);
/* 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;
){
UNUSED_PARAMETER(fd);
sqlite3MemoryBarrier(); /* compiler-defined memory barrier */
+ assert( unixFileMutexNotheld((unixFile*)fd) );
unixEnterMutex(); /* Also mutex, for redundancy */
unixLeaveMutex();
}
/* If pShmNode->nRef has reached 0, then close the underlying
** shared-memory file, too */
+ assert( unixFileMutexNotheld(pDbFd) );
unixEnterMutex();
assert( pShmNode->nRef>0 );
pShmNode->nRef--;
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 */
**
** 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;
}
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();
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;
}
/*
** 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
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;
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;
}
}
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.
**
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);
/* 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.
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;
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);
}
}
-#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.
*/
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
} {}
#-------------------------------------------------------------------------
+# Triggers.
#
reset_db
do_execsql_test 7.0 {
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
db close
catch { sqlite3 db test.db }
catchsql { DROP TRIGGER t2r5 }
-} {1 {malformed database schema (t2r12)}}
+} {/1 {malformed database schema .*}/}
finish_test
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