1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-08 14:02:16 +03:00

Have ALTER TABLE RENAME edit column references in CREATE VIEW statements.

FossilOrigin-Name: db829dc1a2d7afa49798a2fd32d1f070185b23e513416e65d8144fda24f23b50
This commit is contained in:
dan
2018-08-14 20:18:50 +00:00
parent 5496d6a25a
commit 987db767d2
14 changed files with 479 additions and 204 deletions

View File

@@ -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

View File

@@ -1 +1 @@
5fdb6b0aafba727139e1937ef5950e4434a77f95a10fc46f8010ca2de3922326
db829dc1a2d7afa49798a2fd32d1f070185b23e513416e65d8144fda24f23b50

View 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);
}
}
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( 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);
}
if( 0==sqlite3_stricmp(pFKey->zTo, zTable)
&& 0==sqlite3_stricmp(pFKey->aCol[i].zCol, zOld)
){
renameTokenFind(&sParse, &sCtx, (void*)pFKey->aCol[i].zCol);
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);
}
}
}
}
@@ -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);
}

View 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;

View 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);
}
testcase( sAddr.ipkTop!=0 );
testcase( sAddr.upsertTop
&& sqlite3VdbeLabelHasBeenResolved(v,sAddr.upsertTop) );
reorderConstraintChecks(v, &sAddr);
/* If the IPK constraint is a REPLACE, run it last */
if( ipkTop ){
sqlite3VdbeGoto(v, ipkTop+1);
VdbeComment((v, "Do IPK REPLACE"));
sqlite3VdbeJumpHere(v, ipkBottom);
}
*pbMayReplace = seenReplace;
VdbeModuleComment((v, "END: GenCnstCks(%d)", seenReplace));
}

View 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();

View 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;
}
}

View 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);

View 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;

View 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);

View 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.
*/

View 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

View 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

View 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