Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Do not allow the use of rowid for views and subqueries unless compiled with -DSQLITE_ALLOW_ROWID_IN_VIEW. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
a2ddb89b206c13876d34c5f9e3db41cd |
User & Date: | drh 2021-04-07 18:17:53.626 |
References
2021-06-25
| ||
11:14 | Fix a bug in the ".fullschema" dot-command of the CLI brought to light by check-in [a2ddb89b206c1387] - raising an error on at attempt to access the rowid of a subquery or view. (check-in: 36875827a4 user: drh tags: trunk) | |
Context
2021-04-08
| ||
13:41 | Fix an obscure memory leak in FTS3. dbsqlfuzz 26a51001beeff35649d2e328b05ec06f93c577a5 (check-in: be7cd9ebb1 user: drh tags: trunk) | |
2021-04-07
| ||
18:17 | Do not allow the use of rowid for views and subqueries unless compiled with -DSQLITE_ALLOW_ROWID_IN_VIEW. (check-in: a2ddb89b20 user: drh tags: trunk) | |
18:08 | Add the SQLITE_ALLOW_ROWID_IN_VIEW option to omitttest.tcl. (Closed-Leaf check-in: 83b8e4c281 user: drh tags: no-rowid-on-view) | |
13:20 | Unnecessary local variable initialization to fix a harmless compiler warning. (check-in: c22e47c77a user: drh tags: trunk) | |
Changes
Changes to src/build.c.
︙ | ︙ | |||
2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 | if( pParse->nVar>0 ){ sqlite3ErrorMsg(pParse, "parameters are not allowed in views"); goto create_view_fail; } sqlite3StartTable(pParse, pName1, pName2, isTemp, 1, 0, noErr); p = pParse->pNewTable; if( p==0 || pParse->nErr ) goto create_view_fail; sqlite3TwoPartName(pParse, pName1, pName2, &pName); iDb = sqlite3SchemaToIndex(db, p->pSchema); sqlite3FixInit(&sFix, pParse, iDb, "view", pName); if( sqlite3FixSelect(&sFix, pSelect) ) goto create_view_fail; /* Make a copy of the entire SELECT statement that defines the view. ** This will force all the Expr.token.z values to be dynamically | > > > > > > > > > > | 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 | if( pParse->nVar>0 ){ sqlite3ErrorMsg(pParse, "parameters are not allowed in views"); goto create_view_fail; } sqlite3StartTable(pParse, pName1, pName2, isTemp, 1, 0, noErr); p = pParse->pNewTable; if( p==0 || pParse->nErr ) goto create_view_fail; /* Legacy versions of SQLite allowed the use of the magic "rowid" column ** on a view, even though views do not have rowids. The following flag ** setting fixes this problem. But the fix can be disabled by compiling ** with -DSQLITE_ALLOW_ROWID_IN_VIEW in case there are legacy apps that ** depend upon the old buggy behavior. */ #ifndef SQLITE_ALLOW_ROWID_IN_VIEW p->tabFlags |= TF_NoVisibleRowid; #endif sqlite3TwoPartName(pParse, pName1, pName2, &pName); iDb = sqlite3SchemaToIndex(db, p->pSchema); sqlite3FixInit(&sFix, pParse, iDb, "view", pName); if( sqlite3FixSelect(&sFix, pSelect) ) goto create_view_fail; /* Make a copy of the entire SELECT statement that defines the view. ** This will force all the Expr.token.z values to be dynamically |
︙ | ︙ |
Changes to src/insert.c.
︙ | ︙ | |||
1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 | ** VdbeCursor.seekResult variable, disabling the OPFLAG_USESEEKRESULT ** functionality. */ bUseSeek = (isReplace==0 || !sqlite3VdbeHasSubProgram(v)); sqlite3CompleteInsertion(pParse, pTab, iDataCur, iIdxCur, regIns, aRegIdx, 0, appendFlag, bUseSeek ); } }else if( pParse->bReturning ){ /* If there is a RETURNING clause, populate the rowid register with ** constant value -1, in case one or more of the returned expressions ** refer to the "rowid" of the view. */ sqlite3VdbeAddOp2(v, OP_Integer, -1, regRowid); } /* Update the count of rows that are inserted */ if( regRowCount ){ sqlite3VdbeAddOp2(v, OP_AddImm, regRowCount, 1); } | > > | 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 | ** VdbeCursor.seekResult variable, disabling the OPFLAG_USESEEKRESULT ** functionality. */ bUseSeek = (isReplace==0 || !sqlite3VdbeHasSubProgram(v)); sqlite3CompleteInsertion(pParse, pTab, iDataCur, iIdxCur, regIns, aRegIdx, 0, appendFlag, bUseSeek ); } #ifdef SQLITE_ALLOW_ROWID_IN_VIEW }else if( pParse->bReturning ){ /* If there is a RETURNING clause, populate the rowid register with ** constant value -1, in case one or more of the returned expressions ** refer to the "rowid" of the view. */ sqlite3VdbeAddOp2(v, OP_Integer, -1, regRowid); #endif } /* Update the count of rows that are inserted */ if( regRowCount ){ sqlite3VdbeAddOp2(v, OP_AddImm, regRowCount, 1); } |
︙ | ︙ |
Changes to src/select.c.
︙ | ︙ | |||
1803 1804 1805 1806 1807 1808 1809 | assert( pTab && pExpr->y.pTab==pTab ); if( pS ){ /* The "table" is actually a sub-select or a view in the FROM clause ** of the SELECT statement. Return the declaration type and origin ** data for the result-set column of the sub-select. */ | | > > > > > > | 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 | assert( pTab && pExpr->y.pTab==pTab ); if( pS ){ /* The "table" is actually a sub-select or a view in the FROM clause ** of the SELECT statement. Return the declaration type and origin ** data for the result-set column of the sub-select. */ if( iCol<pS->pEList->nExpr #ifdef SQLITE_ALLOW_ROWID_IN_VIEW && iCol>=0 #else && ALWAYS(iCol>=0) #endif ){ /* If iCol is less than zero, then the expression requests the ** rowid of the sub-select or view. This expression is legal (see ** test case misc2.2.2) - it always evaluates to NULL. */ NameContext sNC; Expr *p = pS->pEList->a[iCol].pExpr; sNC.pSrcList = pS->pSrc; |
︙ | ︙ | |||
3589 3590 3591 3592 3593 3594 3595 3596 3597 | ){ pExpr->iRightJoinTable = pSubst->iNewTable; } if( pExpr->op==TK_COLUMN && pExpr->iTable==pSubst->iTable && !ExprHasProperty(pExpr, EP_FixedCol) ){ if( pExpr->iColumn<0 ){ pExpr->op = TK_NULL; | > | > > | 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 | ){ pExpr->iRightJoinTable = pSubst->iNewTable; } if( pExpr->op==TK_COLUMN && pExpr->iTable==pSubst->iTable && !ExprHasProperty(pExpr, EP_FixedCol) ){ #ifdef SQLITE_ALLOW_ROWID_IN_VIEW if( pExpr->iColumn<0 ){ pExpr->op = TK_NULL; }else #endif { Expr *pNew; Expr *pCopy = pSubst->pEList->a[pExpr->iColumn].pExpr; Expr ifNullRow; assert( pSubst->pEList!=0 && pExpr->iColumn<pSubst->pEList->nExpr ); assert( pExpr->pRight==0 ); if( sqlite3ExprIsVector(pCopy) ){ sqlite3VectorErrorMsg(pSubst->pParse, pCopy); |
︙ | ︙ | |||
5230 5231 5232 5233 5234 5235 5236 | }else{ pTab->zName = sqlite3MPrintf(pParse->db, "subquery_%u", pSel->selId); } while( pSel->pPrior ){ pSel = pSel->pPrior; } sqlite3ColumnsFromExprList(pParse, pSel->pEList,&pTab->nCol,&pTab->aCol); pTab->iPKey = -1; pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) ); | > > | > > > > | 5239 5240 5241 5242 5243 5244 5245 5246 5247 5248 5249 5250 5251 5252 5253 5254 5255 5256 5257 5258 5259 | }else{ pTab->zName = sqlite3MPrintf(pParse->db, "subquery_%u", pSel->selId); } while( pSel->pPrior ){ pSel = pSel->pPrior; } sqlite3ColumnsFromExprList(pParse, pSel->pEList,&pTab->nCol,&pTab->aCol); pTab->iPKey = -1; pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) ); #ifndef SQLITE_ALLOW_ROWID_IN_VIEW /* The usual case - do not allow ROWID on a subquery */ pTab->tabFlags |= TF_Ephemeral | TF_NoVisibleRowid; #else pTab->tabFlags |= TF_Ephemeral; /* Legacy compatibility mode */ #endif return pParse->nErr ? SQLITE_ERROR : SQLITE_OK; } /* ** This routine is a Walker callback for "expanding" a SELECT statement. ** "Expanding" means to do the following: |
︙ | ︙ |
Changes to src/update.c.
︙ | ︙ | |||
795 796 797 798 799 800 801 | if( addrOnce ){ sqlite3VdbeJumpHereOrPopInst(v, addrOnce); } } /* Top of the update loop */ if( eOnePass!=ONEPASS_OFF ){ | > | > > > > | 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 | if( addrOnce ){ sqlite3VdbeJumpHereOrPopInst(v, addrOnce); } } /* Top of the update loop */ if( eOnePass!=ONEPASS_OFF ){ if( aiCurOnePass[0]!=iDataCur && aiCurOnePass[1]!=iDataCur #ifdef SQLITE_ALLOW_ROWID_IN_VIEW && !isView #endif ){ assert( pPk ); sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelBreak, regKey,nKey); VdbeCoverage(v); } if( eOnePass!=ONEPASS_SINGLE ){ labelContinue = sqlite3VdbeMakeLabel(pParse); } |
︙ | ︙ |
Changes to src/where.c.
︙ | ︙ | |||
627 628 629 630 631 632 633 | if( pOp->p1!=iTabCur ) continue; if( pOp->opcode==OP_Column ){ pOp->opcode = OP_Copy; pOp->p1 = pOp->p2 + iRegister; pOp->p2 = pOp->p3; pOp->p3 = 0; }else if( pOp->opcode==OP_Rowid ){ | < | | < > > < > | 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 | if( pOp->p1!=iTabCur ) continue; if( pOp->opcode==OP_Column ){ pOp->opcode = OP_Copy; pOp->p1 = pOp->p2 + iRegister; pOp->p2 = pOp->p3; pOp->p3 = 0; }else if( pOp->opcode==OP_Rowid ){ pOp->opcode = OP_Sequence; pOp->p1 = iAutoidxCur; #ifdef SQLITE_ALLOW_ROWID_IN_VIEW if( iAutoidxCur==0 ){ pOp->opcode = OP_Null; pOp->p3 = 0; } #endif } } } /* ** Two routines for printing the content of an sqlite3_index_info ** structure. Used for testing and debugging only. If neither |
︙ | ︙ |
Changes to test/autoindex5.test.
︙ | ︙ | |||
119 120 121 122 123 124 125 | UNION ALL SELECT 0, 0 WHERE 0; SELECT ( SELECT sum(z) FROM vvv WHERE x='aaa' ) FROM one; } {8.0} | | | | 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | UNION ALL SELECT 0, 0 WHERE 0; SELECT ( SELECT sum(z) FROM vvv WHERE x='aaa' ) FROM one; } {8.0} do_catchsql_test 2.2 { DROP TABLE t1; CREATE TABLE t1(aaa); INSERT INTO t1(aaa) VALUES(9); SELECT ( SELECT aaa FROM t1 GROUP BY ( SELECT bbb FROM ( SELECT ccc AS bbb FROM ( SELECT 1 ccc ) WHERE rowid IS NOT 1 ) WHERE bbb = 1 ) ); } {1 {no such column: rowid}} # Ticket https://www.sqlite.org/src/info/787fa716be3a7f65 # Segfault due to multiple uses of the same subquery where the # subquery is implemented via coroutine. # ifcapable windowfunc { sqlite3 db :memory: |
︙ | ︙ |
Changes to test/distinct.test.
︙ | ︙ | |||
123 124 125 126 127 128 129 | 18 1 "SELECT DISTINCT c1, c2 FROM t3" 19 1 "SELECT DISTINCT c1 FROM t3" 20 1 "SELECT DISTINCT * FROM t3" 21 0 "SELECT DISTINCT c2 FROM t3" 22 0 "SELECT DISTINCT * FROM (SELECT 1, 2, 3 UNION SELECT 4, 5, 6)" | < | 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | 18 1 "SELECT DISTINCT c1, c2 FROM t3" 19 1 "SELECT DISTINCT c1 FROM t3" 20 1 "SELECT DISTINCT * FROM t3" 21 0 "SELECT DISTINCT c2 FROM t3" 22 0 "SELECT DISTINCT * FROM (SELECT 1, 2, 3 UNION SELECT 4, 5, 6)" 24 0 "SELECT DISTINCT rowid/2 FROM t1" 25 1 "SELECT DISTINCT rowid/2, rowid FROM t1" 26.1 0 "SELECT DISTINCT rowid/2, b FROM t1 WHERE c = ?" 26.2 1 "SELECT DISTINCT rowid/2, b FROM t4 WHERE c = ?" } { if {$noop} { |
︙ | ︙ |
Changes to test/minmax.test.
︙ | ︙ | |||
290 291 292 293 294 295 296 297 298 | } } {34 1234} # Ticket #658: Test the min()/max() optimization when the FROM clause # is a subquery. # ifcapable {compound && subquery} { do_test minmax-9.1 { execsql { | > > > > > | | | | | | 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 | } } {34 1234} # Ticket #658: Test the min()/max() optimization when the FROM clause # is a subquery. # ifcapable {compound && subquery} { do_test minmax-9.0 { execsql { SELECT max(rowid) AS yy FROM t4 UNION SELECT max(rowid) FROM t5 } } {3} do_test minmax-9.1 { execsql { SELECT max(yy) FROM ( SELECT max(rowid) AS yy FROM t4 UNION SELECT max(rowid) FROM t5 ) } } {3} do_test minmax-9.2 { execsql { SELECT max(yy) FROM ( SELECT max(rowid) AS yy FROM t4 EXCEPT SELECT max(rowid) FROM t5 ) } } {{}} } ;# ifcapable compound&&subquery # If there is a NULL in an aggregate max() or min(), ignore it. An # aggregate min() or max() will only return NULL if all values are NULL. |
︙ | ︙ |
Changes to test/minmax2.test.
︙ | ︙ | |||
279 280 281 282 283 284 285 286 287 | } } {34 1234} # Ticket #658: Test the min()/max() optimization when the FROM clause # is a subquery. # ifcapable {compound && subquery} { do_test minmax2-9.1 { execsql { | > > > > > | | | | | | 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 | } } {34 1234} # Ticket #658: Test the min()/max() optimization when the FROM clause # is a subquery. # ifcapable {compound && subquery} { do_test minmax2-9.0 { execsql { SELECT max(rowid) FROM t4 UNION SELECT max(rowid) FROM t5 } } {3} do_test minmax2-9.1 { execsql { SELECT max(yy) FROM ( SELECT max(rowid) AS yy FROM t4 UNION SELECT max(rowid) FROM t5 ) } } {3} do_test minmax2-9.2 { execsql { SELECT max(yy) FROM ( SELECT max(rowid) AS yy FROM t4 EXCEPT SELECT max(rowid) FROM t5 ) } } {{}} } ;# ifcapable compound&&subquery # If there is a NULL in an aggregate max() or min(), ignore it. An # aggregate min() or max() will only return NULL if all values are NULL. |
︙ | ︙ |
Changes to test/misc2.test.
︙ | ︙ | |||
50 51 52 53 54 55 56 | CREATE TABLE t1(a,b,c); INSERT INTO t1 VALUES(1,2,3); CREATE TABLE t2(a,b,c); INSERT INTO t2 VALUES(7,8,9); } } {} ifcapable subquery { | | < | > > > > | < | | < | | < > > > | | 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | CREATE TABLE t1(a,b,c); INSERT INTO t1 VALUES(1,2,3); CREATE TABLE t2(a,b,c); INSERT INTO t2 VALUES(7,8,9); } } {} ifcapable subquery { do_catchsql_test misc2-2.2 { SELECT rowid, * FROM (SELECT * FROM t1, t2); } {1 {no such column: rowid}} do_catchsql_test misc2-2.2b { SELECT 'rowid', * FROM (SELECT * FROM t1, t2); } {0 {rowid 1 2 3 7 8 9}} } ifcapable view { do_catchsql_test misc2-2.3 { CREATE VIEW v1 AS SELECT * FROM t1, t2; SELECT rowid, * FROM v1; } {1 {no such column: rowid}} do_catchsql_test misc2-2.3b { SELECT 'rowid', * FROM v1; } {0 {rowid 1 2 3 7 8 9}} } ;# ifcapable view # Ticket #2002 and #1952. ifcapable subquery { do_test misc2-2.4 { execsql2 { SELECT * FROM (SELECT a, b AS 'a', c AS 'a', 4 AS 'a' FROM t1) |
︙ | ︙ |
Changes to test/misc8.test.
︙ | ︙ | |||
96 97 98 99 100 101 102 | 0 8 {} 10 {} {} 0 9 {} 10 {} {} 0 10 {} 10 {} {} } # 2016-02-26: An assertion fault found by the libFuzzer project # | | | | 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | 0 8 {} 10 {} {} 0 9 {} 10 {} {} 0 10 {} 10 {} {} } # 2016-02-26: An assertion fault found by the libFuzzer project # do_catchsql_test misc8-3.0 { SELECT * FROM ( (SELECT 0 AS i) AS x1, (SELECT 1) AS x2 ) AS x3, (SELECT 6 AS j UNION ALL SELECT 7) AS x4 WHERE i<rowid ORDER BY 1; } {1 {no such column: rowid}} # The SQLITE_DBCONFIG_MAINDBNAME interface # db close forcedelete test.db test2.db sqlite3 db test.db do_execsql_test misc8-4.0 { |
︙ | ︙ |
Changes to test/returning1.test.
︙ | ︙ | |||
206 207 208 209 210 211 212 | INSERT INTO log VALUES('insert', new.rowid, new.a, new.b); END; CREATE TRIGGER tr2 INSTEAD OF UPDATE ON t1 BEGIN INSERT INTO log VALUES('update', new.rowid, new.a, new.b); END; } | | | | | | < | < < < | 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 | INSERT INTO log VALUES('insert', new.rowid, new.a, new.b); END; CREATE TRIGGER tr2 INSTEAD OF UPDATE ON t1 BEGIN INSERT INTO log VALUES('update', new.rowid, new.a, new.b); END; } do_catchsql_test 10.3 { INSERT INTO t1(a, b) VALUES(1234, 5678) RETURNING rowid; } {1 {no such column: rowid}} do_catchsql_test 10.3 { UPDATE t1 SET a='z' WHERE b='y' RETURNING rowid; } {1 {no such column: rowid}} do_execsql_test 10.4 { SELECT * FROM log; } {} finish_test |
Changes to test/trigger9.test.
︙ | ︙ | |||
238 239 240 241 242 243 244 | END; CREATE TRIGGER tr3 INSTEAD OF INSERT ON v1 BEGIN INSERT INTO log VALUES('insert'); END; } | | | | < | < < | 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 | END; CREATE TRIGGER tr3 INSTEAD OF INSERT ON v1 BEGIN INSERT INTO log VALUES('insert'); END; } do_catchsql_test 4.2 { DELETE FROM v1 WHERE rowid=1; } {1 {no such column: rowid}} do_catchsql_test 4.3 { UPDATE v1 SET a=b WHERE rowid=2; } {1 {no such column: rowid}} finish_test |
Changes to tool/omittest.tcl.
︙ | ︙ | |||
250 251 252 253 254 255 256 257 258 259 260 261 262 263 | SQLITE_OMIT_WAL \ SQLITE_OMIT_WINDOWFUNC \ SQLITE_OMIT_WSD \ SQLITE_OMIT_XFER_OPT \ ] set ::ENABLE_SYMBOLS [list \ SQLITE_DISABLE_DIRSYNC \ SQLITE_DISABLE_LFS \ SQLITE_ENABLE_ATOMIC_WRITE \ SQLITE_ENABLE_COLUMN_METADATA \ SQLITE_ENABLE_EXPENSIVE_ASSERT \ SQLITE_ENABLE_FTS3 \ SQLITE_ENABLE_FTS3_PARENTHESIS \ | > | 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 | SQLITE_OMIT_WAL \ SQLITE_OMIT_WINDOWFUNC \ SQLITE_OMIT_WSD \ SQLITE_OMIT_XFER_OPT \ ] set ::ENABLE_SYMBOLS [list \ SQLITE_ALLOW_ROWID_IN_VIEW \ SQLITE_DISABLE_DIRSYNC \ SQLITE_DISABLE_LFS \ SQLITE_ENABLE_ATOMIC_WRITE \ SQLITE_ENABLE_COLUMN_METADATA \ SQLITE_ENABLE_EXPENSIVE_ASSERT \ SQLITE_ENABLE_FTS3 \ SQLITE_ENABLE_FTS3_PARENTHESIS \ |
︙ | ︙ |