From: drh Date: Sat, 19 Oct 2013 23:31:56 +0000 (+0000) Subject: Experimental changes toward "index only" tables. Add the ability to specify X-Git-Tag: version-3.8.2~137^2~74 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=81eba73ebfbcd7088f9532636082f041b0374a90;p=thirdparty%2Fsqlite.git Experimental changes toward "index only" tables. Add the ability to specify options on CREATE TABLE statements using the WITH clause modeled after PostgreSQL and SQL Server. Only the "omit_rowid" option is currently recognized and that option is currently a no-op. FossilOrigin-Name: 0248ec5e6e3797575388f046d8c27f7445fe2a39 --- diff --git a/manifest b/manifest index 9a49cfa35d..da1f50d532 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Improved\sheader\scomment\swith\sbetter\sinstructions\son\sthe\svfslog.c\nextension. -D 2013-10-19T16:51:39.506 +C Experimental\schanges\stoward\s"index\sonly"\stables.\s\sAdd\sthe\sability\sto\sspecify\noptions\son\sCREATE\sTABLE\sstatements\susing\sthe\sWITH\sclause\smodeled\safter\nPostgreSQL\sand\sSQL\sServer.\s\sOnly\sthe\s"omit_rowid"\soption\sis\scurrently\nrecognized\sand\sthat\soption\sis\scurrently\sa\sno-op. +D 2013-10-19T23:31:56.588 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 6d9a545d726956fdc0c63d7076291fc9e7207484 +F src/build.c 2adfe99b972f1f270e602c39f7c726fa8477343b F src/callback.c f99a8957ba2adf369645fac0db09ad8adcf1caa2 F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac F src/ctime.c ea4b7f3623a0fcb1146e7f245d7410033e86859c @@ -208,7 +208,7 @@ F src/os_unix.c 243fb37f47dc072fc59839ea241ff0a17c8d76e6 F src/os_win.c b159b5249d9f70607d961bbdd1dbba789c75812c F src/pager.c 2aa4444ffe86e9282d03bc349a4a5e49bd77c0e8 F src/pager.h f094af9f6ececfaa8a1e93876905a4f34233fb0c -F src/parse.y a97566d6da75075589a7c716d1bda14b586cf8da +F src/parse.y 909868a9a60caeaa94d439a8b321e5491f989111 F src/pcache.c f8043b433a57aba85384a531e3937a804432a346 F src/pcache.h a5e4f5d9f5d592051d91212c5949517971ae6222 F src/pcache1.c a467393909a4ed7ca9de066d85ba5c5b04a5be63 @@ -223,7 +223,7 @@ F src/shell.c 6f11f0e9ded63d48e306f2c6858c521e568a47bb F src/sqlite.h.in 547a44dd4ff4d975e92a645ea2d609e543a83d0f F src/sqlite3.rc 11094cc6a157a028b301a9f06b3d03089ea37c3e F src/sqlite3ext.h 886f5a34de171002ad46fae8c36a7d8051c190fc -F src/sqliteInt.h 4dd81a25a919509ce94b1f8f120bbef14458d4b9 +F src/sqliteInt.h 64409173b3e74ae26c66e737fa36d0b792dd77f9 F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d F src/status.c 7ac05a5c7017d0b9f0b4bcd701228b784f987158 F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e @@ -298,7 +298,7 @@ F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/aggnested.test 45c0201e28045ad38a530b5a144b73cd4aa2cfd6 F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87 F test/all.test 6ff7b43c2b4b905c74dc4a813d201d0fa64c5783 -F test/alter.test 775a1dded3f8247983c9a3e767b011d9a5114442 +F test/alter.test f128974fbafff7c57e9f5ff2b2ecdd1dd4c4f84d F test/alter2.test 7ea05c7d92ac99349a802ef7ada17294dd647060 F test/alter3.test 49c9d9fba2b8fcdce2dedeca97bbf1f369cc548d F test/alter4.test 8e93bf7a7e6919b14b0c9a6c1e4908bcf21b0165 @@ -824,6 +824,7 @@ F test/syscall.test a653783d985108c4912cc64d341ffbbb55ad2806 F test/sysfault.test fa776e60bf46bdd3ae69f0b73e46ee3977a58ae6 F test/table.test 30423211108121884588d24d6776c7f38702ad7b F test/tableapi.test 2674633fa95d80da917571ebdd759a14d9819126 +F test/tableopts.test 28c869c5590fac5469a6da0a3612485d8c64cd25 F test/tclsqlite.test 37a61c2da7e3bfe3b8c1a2867199f6b860df5d43 F test/tempdb.test 19d0f66e2e3eeffd68661a11c83ba5e6ace9128c F test/temptable.test d2c9b87a54147161bcd1822e30c1d1cd891e5b30 @@ -1091,7 +1092,7 @@ F tool/lemon.c 796930d5fc2036c7636f3f1ee12f9ae03719a2eb F tool/lempar.c 01ca97f87610d1dac6d8cd96ab109ab1130e76dc F tool/logest.c 7ad625cac3d54012b27d468b7af6612f78b9ba75 F tool/mkautoconfamal.sh f8d8dbf7d62f409ebed5134998bf5b51d7266383 -F tool/mkkeywordhash.c bb52064aa614e1426445e4b2b9b00eeecd23cc79 +F tool/mkkeywordhash.c d29369d17069558affef8c4ed5c58ad6c0238c68 F tool/mkopts.tcl 66ac10d240cc6e86abd37dc908d50382f84ff46e F tool/mkpragmatab.tcl 3fc52e00a234750675e8a569d2919ff48558e9eb F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97 @@ -1126,7 +1127,10 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01 F tool/win/sqlite.vsix 030f3eeaf2cb811a3692ab9c14d021a75ce41fff -P 56dca4a65c3b14123272fa0cc5c15530c06fda28 -R 1cee309be53ed4b90f763a6791fd81dc +P 4bd592c8f0e011e203443a6e88008a61d6926df5 +R fbf9e989e7ae142368f9794120af33f8 +T *branch * omit-rowid +T *sym-omit-rowid * +T -sym-trunk * U drh -Z 23f623447e2329bbecfcb9fee8496fa4 +Z 7440405d7d57d4e7a838139e7bed4bdc diff --git a/manifest.uuid b/manifest.uuid index dd2fc69985..fe09693b03 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4bd592c8f0e011e203443a6e88008a61d6926df5 \ No newline at end of file +0248ec5e6e3797575388f046d8c27f7445fe2a39 \ No newline at end of file diff --git a/src/build.c b/src/build.c index 38bdad859f..f17683a9c6 100644 --- a/src/build.c +++ b/src/build.c @@ -1556,7 +1556,9 @@ static void estimateIndexWidth(Index *pIdx){ void sqlite3EndTable( Parse *pParse, /* Parse context */ Token *pCons, /* The ',' token after the last column defn. */ - Token *pEnd, /* The final ')' token in the CREATE TABLE */ + Token *pEnd1, /* The ')' before options in the CREATE TABLE */ + Token *pEnd2, /* The final ')' in the whole CREATE TABLE */ + IdList *pOpts, /* List of table options. May be NULL */ Select *pSelect /* Select from a "CREATE ... AS SELECT" */ ){ Table *p; /* The new table */ @@ -1564,14 +1566,28 @@ void sqlite3EndTable( int iDb; /* Database in which the table lives */ Index *pIdx; /* An implied index of the table */ - if( (pEnd==0 && pSelect==0) || db->mallocFailed ){ - return; + if( (pEnd1==0 && pSelect==0) || db->mallocFailed ){ + goto end_table_exception; } p = pParse->pNewTable; - if( p==0 ) return; + if( p==0 ) goto end_table_exception; assert( !db->init.busy || !pSelect ); + if( pOpts ){ + int i; + for(i=0; inId; i++){ + if( sqlite3_stricmp(pOpts->a[i].zName, "omit_rowid")==0 ){ + p->tabFlags |= TF_WithoutRowid; + if( (p->tabFlags & TF_HasPrimaryKey)==0 ){ + sqlite3ErrorMsg(pParse, "no PRIMARY KEY for table %s", p->zName); + } + continue; + } + sqlite3ErrorMsg(pParse, "unknown table option: %s", pOpts->a[i].zName); + } + } + iDb = sqlite3SchemaToIndex(db, p->pSchema); #ifndef SQLITE_OMIT_CHECK @@ -1612,7 +1628,7 @@ void sqlite3EndTable( char *zStmt; /* Text of the CREATE TABLE or CREATE VIEW statement */ v = sqlite3GetVdbe(pParse); - if( NEVER(v==0) ) return; + if( NEVER(v==0) ) goto end_table_exception; sqlite3VdbeAddOp1(v, OP_Close, 0); @@ -1657,7 +1673,7 @@ void sqlite3EndTable( sqlite3VdbeAddOp1(v, OP_Close, 1); if( pParse->nErr==0 ){ pSelTab = sqlite3ResultSetOfSelect(pParse, pSelect); - if( pSelTab==0 ) return; + if( pSelTab==0 ) goto end_table_exception; assert( p->aCol==0 ); p->nCol = pSelTab->nCol; p->aCol = pSelTab->aCol; @@ -1671,7 +1687,7 @@ void sqlite3EndTable( if( pSelect ){ zStmt = createTableStmt(db, p); }else{ - n = (int)(pEnd->z - pParse->sNameToken.z) + 1; + n = (int)(pEnd2->z - pParse->sNameToken.z) + 1; zStmt = sqlite3MPrintf(db, "CREATE %s %.*s", zType2, n, pParse->sNameToken.z ); @@ -1729,7 +1745,7 @@ void sqlite3EndTable( if( pOld ){ assert( p==pOld ); /* Malloc must have failed inside HashInsert() */ db->mallocFailed = 1; - return; + goto end_table_exception; } pParse->pNewTable = 0; db->flags |= SQLITE_InternChanges; @@ -1738,15 +1754,19 @@ void sqlite3EndTable( if( !p->pSelect ){ const char *zName = (const char *)pParse->sNameToken.z; int nName; - assert( !pSelect && pCons && pEnd ); + assert( !pSelect && pCons && pEnd1 ); if( pCons->z==0 ){ - pCons = pEnd; + pCons = pEnd1; } nName = (int)((const char *)pCons->z - zName); p->addColOffset = 13 + sqlite3Utf8CharLen(zName, nName); } #endif } + +end_table_exception: + sqlite3IdListDelete(db, pOpts); + return; } #ifndef SQLITE_OMIT_VIEW @@ -1819,7 +1839,7 @@ void sqlite3CreateView( sEnd.n = 1; /* Use sqlite3EndTable() to add the view to the SQLITE_MASTER table */ - sqlite3EndTable(pParse, 0, &sEnd, 0); + sqlite3EndTable(pParse, 0, &sEnd, &sEnd, 0, 0); return; } #endif /* SQLITE_OMIT_VIEW */ diff --git a/src/parse.y b/src/parse.y index 1e8d7f751c..85569e2c07 100644 --- a/src/parse.y +++ b/src/parse.y @@ -163,11 +163,15 @@ ifnotexists(A) ::= IF NOT EXISTS. {A = 1;} temp(A) ::= TEMP. {A = 1;} %endif SQLITE_OMIT_TEMPDB temp(A) ::= . {A = 0;} -create_table_args ::= LP columnlist conslist_opt(X) RP(Y). { - sqlite3EndTable(pParse,&X,&Y,0); +create_table_args ::= LP columnlist conslist_opt(X) RP(E1). { + sqlite3EndTable(pParse,&X,&E1,&E1,0,0); +} +create_table_args ::= LP columnlist conslist_opt(X) RP(E1) + WITH LP idlist(Z) RP(E2). { + sqlite3EndTable(pParse,&X,&E1,&E2,Z,0); } create_table_args ::= AS select(S). { - sqlite3EndTable(pParse,0,0,S); + sqlite3EndTable(pParse,0,0,0,0,S); sqlite3SelectDelete(pParse->db, S); } columnlist ::= columnlist COMMA column. @@ -205,7 +209,7 @@ id(A) ::= INDEXED(X). {A = X;} CONFLICT DATABASE DEFERRED DESC DETACH EACH END EXCLUSIVE EXPLAIN FAIL FOR IGNORE IMMEDIATE INITIALLY INSTEAD LIKE_KW MATCH NO PLAN QUERY KEY OF OFFSET PRAGMA RAISE RELEASE REPLACE RESTRICT ROW ROLLBACK - SAVEPOINT TEMP TRIGGER VACUUM VIEW VIRTUAL + SAVEPOINT TEMP TRIGGER VACUUM VIEW VIRTUAL WITH %ifdef SQLITE_OMIT_COMPOUND_SELECT EXCEPT INTERSECT UNION %endif SQLITE_OMIT_COMPOUND_SELECT @@ -573,7 +577,7 @@ indexed_opt(A) ::= NOT INDEXED. {A.z=0; A.n=1;} %type using_opt {IdList*} %destructor using_opt {sqlite3IdListDelete(pParse->db, $$);} -using_opt(U) ::= USING LP inscollist(L) RP. {U = L;} +using_opt(U) ::= USING LP idlist(L) RP. {U = L;} using_opt(U) ::= . {U = 0;} @@ -740,14 +744,14 @@ valuelist(A) ::= valuelist(X) COMMA LP exprlist(Y) RP. { %type inscollist_opt {IdList*} %destructor inscollist_opt {sqlite3IdListDelete(pParse->db, $$);} -%type inscollist {IdList*} -%destructor inscollist {sqlite3IdListDelete(pParse->db, $$);} +%type idlist {IdList*} +%destructor idlist {sqlite3IdListDelete(pParse->db, $$);} inscollist_opt(A) ::= . {A = 0;} -inscollist_opt(A) ::= LP inscollist(X) RP. {A = X;} -inscollist(A) ::= inscollist(X) COMMA nm(Y). +inscollist_opt(A) ::= LP idlist(X) RP. {A = X;} +idlist(A) ::= idlist(X) COMMA nm(Y). {A = sqlite3IdListAppend(pParse->db,X,&Y);} -inscollist(A) ::= nm(Y). +idlist(A) ::= nm(Y). {A = sqlite3IdListAppend(pParse->db,0,&Y);} /////////////////////////// Expression Processing ///////////////////////////// @@ -1227,7 +1231,7 @@ trigger_time(A) ::= . { A = TK_BEFORE; } %destructor trigger_event {sqlite3IdListDelete(pParse->db, $$.b);} trigger_event(A) ::= DELETE|INSERT(OP). {A.a = @OP; A.b = 0;} trigger_event(A) ::= UPDATE(OP). {A.a = @OP; A.b = 0;} -trigger_event(A) ::= UPDATE OF inscollist(X). {A.a = TK_UPDATE; A.b = X;} +trigger_event(A) ::= UPDATE OF idlist(X). {A.a = TK_UPDATE; A.b = X;} foreach_clause ::= . foreach_clause ::= FOR EACH ROW. diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 4974b2d0f3..9f7215e815 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -1411,6 +1411,7 @@ struct Table { #define TF_HasPrimaryKey 0x04 /* Table has a primary key */ #define TF_Autoincrement 0x08 /* Integer primary key is autoincrement */ #define TF_Virtual 0x10 /* Is a virtual table */ +#define TF_WithoutRowid 0x20 /* No rowid used. PRIMARY KEY is the key */ /* @@ -2772,7 +2773,7 @@ void sqlite3AddCheckConstraint(Parse*, Expr*); void sqlite3AddColumnType(Parse*,Token*); void sqlite3AddDefaultValue(Parse*,ExprSpan*); void sqlite3AddCollateType(Parse*, Token*); -void sqlite3EndTable(Parse*,Token*,Token*,Select*); +void sqlite3EndTable(Parse*,Token*,Token*,Token*,IdList*,Select*); int sqlite3ParseUri(const char*,const char*,unsigned int*, sqlite3_vfs**,char**,char **); Btree *sqlite3DbNameToBtree(sqlite3*,const char*); diff --git a/test/alter.test b/test/alter.test index 1ccea22624..4ef5dd9406 100644 --- a/test/alter.test +++ b/test/alter.test @@ -860,5 +860,19 @@ foreach {tn tbl} $system_table_list { } [list 1 "table $tbl may not be altered"] } +#------------------------------------------------------------------------ +# Verify that ALTER TABLE works on tables with WITH options. +# +do_execsql_test alter-16.1 { + CREATE TABLE t16a(a TEXT, b REAL, c INT, PRIMARY KEY(a,b)) WITH (omit_rowid); + INSERT INTO t16a VALUES('abc',1.25,99); + ALTER TABLE t16a ADD COLUMN d TEXT DEFAULT 'xyzzy'; + INSERT INTO t16a VALUES('cba',5.5,98,'fizzle'); + SELECT * FROM t16a ORDER BY a; +} {abc 1.25 99 xyzzy cba 5.5 98 fizzle} +do_execsql_test alter-16.2 { + ALTER TABLE t16a RENAME TO t16a_rn; + SELECT * FROM t16a_rn ORDER BY a; +} {abc 1.25 99 xyzzy cba 5.5 98 fizzle} finish_test diff --git a/test/tableopts.test b/test/tableopts.test new file mode 100644 index 0000000000..ef1dae0a87 --- /dev/null +++ b/test/tableopts.test @@ -0,0 +1,56 @@ +# 2013-10-19 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# +# Test the operation of table-options in the WITH clause of the +# CREATE TABLE statement. +# + + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +do_test tableopt-1.1 { + catchsql { + CREATE TABLE t1(a,b) WITH (omit_rowid); + } +} {1 {no PRIMARY KEY for table t1}} +do_test tableopt-1.2 { + catchsql { + CREATE TABLE t1(a,b) WITH (unknown1, unknown2); + } +} {1 {unknown table option: unknown2}} +do_test tableopt-1.3 { + catchsql { + CREATE TABLE t1(a,b,c,PRIMARY KEY(a,b)) WITH (omit_rowid, unknown3); + } +} {1 {unknown table option: unknown3}} +do_test tableopt-1.4 { + catchsql { + CREATE TABLE t1(a,b,c,PRIMARY KEY(a,b)) WITH (unknown4, omit_rowid); + } +} {1 {unknown table option: unknown4}} + +do_execsql_test tableopt-2.1 { + CREATE TABLE t1(a, b, c, PRIMARY KEY(a,b)) WITH (omit_rowid); + INSERT INTO t1 VALUES(1,2,3),(2,3,4); + SELECT c FROM t1 WHERE a IN (1,2) ORDER BY b; +} {3 4} +do_execsql_test tableopt-2.2 { + VACUUM; + SELECT c FROM t1 WHERE a IN (1,2) ORDER BY b; +} {3 4} +do_test tableopt-2.3 { + sqlite3 db2 test.db + db2 eval {SELECT c FROM t1 WHERE a IN (1,2) ORDER BY b;} +} {3 4} +db2 close + +finish_test diff --git a/tool/mkkeywordhash.c b/tool/mkkeywordhash.c index 4e5ba8f1a8..3e60d38599 100644 --- a/tool/mkkeywordhash.c +++ b/tool/mkkeywordhash.c @@ -262,6 +262,7 @@ static Keyword aKeywordTable[] = { { "VALUES", "TK_VALUES", ALWAYS }, { "VIEW", "TK_VIEW", VIEW }, { "VIRTUAL", "TK_VIRTUAL", VTAB }, + { "WITH", "TK_WITH", ALWAYS }, { "WHEN", "TK_WHEN", ALWAYS }, { "WHERE", "TK_WHERE", ALWAYS }, };