From: drh Date: Mon, 24 Aug 2015 17:42:49 +0000 (+0000) Subject: Enhance the CREATE VIEW syntax so that the names of columns of the view can X-Git-Tag: version-3.9.0~193 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=8981b904b545ad056ad2f6eb0ebef6a6b9606b5b;p=thirdparty%2Fsqlite.git Enhance the CREATE VIEW syntax so that the names of columns of the view can be specified after the view name. FossilOrigin-Name: d794b34da6f9c77dfe17773b0b17b22de72cce7f --- diff --git a/manifest b/manifest index bc1fdc39c8..ac41156aae 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Enhancements\sto\sthe\sbatch\sbuild\stool\sfor\sMSVC. -D 2015-08-24T17:18:43.705 +C Enhance\sthe\sCREATE\sVIEW\ssyntax\sso\sthat\sthe\snames\sof\scolumns\sof\sthe\sview\scan\nbe\sspecified\safter\sthe\sview\sname. +D 2015-08-24T17:42:49.622 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in e2218eb228374422969de7b1680eda6864affcef F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -282,7 +282,7 @@ F src/btmutex.c 45a968cc85afed9b5e6cf55bf1f42f8d18107f79 F src/btree.c f48b3ef91676c06a90a8832987ecef6b94c931ee F src/btree.h 969adc948e89e449220ff0ff724c94bb2a52e9f1 F src/btreeInt.h 8177c9ab90d772d6d2c6c517e05bed774b7c92c0 -F src/build.c f49c55c1fba430c2d6af5039d0bf14de5ae6a427 +F src/build.c a4e2669bc59729589b7e3867c58eae5a4f2419ff F src/callback.c 7b44ce59674338ad48b0e84e7b72f935ea4f68b0 F src/complete.c addcd8160b081131005d5bc2d34adf20c1c5c92f F src/ctime.c 5a0b735dc95604766f5dac73973658eef782ee8b @@ -326,7 +326,7 @@ F src/os_win.c 40b3af7a47eb1107d0d69e592bec345a3b7b798a F src/os_win.h eb7a47aa17b26b77eb97e4823f20a00b8bda12ca F src/pager.c aa916ca28606ccf4b6877dfc2b643ccbca86589f F src/pager.h 6d435f563b3f7fcae4b84433b76a6ac2730036e2 -F src/parse.y b5e0a5f8bb9ec33a58aa34850b6fae46aac51fdd +F src/parse.y 07f2084f9ec157b108f1bf12466277d6f17b59d1 F src/pcache.c cde06aa50962595e412d497e22fd2e07878ba1f0 F src/pcache.h 9968603796240cdf83da7e7bef76edf90619cea9 F src/pcache1.c a3fe31b17e841ec70beee72a2c960e9c787a8857 @@ -337,12 +337,12 @@ F src/printf.c 2bc439ff20a4aad0e0ad50a37a67b5eae7d20edc F src/random.c ba2679f80ec82c4190062d756f22d0c358180696 F src/resolve.c 7a67cd2aebc9a9eeecd1d104eb6a9237388eb452 F src/rowset.c eccf6af6d620aaa4579bd3b72c1b6395d9e9fa1e -F src/select.c 24323faace224dce1e6b65276605de1db39d77a4 +F src/select.c da6d1e7a4f1c8d713ed5415b5ed21d82ef465c0f F src/shell.c b1f91e60918df3a68efad1e3a11696b9a7e23d23 F src/sqlite.h.in 378bebc8fe6a88bade25e5f23b7e6123fdc64b00 F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad F src/sqlite3ext.h f700e6a9dd1fdcccc9951ab022b366fb66b9e413 -F src/sqliteInt.h 1741691824491c330dda1e49f370920bfd3aa230 +F src/sqliteInt.h 79a8e76bcbe67170d371ae2a08c85cc2d7cd0caf F src/sqliteLimit.h 216557999cb45f2e3578ed53ebefe228d779cb46 F src/status.c f266ad8a2892d659b74f0f50cb6a88b6e7c12179 F src/table.c 51b46b2a62d1b3a959633d593b89bab5e2c9155e @@ -1239,7 +1239,7 @@ F test/vacuum3.test 77ecdd54592b45a0bcb133339f99f1ae0ae94d0d F test/vacuum4.test d3f8ecff345f166911568f397d2432c16d2867d9 F test/varint.test ab7b110089a08b9926ed7390e7e97bdefeb74102 F test/veryquick.test 57ab846bacf7b90cf4e9a672721ea5c5b669b661 -F test/view.test f44014f78d7650fb4bfb8ef96a5e4dc8f25eb083 +F test/view.test 3930ae94042d702ab15a6a0ef692cfa5c9f9b68b F test/vtab1.test 6210e076997f176bedc300a87ad6404651b601dd F test/vtab2.test f8cd1bb9aba7143eba97812d9617880a36d247ad F test/vtab3.test b45f47d20f225ccc9c28dc915d92740c2dee311e @@ -1379,7 +1379,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 83cbc4d8761498647794affffa961a4fca311be7 -R 854ee56e3c07dedcc4aa69e0f36f6060 -U mistachkin -Z 2aa32a441452e19f6c0e00af3c0b6972 +P a1ae20cd97456a1126cfa1a9bedce0bac0940ad6 +R 7c6b5f2ebead0dd2f6f395aca9a96d64 +U drh +Z 9176898b8b319f8964514d527c2d8cd5 diff --git a/manifest.uuid b/manifest.uuid index fbc4c6a88f..358a0a2590 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a1ae20cd97456a1126cfa1a9bedce0bac0940ad6 \ No newline at end of file +d794b34da6f9c77dfe17773b0b17b22de72cce7f \ No newline at end of file diff --git a/src/build.c b/src/build.c index 0a816b398e..815b17deb8 100644 --- a/src/build.c +++ b/src/build.c @@ -2056,6 +2056,7 @@ void sqlite3CreateView( Token *pBegin, /* The CREATE token that begins the statement */ Token *pName1, /* The token that holds the name of the view */ Token *pName2, /* The token that holds the name of the view */ + ExprList *pCNames, /* Optional list of view column names */ Select *pSelect, /* A SELECT statement that will become the new view */ int isTemp, /* TRUE for a TEMPORARY view */ int noErr /* Suppress error messages if VIEW already exists */ @@ -2075,18 +2076,13 @@ void sqlite3CreateView( return; } sqlite3StartTable(pParse, pName1, pName2, isTemp, 1, 0, noErr); + sqlite3RestrictColumnListSyntax(pParse, pCNames); p = pParse->pNewTable; - if( p==0 || pParse->nErr ){ - sqlite3SelectDelete(db, pSelect); - return; - } + 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) ){ - sqlite3SelectDelete(db, pSelect); - return; - } + 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 @@ -2094,27 +2090,31 @@ void sqlite3CreateView( ** they will persist after the current sqlite3_exec() call returns. */ p->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE); - sqlite3SelectDelete(db, pSelect); - if( db->mallocFailed ){ - return; - } + p->pCheck = sqlite3ExprListDup(db, pCNames, EXPRDUP_REDUCE); + if( db->mallocFailed ) goto create_view_fail; /* Locate the end of the CREATE VIEW statement. Make sEnd point to ** the end. */ sEnd = pParse->sLastToken; - if( ALWAYS(sEnd.z[0]!=0) && sEnd.z[0]!=';' ){ + assert( sEnd.z[0]!=0 ); + if( sEnd.z[0]!=';' ){ sEnd.z += sEnd.n; } sEnd.n = 0; n = (int)(sEnd.z - pBegin->z); + assert( n>0 ); z = pBegin->z; - while( ALWAYS(n>0) && sqlite3Isspace(z[n-1]) ){ n--; } + while( sqlite3Isspace(z[n-1]) ){ n--; } sEnd.z = &z[n-1]; sEnd.n = 1; /* Use sqlite3EndTable() to add the view to the SQLITE_MASTER table */ sqlite3EndTable(pParse, 0, &sEnd, 0, 0); + +create_view_fail: + sqlite3SelectDelete(db, pSelect); + sqlite3ExprListDelete(db, pCNames); return; } #endif /* SQLITE_OMIT_VIEW */ @@ -2132,6 +2132,7 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){ int n; /* Temporarily holds the number of cursors assigned */ sqlite3 *db = pParse->db; /* Database connection for malloc errors */ sqlite3_xauth xAuth; /* Saved xAuth pointer */ + u8 bEnabledLA; /* Saved db->lookaside.bEnabled state */ assert( pTable ); @@ -2177,40 +2178,46 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){ ** statement that defines the view. */ assert( pTable->pSelect ); - pSel = sqlite3SelectDup(db, pTable->pSelect, 0); - if( pSel ){ - u8 enableLookaside = db->lookaside.bEnabled; - n = pParse->nTab; - sqlite3SrcListAssignCursors(pParse, pSel->pSrc); - pTable->nCol = -1; + bEnabledLA = db->lookaside.bEnabled; + if( pTable->pCheck ){ db->lookaside.bEnabled = 0; + sqlite3ColumnsFromExprList(pParse, pTable->pCheck, + &pTable->nCol, &pTable->aCol); + }else{ + pSel = sqlite3SelectDup(db, pTable->pSelect, 0); + if( pSel ){ + n = pParse->nTab; + sqlite3SrcListAssignCursors(pParse, pSel->pSrc); + pTable->nCol = -1; + db->lookaside.bEnabled = 0; #ifndef SQLITE_OMIT_AUTHORIZATION - xAuth = db->xAuth; - db->xAuth = 0; - pSelTab = sqlite3ResultSetOfSelect(pParse, pSel); - db->xAuth = xAuth; + xAuth = db->xAuth; + db->xAuth = 0; + pSelTab = sqlite3ResultSetOfSelect(pParse, pSel); + db->xAuth = xAuth; #else - pSelTab = sqlite3ResultSetOfSelect(pParse, pSel); + pSelTab = sqlite3ResultSetOfSelect(pParse, pSel); #endif - db->lookaside.bEnabled = enableLookaside; - pParse->nTab = n; - if( pSelTab ){ - assert( pTable->aCol==0 ); - pTable->nCol = pSelTab->nCol; - pTable->aCol = pSelTab->aCol; - pSelTab->nCol = 0; - pSelTab->aCol = 0; - sqlite3DeleteTable(db, pSelTab); - assert( sqlite3SchemaMutexHeld(db, 0, pTable->pSchema) ); - pTable->pSchema->schemaFlags |= DB_UnresetViews; - }else{ - pTable->nCol = 0; + pParse->nTab = n; + if( pSelTab ){ + assert( pTable->aCol==0 ); + pTable->nCol = pSelTab->nCol; + pTable->aCol = pSelTab->aCol; + pSelTab->nCol = 0; + pSelTab->aCol = 0; + sqlite3DeleteTable(db, pSelTab); + assert( sqlite3SchemaMutexHeld(db, 0, pTable->pSchema) ); + }else{ + pTable->nCol = 0; + nErr++; + } + sqlite3SelectDelete(db, pSel); + } else { nErr++; } - sqlite3SelectDelete(db, pSel); - } else { - nErr++; } + db->lookaside.bEnabled = bEnabledLA; + pTable->pSchema->schemaFlags |= DB_UnresetViews; #endif /* SQLITE_OMIT_VIEW */ return nErr; } diff --git a/src/parse.y b/src/parse.y index bc193ec1f2..acd899449e 100644 --- a/src/parse.y +++ b/src/parse.y @@ -386,8 +386,9 @@ ifexists(A) ::= . {A = 0;} ///////////////////// The CREATE VIEW statement ///////////////////////////// // %ifndef SQLITE_OMIT_VIEW -cmd ::= createkw(X) temp(T) VIEW ifnotexists(E) nm(Y) dbnm(Z) AS select(S). { - sqlite3CreateView(pParse, &X, &Y, &Z, S, T, E); +cmd ::= createkw(X) temp(T) VIEW ifnotexists(E) nm(Y) dbnm(Z) idxlist_opt(C) + AS select(S). { + sqlite3CreateView(pParse, &X, &Y, &Z, C, S, T, E); } cmd ::= DROP VIEW ifexists(E) fullname(X). { sqlite3DropTable(pParse, X, 1, E); @@ -784,11 +785,11 @@ setlist(A) ::= nm(X) EQ expr(Y). { ////////////////////////// The INSERT command ///////////////////////////////// // -cmd ::= with(W) insert_cmd(R) INTO fullname(X) inscollist_opt(F) select(S). { +cmd ::= with(W) insert_cmd(R) INTO fullname(X) idlist_opt(F) select(S). { sqlite3WithPush(pParse, W, 1); sqlite3Insert(pParse, X, S, F, R); } -cmd ::= with(W) insert_cmd(R) INTO fullname(X) inscollist_opt(F) DEFAULT VALUES. +cmd ::= with(W) insert_cmd(R) INTO fullname(X) idlist_opt(F) DEFAULT VALUES. { sqlite3WithPush(pParse, W, 1); sqlite3Insert(pParse, X, 0, F, R); @@ -798,13 +799,13 @@ cmd ::= with(W) insert_cmd(R) INTO fullname(X) inscollist_opt(F) DEFAULT VALUES. insert_cmd(A) ::= INSERT orconf(R). {A = R;} insert_cmd(A) ::= REPLACE. {A = OE_Replace;} -%type inscollist_opt {IdList*} -%destructor inscollist_opt {sqlite3IdListDelete(pParse->db, $$);} +%type idlist_opt {IdList*} +%destructor idlist_opt {sqlite3IdListDelete(pParse->db, $$);} %type idlist {IdList*} %destructor idlist {sqlite3IdListDelete(pParse->db, $$);} -inscollist_opt(A) ::= . {A = 0;} -inscollist_opt(A) ::= LP idlist(X) RP. {A = X;} +idlist_opt(A) ::= . {A = 0;} +idlist_opt(A) ::= LP idlist(X) RP. {A = X;} idlist(A) ::= idlist(X) COMMA nm(Y). {A = sqlite3IdListAppend(pParse->db,X,&Y);} idlist(A) ::= nm(Y). @@ -1369,7 +1370,7 @@ trigger_cmd(A) ::= { A = sqlite3TriggerUpdateStep(pParse->db, &X, Y, Z, R); } // INSERT -trigger_cmd(A) ::= insert_cmd(R) INTO trnm(X) inscollist_opt(F) select(S). +trigger_cmd(A) ::= insert_cmd(R) INTO trnm(X) idlist_opt(F) select(S). {A = sqlite3TriggerInsertStep(pParse->db, &X, F, S, R);} // DELETE diff --git a/src/select.c b/src/select.c index e767111d8d..2581a240e9 100644 --- a/src/select.c +++ b/src/select.c @@ -1587,7 +1587,7 @@ static void generateColumnNames( ** Return SQLITE_OK on success. If a memory allocation error occurs, ** store NULL in *paCol and 0 in *pnCol and return SQLITE_NOMEM. */ -static int selectColumnsFromExprList( +int sqlite3ColumnsFromExprList( Parse *pParse, /* Parsing context */ ExprList *pEList, /* Expr list from which to derive column names */ i16 *pnCol, /* Write the number of columns here */ @@ -1754,7 +1754,7 @@ Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect){ pTab->nRef = 1; pTab->zName = 0; pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) ); - selectColumnsFromExprList(pParse, pSelect->pEList, &pTab->nCol, &pTab->aCol); + sqlite3ColumnsFromExprList(pParse, pSelect->pEList, &pTab->nCol, &pTab->aCol); selectAddColumnTypeAndCollation(pParse, pTab, pSelect); pTab->iPKey = -1; if( db->mallocFailed ){ @@ -4121,7 +4121,7 @@ static int withExpand( pEList = pCte->pCols; } - selectColumnsFromExprList(pParse, pEList, &pTab->nCol, &pTab->aCol); + sqlite3ColumnsFromExprList(pParse, pEList, &pTab->nCol, &pTab->aCol); if( bMayRecursive ){ if( pSel->selFlags & SF_Recursive ){ pCte->zErr = "multiple recursive references: %s"; @@ -4244,7 +4244,7 @@ static int selectExpander(Walker *pWalker, Select *p){ pTab->nRef = 1; pTab->zName = sqlite3MPrintf(db, "sqlite_sq_%p", (void*)pTab); while( pSel->pPrior ){ pSel = pSel->pPrior; } - selectColumnsFromExprList(pParse, pSel->pEList, &pTab->nCol, &pTab->aCol); + sqlite3ColumnsFromExprList(pParse, pSel->pEList,&pTab->nCol,&pTab->aCol); pTab->iPKey = -1; pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) ); pTab->tabFlags |= TF_Ephemeral; diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 18a0fd705f..5f12350538 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -1632,9 +1632,8 @@ struct Table { Select *pSelect; /* NULL for tables. Points to definition if a view. */ FKey *pFKey; /* Linked list of all foreign keys in this table */ char *zColAff; /* String defining the affinity of each column */ -#ifndef SQLITE_OMIT_CHECK ExprList *pCheck; /* All CHECK constraints */ -#endif + /* ... also used as column name list in a VIEW */ int tnum; /* Root BTree page for this table */ i16 iPKey; /* If not negative, use aCol[iPKey] as the rowid */ i16 nCol; /* Number of columns in this table */ @@ -3260,6 +3259,7 @@ void sqlite3CollapseDatabaseArray(sqlite3*); void sqlite3BeginParse(Parse*,int); void sqlite3CommitInternalChanges(sqlite3*); void sqlite3DeleteColumnNames(sqlite3*,Table*); +int sqlite3ColumnsFromExprList(Parse*,ExprList*,i16*,Column**); Table *sqlite3ResultSetOfSelect(Parse*,Select*); void sqlite3OpenMasterTable(Parse *, int); Index *sqlite3PrimaryKeyIndex(Table*); @@ -3301,7 +3301,7 @@ void sqlite3RowSetInsert(RowSet*, i64); int sqlite3RowSetTest(RowSet*, int iBatch, i64); int sqlite3RowSetNext(RowSet*, i64*); -void sqlite3CreateView(Parse*,Token*,Token*,Token*,Select*,int,int); +void sqlite3CreateView(Parse*,Token*,Token*,Token*,ExprList*,Select*,int,int); #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE) int sqlite3ViewGetColumnNames(Parse*,Table*); diff --git a/test/view.test b/test/view.test index a5fe85e648..b39487689c 100644 --- a/test/view.test +++ b/test/view.test @@ -152,6 +152,15 @@ do_test view-3.3.2 { SELECT * FROM v1b LIMIT 1 } } {a 2 b+c 7 c 4} +do_test view-3.3.3 { + execsql2 { + CREATE VIEW v1c(x,y,z) AS SELECT a, b+c, c-b FROM t1; + SELECT * FROM v1c LIMIT 1; + } +} {x 2 y 7 z 1} +do_catchsql_test view-3.3.4 { + CREATE VIEW v1err(x,y DESC,z) AS SELECT a, b+c, c-b FROM t1; +} {1 {syntax error after column name "y"}} ifcapable compound { do_test view-3.4 { @@ -337,7 +346,7 @@ do_test view-8.2 { } {7 2 13 5 19 8 27 12} do_test view-8.3 { execsql { - CREATE VIEW v7 AS SELECT pqr+xyz AS a FROM v6; + CREATE VIEW v7(a) AS SELECT pqr+xyz FROM v6; SELECT * FROM v7 ORDER BY a; } } {9 18 27 39}