From: drh Date: Sat, 2 Nov 2013 14:37:18 +0000 (+0000) Subject: Store the root page of the PRIMARY KEY index for a WITHOUT ROWID table in X-Git-Tag: version-3.8.2~137^2~30 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=c6bd4e4a362d360d5d96bb67b820b0b0375989c2;p=thirdparty%2Fsqlite.git Store the root page of the PRIMARY KEY index for a WITHOUT ROWID table in the sqlite_master entry for the main table and omit the sqlite_master entry for the PRIMARY KEY. FossilOrigin-Name: b7544bb280f1c1c55135a9b35aeb85604fef94a3 --- diff --git a/manifest b/manifest index cd6383f16f..c01b546d05 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Import\sthe\ssqlite3_analyzer\sfixes\sfrom\strunk. -D 2013-11-02T11:43:05.572 +C Store\sthe\sroot\spage\sof\sthe\sPRIMARY\sKEY\sindex\sfor\sa\sWITHOUT\sROWID\stable\sin\nthe\ssqlite_master\sentry\sfor\sthe\smain\stable\sand\somit\sthe\ssqlite_master\sentry\nfor\sthe\sPRIMARY\sKEY. +D 2013-11-02T14:37:18.563 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 0522b53cdc1fcfc18f3a98e0246add129136c654 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -168,7 +168,7 @@ F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7 F src/btree.c 509722ce305471b626d3401c0631a808fd33237b F src/btree.h bfe0e8c5759b4ec77b0d18390064a6ef3cdffaaf F src/btreeInt.h f038e818bfadf75afbd09819ed93c26a333d39e0 -F src/build.c bc63356abffdde0271f8d7667bad32e0566debe1 +F src/build.c 38d6d7396213a5320ce6e822145b671e3e2653eb F src/callback.c f99a8957ba2adf369645fac0db09ad8adcf1caa2 F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac F src/ctime.c ea4b7f3623a0fcb1146e7f245d7410033e86859c @@ -213,7 +213,7 @@ F src/pcache.c f8043b433a57aba85384a531e3937a804432a346 F src/pcache.h a5e4f5d9f5d592051d91212c5949517971ae6222 F src/pcache1.c a467393909a4ed7ca9de066d85ba5c5b04a5be63 F src/pragma.c ff1a98998d2038bc9c770326986b7c4728de4973 -F src/prepare.c ea231a8450eef356490b09481db9fe51a6a59c32 +F src/prepare.c fa6988589f39af8504a61731614cd4f6ae71554f F src/printf.c da9119eb31a187a4b99f60aa4a225141c0ebb74b F src/random.c 0b2dbc37fdfbfa6bd455b091dfcef5bdb32dba68 F src/resolve.c e729889b2c7a680ba4aa7296efa72c09369956d8 @@ -223,7 +223,7 @@ F src/shell.c 03d8d9b4052430343ff30d646334621f980f1202 F src/sqlite.h.in 547a44dd4ff4d975e92a645ea2d609e543a83d0f F src/sqlite3.rc 11094cc6a157a028b301a9f06b3d03089ea37c3e F src/sqlite3ext.h 886f5a34de171002ad46fae8c36a7d8051c190fc -F src/sqliteInt.h bc4588b0d2eac8429d102609af6cfad583bfb41f +F src/sqliteInt.h 7cc1b32804563f26cf44a4a48ad370490c8f737c F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d F src/status.c 7ac05a5c7017d0b9f0b4bcd701228b784f987158 F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e @@ -273,7 +273,7 @@ F src/test_thread.c 1e133a40b50e9c035b00174035b846e7eef481cb F src/test_vfs.c e72f555ef7a59080f898fcf1a233deb9eb704ea9 F src/test_vfstrace.c 34b544e80ba7fb77be15395a609c669df2e660a2 F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9 -F src/tokenize.c 70061085a51f2f4fc15ece94f32c03bcb78e63b2 +F src/tokenize.c ec4c1a62b890bf1dbcdb966399e140b904c700a4 F src/trigger.c 53d6b5d50b3b23d4fcd0a36504feb5cff9aed716 F src/update.c 94d63d3e06b09df3618655a841dc95d5b9466dc6 F src/utf.c 6fc6c88d50448c469c5c196acf21617a24f90269 @@ -387,7 +387,7 @@ F test/colname.test 08948a4809d22817e0e5de89c7c0a8bd90cb551b F test/conflict.test 0b3922d2304a14a47e3ccd61bbd6824327af659b F test/contrib01.test 2a1cbc0f2f48955d7d073f725765da6fbceda6b4 F test/corrupt.test 4aabd06cff3fe759e3e658bcc17b71789710665e -F test/corrupt2.test b8174976fab5bc000e58539ceb3bb9f31b4813f8 +F test/corrupt2.test 9c0ab4becd50e9050bc1ebb8675456a4e5587bf0 F test/corrupt3.test 889d7cdb811800303aa722d7813fe8a4299cf726 F test/corrupt4.test b963f9e01e0f92d15c76fb0747876fd4b96dc30a F test/corrupt5.test c23da7bfb20917cc7fdbb13ee25c7cc4e9fffeff @@ -1128,7 +1128,7 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01 F tool/win/sqlite.vsix 030f3eeaf2cb811a3692ab9c14d021a75ce41fff -P 302a81390f039fc23eeb8510e95b9d9fa0b41edd 42a11e7464ab1d97d603c7409f10710ad4f1f542 -R 3792eebb99bae3c39c82ab2be948da7e +P ac711459ff243e787ea5e9c01720dff75a5eda9b +R ae34f785e9853a53b0aaa3951983c6d8 U drh -Z 67feff5a16da3f8a7e3efdb5ed709f00 +Z 71ade7b873cbec3da5d0aa4bb763c9f6 diff --git a/manifest.uuid b/manifest.uuid index f638e5858c..2764a20dca 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ac711459ff243e787ea5e9c01720dff75a5eda9b \ No newline at end of file +b7544bb280f1c1c55135a9b35aeb85604fef94a3 \ No newline at end of file diff --git a/src/build.c b/src/build.c index 16b0f4840c..666d1b8ba8 100644 --- a/src/build.c +++ b/src/build.c @@ -1275,11 +1275,14 @@ void sqlite3AddPrimaryKey( "INTEGER PRIMARY KEY"); #endif }else{ + Vdbe *v = pParse->pVdbe; Index *p; + if( v ) pParse->addrSkipPK = sqlite3VdbeAddOp0(v, OP_Noop); p = sqlite3CreateIndex(pParse, 0, 0, 0, pList, onError, 0, 0, sortOrder, 0); if( p ){ p->autoIndex = 2; + if( v ) sqlite3VdbeJumpHere(v, pParse->addrSkipPK); } pList = 0; } @@ -1590,13 +1593,27 @@ static int hasColumn(const i16 *aiCol, int nCol, int x){ } /* -** The table pTab has a WITHOUT ROWID clause at the end. Go through and -** make all the changes necessary to make this a WITHOUT ROWID table. +** This routine runs at the end of parsing a CREATE TABLE statement that +** has a WITHOUT ROWID clause. The job of this routine is to convert both +** internal schema data structures and the generated VDBE code so that they +** are appropriate for a WITHOUT ROWID table instead of a rowid table. +** Changes include: ** -** (1) Convert the OP_CreateTable into an no-op. -** (2) Make sure all table columns are part of the PRIMARY KEY -** (3) Make sure all PRIMARY KEY columns are part of all UNIQUE -** indices +** (1) Convert the OP_CreateTable into an OP_CreateIndex. There is +** no rowid btree for a WITHOUT ROWID. Instead, the canonical +** data storage is a covering index btree. +** (2) Bypass the creation of the sqlite_master table entry +** for the PRIMARY KEY as the the primary key index is now +** identified by the sqlite_master table entry of the table itself. +** (3) Set the Index.tnum of the PRIMARY KEY Index object in the +** schema to the rootpage from the main table. +** (4) Set all columns of the PRIMARY KEY schema object to be NOT NULL. +** (5) Add all table columns to the PRIMARY KEY Index object +** so that the PRIMARY KEY is a covering index. The surplus +** columns are part of KeyInfo.nXField and are not used for +** sorting or lookup or uniqueness checks. +** (6) Replace the rowid tail on all automatically generated UNIQUE +** indices with the PRIMARY KEY columns. */ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){ Index *pIdx; @@ -1604,15 +1621,23 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){ int nPk; int i, j; sqlite3 *db = pParse->db; + Vdbe *v = pParse->pVdbe; /* Convert the OP_CreateTable opcode that would normally create the - ** root-page for the table into a OP_Null opcode. This prevents the - ** allocation of the root-page (which would never been used, as all - ** content is stored in the primary-key index instead) and it causes - ** a NULL value in the sqlite_master.rootpage field of the schema. + ** root-page for the table into a OP_CreateIndex opcode. The index + ** created will become the PRIMARY KEY index. */ if( pParse->addrCrTab ){ - sqlite3VdbeGetOp(pParse->pVdbe, pParse->addrCrTab)->opcode = OP_Null; + assert( v ); + sqlite3VdbeGetOp(v, pParse->addrCrTab)->opcode = OP_CreateIndex; + } + + /* Bypass the creation of the PRIMARY KEY btree and the sqlite_master + ** table entry. + */ + if( pParse->addrSkipPK ){ + assert( v ); + sqlite3VdbeGetOp(v, pParse->addrSkipPK)->opcode = OP_Goto; } /* Locate the PRIMARY KEY index. Or, if this table was originally @@ -1641,6 +1666,9 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){ } pPk->uniqNotNull = 1; + /* The root page of the PRIMARY KEY is the table root page */ + pPk->tnum = pTab->tnum; + /* Update the in-memory representation of all UNIQUE indices by converting ** the final rowid column into one or more columns of the PRIMARY KEY. */ @@ -1723,6 +1751,17 @@ void sqlite3EndTable( assert( !db->init.busy || !pSelect ); + /* If the db->init.busy is 1 it means we are reading the SQL off the + ** "sqlite_master" or "sqlite_temp_master" table on the disk. + ** So do not write to the disk again. Extract the root page number + ** for the table from the db->init.newTnum field. (The page number + ** should have been put there by the sqliteOpenCb routine.) + */ + if( db->init.busy ){ + p->tnum = db->init.newTnum; + } + + /* Special processing for WITHOUT ROWID Tables */ if( tabOpts & TF_WithoutRowid ){ if( (p->tabFlags & TF_HasPrimaryKey)==0 ){ sqlite3ErrorMsg(pParse, "no PRIMARY KEY for table %s", p->zName); @@ -1748,16 +1787,6 @@ void sqlite3EndTable( estimateIndexWidth(pIdx); } - /* If the db->init.busy is 1 it means we are reading the SQL off the - ** "sqlite_master" or "sqlite_temp_master" table on the disk. - ** So do not write to the disk again. Extract the root page number - ** for the table from the db->init.newTnum field. (The page number - ** should have been put there by the sqliteOpenCb routine.) - */ - if( db->init.busy ){ - p->tnum = db->init.newTnum; - } - /* If not initializing, then create a record for the new table ** in the SQLITE_MASTER table of the database. ** diff --git a/src/prepare.c b/src/prepare.c index 88a6b65288..cfc9c34855 100644 --- a/src/prepare.c +++ b/src/prepare.c @@ -65,7 +65,9 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){ assert( iDb>=0 && iDbnDb ); if( argv==0 ) return 0; /* Might happen if EMPTY_RESULT_CALLBACKS are on */ - if( argv[2] && argv[2][0] ){ + if( argv[1]==0 ){ + corruptSchema(pData, argv[0], 0); + }else if( argv[2] && argv[2][0] ){ /* Call the parser to process a CREATE TABLE, INDEX or VIEW. ** But because db->init.busy is set to 1, no VDBE code is generated ** or executed. All the parser does is build the internal data @@ -77,7 +79,7 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){ assert( db->init.busy ); db->init.iDb = iDb; - db->init.newTnum = argv[1] ? sqlite3Atoi(argv[1]) : 0; + db->init.newTnum = sqlite3Atoi(argv[1]); db->init.orphanTrigger = 0; TESTONLY(rcp = ) sqlite3_prepare(db, argv[2], -1, &pStmt, 0); rc = db->errCode; @@ -116,10 +118,6 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){ /* Do Nothing */; }else if( sqlite3GetInt32(argv[1], &pIndex->tnum)==0 ){ corruptSchema(pData, argv[0], "invalid rootpage"); - }else if( pIndex->autoIndex==2 - && (pIndex->pTable->tabFlags & TF_WithoutRowid)!=0 - ){ - pIndex->pTable->tnum = pIndex->tnum; } } return 0; diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 97b75f16b2..8890cbeaf4 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -2283,6 +2283,8 @@ struct Parse { /* Information used while coding trigger programs. */ Parse *pToplevel; /* Parse structure for main program (or NULL) */ Table *pTriggerTab; /* Table triggers are being coded for */ + int addrCrTab; /* Address of OP_CreateTable opcode on CREATE TABLE */ + int addrSkipPK; /* Address of instruction to skip PRIMARY KEY index */ u32 nQueryLoop; /* Est number of iterations of a query (10*log2(N)) */ u32 oldmask; /* Mask of old.* columns referenced */ u32 newmask; /* Mask of new.* columns referenced */ @@ -2295,7 +2297,6 @@ struct Parse { int nVar; /* Number of '?' variables seen in the SQL so far */ int nzVar; /* Number of available slots in azVar[] */ - int addrCrTab; /* Address of OP_CreateTable opcode */ u8 iPkSortOrder; /* ASC or DESC for INTEGER PRIMARY KEY */ u8 explain; /* True if the EXPLAIN flag is found on the query */ #ifndef SQLITE_OMIT_VIRTUALTABLE @@ -2310,7 +2311,6 @@ struct Parse { #endif char **azVar; /* Pointers to names of parameters */ Vdbe *pReprepare; /* VM being reprepared (sqlite3Reprepare()) */ - int *aAlias; /* Register used to hold aliased result */ const char *zTail; /* All SQL text past the last semicolon parsed */ Table *pNewTable; /* A table being constructed by CREATE TABLE */ Trigger *pNewTrigger; /* Trigger under construct by a CREATE TRIGGER */ diff --git a/src/tokenize.c b/src/tokenize.c index d26157f7d2..f27929ea5b 100644 --- a/src/tokenize.c +++ b/src/tokenize.c @@ -506,7 +506,6 @@ abort_parse: sqlite3DeleteTrigger(db, pParse->pNewTrigger); for(i=pParse->nzVar-1; i>=0; i--) sqlite3DbFree(db, pParse->azVar[i]); sqlite3DbFree(db, pParse->azVar); - sqlite3DbFree(db, pParse->aAlias); while( pParse->pAinc ){ AutoincInfo *p = pParse->pAinc; pParse->pAinc = p->pNext; diff --git a/test/corrupt2.test b/test/corrupt2.test index f395a0ba91..744a76ed04 100644 --- a/test/corrupt2.test +++ b/test/corrupt2.test @@ -466,11 +466,6 @@ corruption_test -sqlprep { } {1 {database disk image is malformed}} } -# Since the introduction of WITHOUT ROWID tables, having a table entry in -# the sqlite_master table with a NULL rootpage is no longer a sign of -# corruption. -# -if 0 { corruption_test -sqlprep { CREATE TABLE t1(a, b, c); CREATE TABLE t2(a, b, c); @@ -484,7 +479,6 @@ corruption_test -sqlprep { sqlite3_errcode db } {SQLITE_CORRUPT} } -} ;# Disabled rootpage==NULL corruption test corruption_test -sqlprep { PRAGMA auto_vacuum = incremental;