diff options
-rw-r--r-- | main.mk | 2 | ||||
-rw-r--r-- | manifest | 48 | ||||
-rw-r--r-- | manifest.uuid | 2 | ||||
-rw-r--r-- | src/analyze.c | 3 | ||||
-rw-r--r-- | src/attach.c | 2 | ||||
-rw-r--r-- | src/expr.c | 28 | ||||
-rw-r--r-- | src/func.c | 2 | ||||
-rw-r--r-- | src/parse.y | 60 | ||||
-rw-r--r-- | src/resolve.c | 48 | ||||
-rw-r--r-- | src/select.c | 357 | ||||
-rw-r--r-- | src/sqliteInt.h | 61 | ||||
-rw-r--r-- | src/test1.c | 7 | ||||
-rw-r--r-- | src/vdbe.c | 6 | ||||
-rw-r--r-- | src/vdbeInt.h | 1 | ||||
-rw-r--r-- | src/vdbemem.c | 17 | ||||
-rw-r--r-- | src/window.c | 53 | ||||
-rw-r--r-- | test/permutations.test | 2 | ||||
-rw-r--r-- | test/window1.test | 112 | ||||
-rw-r--r-- | tool/mkkeywordhash.c | 9 |
19 files changed, 759 insertions, 61 deletions
@@ -75,7 +75,7 @@ LIBOBJ+= vdbe.o parse.o \ update.o upsert.o userauth.o util.o vacuum.o \ vdbeapi.o vdbeaux.o vdbeblob.o vdbemem.o vdbesort.o \ vdbetrace.o wal.o walker.o where.o wherecode.o whereexpr.o \ - utf.o vtab.o + utf.o vtab.o window.o LIBOBJ += sqlite3session.o @@ -1,5 +1,5 @@ -C Enhance\sthe\ssqlite3_str_new()\sinterface\sso\sthat\sit\salways\sreturns\sa\svalid\nand\snon-NULL\spointer\seven\sin\san\sOOM\scondition. -D 2018-05-16T15:35:03.770 +C Start\sof\sexperimental\simplementation\sof\sSQL\swindow\sfunctions.\sDoes\snot\syet\nwork. +D 2018-05-16T20:58:07.009 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F Makefile.in bfc40f350586923e0419d2ea4b559c37ec10ee4b6e210e08c14401f8e340f0da @@ -415,7 +415,7 @@ F ext/userauth/userauth.c 3410be31283abba70255d71fd24734e017a4497f F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 -F main.mk 3f50dfe5cb4257c1aca96e417636ed51bc2561e71d31a21e9ccdf66feb912f43 +F main.mk e829e6dca3fcf542747d0c210516d5e6893f773099b2e5deb924eb7dc70f7384 F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83 F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271 F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504 @@ -428,8 +428,8 @@ F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786 F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a F src/alter.c cf7a8af45cb0ace672f47a1b29ab24092a9e8cd8d945a9974e3b5d925f548594 -F src/analyze.c 71fbbeb7b25417592f54d869fe90c28b48e4cecb9926ef9b06d90fb0aec48941 -F src/attach.c 4a3138bd771d5426ae4344d8d5e900440af29fabc5ec2f39f69a45010dfbccd7 +F src/analyze.c 1250e69bd137314845afec5c489fc49c9de7baef68970b5530a7bc28f7611db1 +F src/attach.c 3af6abc40733d10014b401c89a4e8ecfa7c3855517c62004461875220a3af453 F src/auth.c 6277d63837357549fe14e723490d6dc1a38768d71c795c5eb5c0f8a99f918f73 F src/backup.c faf17e60b43233c214aae6a8179d24503a61e83b F src/bitvec.c 17ea48eff8ba979f1f5b04cc484c7bb2be632f33 @@ -445,10 +445,10 @@ F src/date.c ebe1dc7c8a347117bb02570f1a931c62dd78f4a2b1b516f4837d45b7d6426957 F src/dbpage.c 8db4c97f630e7d83f884ea75caf1ffd0988c160e9d530194d93721c80821e0f6 F src/dbstat.c edabb82611143727511a45ca0859b8cd037851ebe756ae3db289859dd18b6f91 F src/delete.c b0f90749e22d5e41a12dbf940f4811138cf97da54b46b737089b93eb64a2896f -F src/expr.c af4a81a385277510bfc56df87c25d76fc365f98c33bc8797c4a8d84b88e31013 +F src/expr.c 6e443e4f9fabd3125800076f2a7cd90c84d2c106ed1815cbe5e9c96af2b9eb74 F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007 F src/fkey.c d617daf66b5515e2b42c1405b2b4984c30ca50fb705ab164271a9bf66c69e331 -F src/func.c e2e3c02621a528a472933fd4733a5da635676f1461be73293f6e9f62f18d4eaa +F src/func.c 03c99a50c69f7d565e13179aad26af703b9df7752a4d690af1540c5e04ababc2 F src/global.c 9bf034fd560bdd514715170ed8460bb7f823cec113f0569ef3f18a20c7ccd128 F src/hash.c a12580e143f10301ed5166ea4964ae2853d3905a511d4e0c44497245c7ce1f7a F src/hash.h ab34c5c54a9e9de2e790b24349ba5aab3dbb4fd4 @@ -482,7 +482,7 @@ F src/os_win.c ac29c25cde4cfb4adacc59cdec4aa45698ca0e29164ea127859585ccd9faa354 F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a F src/pager.c 1bb6a57fa0465296a4d6109a1a64610a0e7adde1f3acf3ef539a9d972908ce8f F src/pager.h c571b064df842ec8f2e90855dead9acf4cbe0d1b2c05afe0ef0d0145f7fd0388 -F src/parse.y 07784439d25f0bc64a656eece4caecc549b147d213f513cdbeb8430345ec2911 +F src/parse.y 5df899a48f439c5e67e5194fe06ff28d928d2f531edc3169964f668eb16fee34 F src/pcache.c 135ef0bc6fb2e3b7178d49ab5c9176254c8a691832c1bceb1156b2fbdd0869bd F src/pcache.h 072f94d29281cffd99e46c1539849f248c4b56ae7684c1f36626797fee375170 F src/pcache1.c 716975564c15eb6679e97f734cec1bfd6c16ac3d4010f05f1f8e509fc7d19880 @@ -491,19 +491,19 @@ F src/pragma.h bb83728944b42f6d409c77f5838a8edbdb0fe83046c5496ffc9602b40340a324 F src/prepare.c 95a9dba7a5d032039a77775188cb3b6fb17f2fa1a0b7cd915b30b4b823383ffa F src/printf.c 1d1b4a568a58d0f32a5ff26c6b98db8a6e1883467f343a6406263cacd2e60c21 F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384 -F src/resolve.c 6415381a0e9d22c0e7cba33ca4a53f81474190862f5d4838190f5eb5b0b47bc9 +F src/resolve.c 8feaf2039bd1b17dd5021e0c5731cde741694b59032d0faf5c73df499c880ebf F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac -F src/select.c a35d462ee7a3c0856ad7a9d9c8921fbf3d91d911a8f39ad9d61302eb43b24a71 +F src/select.c 0e82e32d3bd536c90e778c930cdf7dafa5afd886cc8c467c443ff95c38109e10 F src/shell.c.in 53affa90711f280342f7238f9c9aa9dcaed321ec6218a18043cf92154ef8a704 F src/sqlite.h.in 34be2d0d18bf4726538793bdc9854cd87f689fda4b3789515134cdbd68188cf4 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 9887b27e69c01e79c2cbe74ef73bf01af5b5703d6a7f0a4371e386d7249cb1c7 -F src/sqliteInt.h 5abdade4744cf3bd567afb65bb144bb3c61f6132f86813b995a5ca79c7b584e8 +F src/sqliteInt.h ecb9f7d12a22f557d66cafd7a3ea1c2bbfb6773c4b274eb7410ac017c3e18472 F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34 F src/tclsqlite.c 916a92de77ec5cbe27818ca194d8cf0c58aa7ad5b87527098f6aa5a6068800ce -F src/test1.c 51aa5f3030217ca45eb62e90944838794d4faaae7a8f60e0330ae01f30bc997b +F src/test1.c b5e21f2ec8386cabb67346c9399603ddb33f76094a0941f280b403aa93631717 F src/test2.c 3efb99ab7f1fc8d154933e02ae1378bac9637da5 F src/test3.c b8434949dfb8aff8dfa082c8b592109e77844c2135ed3c492113839b6956255b F src/test4.c 18ec393bb4d0ad1de729f0b94da7267270f3d8e6 @@ -563,13 +563,13 @@ F src/upsert.c ae4a4823b45c4daf87e8aea8c0f582a8844763271f5ed54ee5956c4c612734f4 F src/utf.c 810fbfebe12359f10bc2a011520a6e10879ab2a163bcb26c74768eab82ea62a5 F src/util.c d9eb0a6c4aae1b00a7369eadd7ca0bbe946cb4c953b6751aa20d357c2f482157 F src/vacuum.c 37730af7540033135909ecaee3667dddec043293428d8718546d0d64ba4a5025 -F src/vdbe.c 066a4e1de2ed83e253adfd2e97a684cf562eaa41d31ee7f3d3e4c8aea4485a55 +F src/vdbe.c d83cfec9ebf523d5b2a8a3756ba8f23e39723725334a2e2e947e602ef6e6b278 F src/vdbe.h d970d9738efdd09cb2df73e3a40856e7df13e88a3486789c49fcdd322c9eb8a2 -F src/vdbeInt.h 95f7adfdc5c8f1353321f55a6c5ec00a90877e3b85af5159e393afb41ff54110 +F src/vdbeInt.h 3878856fab3a8e64d27d472909e391db9d82f4f8b902a1737a1f7f351299ff52 F src/vdbeapi.c 29d2baf9c1233131ec467d7bed1b7c8a03c27579048d768c4b04acf427838858 F src/vdbeaux.c f1cb5ae6e42c54d4991e2951e5293c1e18bad6847056e9b17622fbf6b17964a9 F src/vdbeblob.c f5c70f973ea3a9e915d1693278a5f890dc78594300cf4d54e64f2b0917c94191 -F src/vdbemem.c 0cbe9b9560e42b72983cf9e1bceba48f297e51142bfb6b57f3747cf60106b92d +F src/vdbemem.c a4b9390323f0ae4972968de8384c64f005fa026e266fa1531dfa9d77bc5861db F src/vdbesort.c 731a09e5cb9e96b70c394c1b7cf3860fbe84acca7682e178615eb941a3a0ef2f F src/vdbetrace.c 79d6dbbc479267b255a7de8080eee6e729928a0ef93ed9b0bfa5618875b48392 F src/vtab.c 0e4885495172e1bdf54b12cce23b395ac74ef5729031f15e1bc1e3e6b360ed1a @@ -581,6 +581,7 @@ F src/where.c 60ec752fcbe9f9e0271ac60548d159a540a1ee47a4f9fedc85e88a3d0e392dd1 F src/whereInt.h cbae2bcd37cfebdb7812a8b188cdb19634ced2b9346470d1c270556b0c33ea53 F src/wherecode.c 728c7f70731430ccdac807a79969873e1af6968bf1c4745dff3f9dd35f636cc8 F src/whereexpr.c e90b2e76dcabc81edff56633bf281bc01d93b71e0c81482dc06925ce39f5844a +F src/window.c 33cc7de721edb12ddae639b96f537535e237edcd576c8d946e4a0b66d912a5db F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd F test/affinity3.test 6a101af2fc945ce2912f6fe54dd646018551710d @@ -1145,7 +1146,7 @@ F test/parser1.test 391b9bf9a229547a129c61ac345ed1a6f5eb1854 F test/pcache.test c8acbedd3b6fd0f9a7ca887a83b11d24a007972b F test/pcache2.test af7f3deb1a819f77a6d0d81534e97d1cf62cd442 F test/percentile.test 4243af26b8f3f4555abe166f723715a1f74c77ff -F test/permutations.test 10793f1de89a226fa22dde9ba9398de22571fee1bfb53a935a11be4aa014704f +F test/permutations.test a4c5c94c5f4e6c49004bf9b9b8b81201081e1d2d127cdda61bdb1c61dd0deb4e F test/pragma.test 7c8cfc328a1717a95663cf8edb06c52ddfeaf97bb0aee69ae7457132e8d39e7d F test/pragma2.test e5d5c176360c321344249354c0c16aec46214c9f F test/pragma3.test 14c12bc5352b1e100e0b6b44f371053a81ccf8ed @@ -1612,6 +1613,7 @@ F test/win32heap.test 10fd891266bd00af68671e702317726375e5407561d859be1aa04696f2 F test/win32lock.test fbf107c91d8f5512be5a5b87c4c42ab9fdd54972 F test/win32longpath.test 169c75a3b2e43481f4a62122510210c67b08f26d F test/win32nolock.test ac4f08811a562e45a5755e661f45ca85892bdbbc +F test/window1.test d1766b0cbaf87521a0245b18da8c907cc0d791b287a66e90c70f8b836985794d F test/with1.test 58475190cd8caaeebea8cfeb2a264ec97a0c492b8ffe9ad20cefbb23df462f96 F test/with2.test e0030e2f0267a910d6c0e4f46f2dfe941c1cc0d4f659ba69b3597728e7e8f1ab F test/with3.test 5e8ce2c585170bbbc0544e2a01a4941fa0be173ba5265e5c92eb588cd99a232d @@ -1656,7 +1658,7 @@ F tool/max-limits.c cbb635fbb37ae4d05f240bfb5b5270bb63c54439 F tool/mkautoconfamal.sh 422fc365358a2e92876ffc62971a0ff28ed472fc8bcf9de0df921c736fdeca5e F tool/mkccode.tcl 86463e68ce9c15d3041610fedd285ce32a5cf7a58fc88b3202b8b76837650dbe x F tool/mkctimec.tcl dd183b73ae1c28249669741c250525f0407e579a70482371668fd5f130d9feb3 -F tool/mkkeywordhash.c 20f366ad3794e1db42e333a6f35fa41a024f2e3528579c9d58eb13eaa3ab4913 +F tool/mkkeywordhash.c dd4d201d646dd4e236b93be17589e89a19b329a8840e559f91db3bdc361f3c39 F tool/mkmsvcmin.tcl cad0c7b54d7dd92bc87d59f36d4cc4f070eb2e625f14159dc2f5c4204e6a13ea F tool/mkopcodec.tcl d1b6362bd3aa80d5520d4d6f3765badf01f6c43c F tool/mkopcodeh.tcl 4ee2a30ccbd900dc4d5cdb61bdab87cd2166cd2affcc78c9cc0b8d22a65b2eee @@ -1728,7 +1730,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P b45b18850c59f22a163ee482f529f578a8798f96d0e26b5a061d336d480a1540 -R c3a19bb61082b137e0cb5588248c4f0e -U drh -Z 26153b062cb510be04ec5eecb2714fde +P ed5b09680fd6659ebbe5ace3c1c56f3962bbd75cfdf65c7565651900cf87917a +R 44bb3242b5e27ab54d0052d5f4fa0103 +T *branch * exp-window-functions +T *sym-exp-window-functions * +T +closed d103c041ccb3a009926b6aa34a283a7cb8e4a645711ecd7a3002a90558d02e9d +T -sym-trunk * +U dan +Z 75ac74ab72bd7e468725d214860f2422 diff --git a/manifest.uuid b/manifest.uuid index c2200e96a..532ea1066 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ed5b09680fd6659ebbe5ace3c1c56f3962bbd75cfdf65c7565651900cf87917a
\ No newline at end of file +3781e520854808fe02ad3fe77dd11fc917448c58ff1fd79123289dd91937decd
\ No newline at end of file diff --git a/src/analyze.c b/src/analyze.c index 48fd4951c..9492e2c2f 100644 --- a/src/analyze.c +++ b/src/analyze.c @@ -485,6 +485,7 @@ static const FuncDef statInitFuncdef = { 0, /* pNext */ statInit, /* xSFunc */ 0, /* xFinalize */ + 0, 0, "stat_init", /* zName */ {0} }; @@ -801,6 +802,7 @@ static const FuncDef statPushFuncdef = { 0, /* pNext */ statPush, /* xSFunc */ 0, /* xFinalize */ + 0, 0, "stat_push", /* zName */ {0} }; @@ -952,6 +954,7 @@ static const FuncDef statGetFuncdef = { 0, /* pNext */ statGet, /* xSFunc */ 0, /* xFinalize */ + 0, 0, "stat_get", /* zName */ {0} }; diff --git a/src/attach.c b/src/attach.c index ca0fd9fd1..1f276156b 100644 --- a/src/attach.c +++ b/src/attach.c @@ -414,6 +414,7 @@ void sqlite3Detach(Parse *pParse, Expr *pDbname){ 0, /* pNext */ detachFunc, /* xSFunc */ 0, /* xFinalize */ + 0, 0, "sqlite_detach", /* zName */ {0} }; @@ -433,6 +434,7 @@ void sqlite3Attach(Parse *pParse, Expr *p, Expr *pDbname, Expr *pKey){ 0, /* pNext */ attachFunc, /* xSFunc */ 0, /* xFinalize */ + 0, 0, "sqlite_attach", /* zName */ {0} }; diff --git a/src/expr.c b/src/expr.c index 6aff83a25..a1407a85f 100644 --- a/src/expr.c +++ b/src/expr.c @@ -1063,6 +1063,9 @@ static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){ }else{ sqlite3ExprListDelete(db, p->x.pList); } + if( !ExprHasProperty(p, EP_Reduced) ){ + sqlite3WindowDelete(db, p->pWin); + } } if( ExprHasProperty(p, EP_MemToken) ) sqlite3DbFree(db, p->u.zToken); if( !ExprHasProperty(p, EP_Static) ){ @@ -1305,6 +1308,24 @@ static With *withDup(sqlite3 *db, With *p){ # define withDup(x,y) 0 #endif +static Window *winDup(sqlite3 *db, Window *p){ + Window *pNew = 0; + if( p ){ + pNew = sqlite3DbMallocZero(db, sizeof(Window)); + if( pNew ){ + pNew->pFilter = sqlite3ExprDup(db, p->pFilter, 0); + pNew->pPartition = sqlite3ExprListDup(db, p->pPartition, 0); + pNew->pOrderBy = sqlite3ExprListDup(db, p->pOrderBy, 0); + pNew->eType = p->eType; + pNew->eEnd = p->eEnd; + pNew->eStart = p->eStart; + pNew->pStart = sqlite3ExprDup(db, pNew->pStart, 0); + pNew->pEnd = sqlite3ExprDup(db, pNew->pEnd, 0); + } + } + return pNew; +} + /* ** The following group of routines make deep copies of expressions, ** expression lists, ID lists, and select statements. The copies can @@ -1469,6 +1490,7 @@ Select *sqlite3SelectDup(sqlite3 *db, Select *pDup, int flags){ pNew->addrOpenEphm[1] = -1; pNew->nSelectRow = p->nSelectRow; pNew->pWith = withDup(db, p->pWith); + pNew->pWin = winDup(db, p->pWin); sqlite3SelectSetName(pNew, p->zSelName); *pp = pNew; pp = &pNew->pPrior; @@ -3778,6 +3800,10 @@ expr_code_doover: u8 enc = ENC(db); /* The text encoding used by this database */ CollSeq *pColl = 0; /* A collating sequence */ + if( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) && pExpr->pWin ){ + return pExpr->pWin->regResult; + } + if( ConstFactorOk(pParse) && sqlite3ExprIsConstantNotJoin(pExpr) ){ /* SQL functions can be expensive. So try to move constant functions ** out of the inner loop, even if that means an extra OP_Copy. */ @@ -3798,7 +3824,7 @@ expr_code_doover: pDef = sqlite3FindFunction(db, "unknown", nFarg, enc, 0); } #endif - if( pDef==0 || pDef->xFinalize!=0 ){ + if( pDef==0 /* || pDef->xFinalize!=0 */ ){ sqlite3ErrorMsg(pParse, "unknown function: %s()", zId); break; } diff --git a/src/func.c b/src/func.c index 17a267e22..f5a839d39 100644 --- a/src/func.c +++ b/src/func.c @@ -1859,7 +1859,7 @@ void sqlite3RegisterBuiltinFunctions(void){ FUNCTION(zeroblob, 1, 0, 0, zeroblobFunc ), FUNCTION(substr, 2, 0, 0, substrFunc ), FUNCTION(substr, 3, 0, 0, substrFunc ), - AGGREGATE(sum, 1, 0, 0, sumStep, sumFinalize ), + WFUNCTION(sum, 1, 0, sumStep, sumFinalize, sumFinalize, 0), AGGREGATE(total, 1, 0, 0, sumStep, totalFinalize ), AGGREGATE(avg, 1, 0, 0, sumStep, avgFinalize ), AGGREGATE2(count, 0, 0, 0, countStep, countFinalize, diff --git a/src/parse.y b/src/parse.y index aca3bfb1c..1d1a2ddaa 100644 --- a/src/parse.y +++ b/src/parse.y @@ -99,6 +99,8 @@ */ struct TrigEvent { int a; IdList * b; }; +struct FrameBound { int eType; Expr *pExpr; }; + /* ** Disable lookaside memory allocation for objects that might be ** shared across database connections. @@ -209,7 +211,7 @@ columnname(A) ::= nm(A) typetoken(Y). {sqlite3AddColumn(pParse,&A,&Y);} CONFLICT DATABASE DEFERRED DESC DETACH DO EACH END EXCLUSIVE EXPLAIN FAIL FOR IGNORE IMMEDIATE INITIALLY INSTEAD LIKE_KW MATCH NO PLAN - QUERY KEY OF OFFSET PRAGMA RAISE RECURSIVE RELEASE REPLACE RESTRICT ROW + QUERY KEY OF OFFSET PRAGMA RAISE RECURSIVE RELEASE REPLACE RESTRICT ROW ROWS ROLLBACK SAVEPOINT TEMP TRIGGER VACUUM VIEW VIRTUAL WITH WITHOUT %ifdef SQLITE_OMIT_COMPOUND_SELECT EXCEPT INTERSECT UNION @@ -1001,11 +1003,12 @@ expr(A) ::= CAST LP expr(E) AS typetoken(T) RP. { sqlite3ExprAttachSubtrees(pParse->db, A, E, 0); } %endif SQLITE_OMIT_CAST -expr(A) ::= id(X) LP distinct(D) exprlist(Y) RP. { +expr(A) ::= id(X) LP distinct(D) exprlist(Y) RP window(Z). { if( Y && Y->nExpr>pParse->db->aLimit[SQLITE_LIMIT_FUNCTION_ARG] ){ sqlite3ErrorMsg(pParse, "too many arguments on function %T", &X); } A = sqlite3ExprFunction(pParse, Y, &X); + sqlite3WindowAttach(pParse, A, Z); if( D==SF_Distinct && A ){ A->flags |= EP_Distinct; } @@ -1017,6 +1020,59 @@ term(A) ::= CTIME_KW(OP). { A = sqlite3ExprFunction(pParse, 0, &OP); } + +%type window {Window*} +%destructor window {sqlite3WindowDelete(pParse->db, $$);} + +%type frame_opt {Window*} +%destructor frame_opt {sqlite3WindowDelete(pParse->db, $$);} + +%type part_opt {ExprList*} +%destructor part_opt {sqlite3ExprListDelete(pParse->db, $$);} + +%type filter_opt {Expr*} +%destructor filter_opt {sqlite3ExprDelete(pParse->db, $$);} + +%type range_or_rows {int} + +%type frame_bound {struct FrameBound} +%destructor frame_bound {sqlite3ExprDelete(pParse->db, $$.pExpr);} + +window(A) ::= . { A = 0; } +window(A) ::= filter_opt(W) OVER LP part_opt(X) orderby_opt(Y) frame_opt(Z) RP.{ + if( Z ){ + A = Z; + A->pFilter = W; + A->pPartition = X; + A->pOrderBy = Y; + } +} + +part_opt(A) ::= PARTITION BY exprlist(X). { A = X; } +part_opt(A) ::= . { A = 0; } +filter_opt(A) ::= . { A = 0; } +filter_opt(A) ::= FILTER LP WHERE expr(X) RP. { A = X; } + +frame_opt(A) ::= . { + A = sqlite3WindowAlloc(pParse, TK_RANGE, TK_UNBOUNDED, 0, TK_CURRENT, 0); +} +frame_opt(A) ::= range_or_rows(X) frame_bound(Y). { + A = sqlite3WindowAlloc(pParse, X, Y.eType, Y.pExpr, TK_CURRENT, 0); +} +frame_opt(A) ::= range_or_rows(X) BETWEEN frame_bound(Y) AND frame_bound(Z). { + A = sqlite3WindowAlloc(pParse, X, Y.eType, Y.pExpr, Z.eType, Z.pExpr); +} + +range_or_rows(A) ::= RANGE. { A = TK_RANGE; } +range_or_rows(A) ::= ROWS. { A = TK_ROWS; } + +frame_bound(A) ::= UNBOUNDED PRECEDING. { A.eType = TK_UNBOUNDED; A.pExpr = 0; } +frame_bound(A) ::= expr(X) PRECEDING. { A.eType = TK_PRECEDING; A.pExpr = X; } +frame_bound(A) ::= CURRENT ROW. { A.eType = TK_CURRENT ; A.pExpr = 0; } +frame_bound(A) ::= expr(X) FOLLOWING. { A.eType = TK_FOLLOWING; A.pExpr = X; } +frame_bound(A) ::= UNBOUNDED FOLLOWING. { A.eType = TK_UNBOUNDED; A.pExpr = 0; } + + expr(A) ::= LP nexprlist(X) COMMA expr(Y) RP. { ExprList *pList = sqlite3ExprListAppend(pParse, X, Y); A = sqlite3PExpr(pParse, TK_VECTOR, 0, 0); diff --git a/src/resolve.c b/src/resolve.c index 4ed36a479..073fdf193 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -753,8 +753,11 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ NC_IdxExpr|NC_PartIdx); } } - if( is_agg && (pNC->ncFlags & NC_AllowAgg)==0 ){ - sqlite3ErrorMsg(pParse, "misuse of aggregate function %.*s()", nId,zId); + if( (is_agg && (pNC->ncFlags & NC_AllowAgg)==0) + || (pExpr->pWin && (pNC->ncFlags & NC_AllowWin)==0) + ){ + const char *zType = pExpr->pWin ? "window" : "aggregate"; + sqlite3ErrorMsg(pParse, "misuse of %s function %.*s()",zType,nId,zId); pNC->nErr++; is_agg = 0; }else if( no_such_func && pParse->db->init.busy==0 @@ -772,19 +775,28 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ if( is_agg ) pNC->ncFlags &= ~NC_AllowAgg; sqlite3WalkExprList(pWalker, pList); if( is_agg ){ - NameContext *pNC2 = pNC; - pExpr->op = TK_AGG_FUNCTION; - pExpr->op2 = 0; - while( pNC2 && !sqlite3FunctionUsesThisSrc(pExpr, pNC2->pSrcList) ){ - pExpr->op2++; - pNC2 = pNC2->pNext; + if( pExpr->pWin ){ + pExpr->pWin->pNextWin = pNC->pWin; + pNC->pWin = pExpr->pWin; + pExpr->pWin->pFunc = pDef; + pExpr->pWin->nArg = pExpr->x.pList->nExpr; } - assert( pDef!=0 ); - if( pNC2 ){ - assert( SQLITE_FUNC_MINMAX==NC_MinMaxAgg ); - testcase( (pDef->funcFlags & SQLITE_FUNC_MINMAX)!=0 ); - pNC2->ncFlags |= NC_HasAgg | (pDef->funcFlags & SQLITE_FUNC_MINMAX); + else + { + NameContext *pNC2 = pNC; + pExpr->op = TK_AGG_FUNCTION; + pExpr->op2 = 0; + while( pNC2 && !sqlite3FunctionUsesThisSrc(pExpr, pNC2->pSrcList) ){ + pExpr->op2++; + pNC2 = pNC2->pNext; + } + assert( pDef!=0 ); + if( pNC2 ){ + assert( SQLITE_FUNC_MINMAX==NC_MinMaxAgg ); + testcase( (pDef->funcFlags & SQLITE_FUNC_MINMAX)!=0 ); + pNC2->ncFlags |= NC_HasAgg | (pDef->funcFlags & SQLITE_FUNC_MINMAX); + } } pNC->ncFlags |= NC_AllowAgg; } @@ -1234,6 +1246,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ nCompound = 0; pLeftmost = p; while( p ){ + assert( p->pWin==0 ); assert( (p->selFlags & SF_Expanded)!=0 ); assert( (p->selFlags & SF_Resolved)==0 ); p->selFlags |= SF_Resolved; @@ -1291,12 +1304,13 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ /* Set up the local name-context to pass to sqlite3ResolveExprNames() to ** resolve the result-set expression list. */ - sNC.ncFlags = NC_AllowAgg; + sNC.ncFlags = NC_AllowAgg|NC_AllowWin; sNC.pSrcList = p->pSrc; sNC.pNext = pOuterNC; /* Resolve names in the result set. */ if( sqlite3ResolveExprListNames(&sNC, p->pEList) ) return WRC_Abort; + sNC.ncFlags &= ~NC_AllowWin; /* If there are no aggregate functions in the result-set, and no GROUP BY ** expression, do not allow aggregates in any of the other expressions. @@ -1345,7 +1359,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ ** outer queries */ sNC.pNext = 0; - sNC.ncFlags |= NC_AllowAgg; + sNC.ncFlags |= NC_AllowAgg|NC_AllowWin; /* If this is a converted compound query, move the ORDER BY clause from ** the sub-query back to the parent query. At this point each term @@ -1376,6 +1390,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ if( db->mallocFailed ){ return WRC_Abort; } + sNC.ncFlags &= ~NC_AllowWin; /* Resolve the GROUP BY clause. At the same time, make sure ** the GROUP BY clause does not contain aggregate functions. @@ -1402,6 +1417,9 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ return WRC_Abort; } + p->pWin = sNC.pWin; + sNC.pWin = 0; + /* Advance to the next term of the compound */ p = p->pPrior; diff --git a/src/select.c b/src/select.c index 3818ef517..2b5a3dc54 100644 --- a/src/select.c +++ b/src/select.c @@ -162,6 +162,7 @@ Select *sqlite3SelectNew( pNew->pNext = 0; pNew->pLimit = pLimit; pNew->pWith = 0; + pNew->pWin = 0; if( pParse->db->mallocFailed ) { clearSelect(pParse->db, pNew, pNew!=&standin); pNew = 0; @@ -3719,6 +3720,8 @@ static int flattenSubquery( pSub = pSubitem->pSelect; assert( pSub!=0 ); + if( p->pWin ) return 0; + pSubSrc = pSub->pSrc; assert( pSubSrc ); /* Prior to version 3.1.2, when LIMIT and OFFSET had to be simple constants, @@ -4588,6 +4591,27 @@ static void selectPopWith(Walker *pWalker, Select *p){ #define selectPopWith 0 #endif +static int selectExpandSubquery(Parse *pParse, struct SrcList_item *pFrom){ + Select *pSel = pFrom->pSelect; + Table *pTab; + + pFrom->pTab = pTab = sqlite3DbMallocZero(pParse->db, sizeof(Table)); + if( pTab==0 ) return WRC_Abort; + pTab->nTabRef = 1; + if( pFrom->zAlias ){ + pTab->zName = sqlite3DbStrDup(pParse->db, pFrom->zAlias); + }else{ + pTab->zName = sqlite3MPrintf(pParse->db, "subquery_%p", (void*)pTab); + } + while( pSel->pPrior ){ pSel = pSel->pPrior; } + sqlite3ColumnsFromExprList(pParse, pSel->pEList,&pTab->nCol,&pTab->aCol); + pTab->iPKey = -1; + pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) ); + pTab->tabFlags |= TF_Ephemeral; + + return WRC_Continue; +} + /* ** This routine is a Walker callback for "expanding" a SELECT statement. ** "Expanding" means to do the following: @@ -4660,6 +4684,8 @@ static int selectExpander(Walker *pWalker, Select *p){ assert( pSel!=0 ); assert( pFrom->pTab==0 ); if( sqlite3WalkSelect(pWalker, pSel) ) return WRC_Abort; + if( selectExpandSubquery(pParse, pFrom) ) return WRC_Abort; +#if 0 pFrom->pTab = pTab = sqlite3DbMallocZero(db, sizeof(Table)); if( pTab==0 ) return WRC_Abort; pTab->nTabRef = 1; @@ -4674,6 +4700,7 @@ static int selectExpander(Walker *pWalker, Select *p){ pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) ); pTab->tabFlags |= TF_Ephemeral; #endif +#endif }else{ /* An ordinary table or view name in the FROM clause */ assert( pFrom->pTab==0 ); @@ -5375,6 +5402,201 @@ static int countOfViewOptimization(Parse *pParse, Select *p){ } #endif /* SQLITE_COUNTOFVIEW_OPTIMIZATION */ +typedef struct WindowRewrite WindowRewrite; +struct WindowRewrite { + Window *pWin; + ExprList *pSub; +}; + +static int selectWindowRewriteSelectCb(Walker *pWalker, Select *pSelect){ + return WRC_Prune; +} + +static int selectWindowRewriteExprCb(Walker *pWalker, Expr *pExpr){ + struct WindowRewrite *p = pWalker->u.pRewrite; + Parse *pParse = pWalker->pParse; + int rc = WRC_Continue; + + switch( pExpr->op ){ + case TK_COLUMN: { + Expr *pDup = sqlite3ExprDup(pParse->db, pExpr, 0); + p->pSub = sqlite3ExprListAppend(pParse, p->pSub, pDup); + if( p->pSub ){ + assert( ExprHasProperty(pExpr, EP_Static)==0 ); + ExprSetProperty(pExpr, EP_Static); + sqlite3ExprDelete(pParse->db, pExpr); + ExprClearProperty(pExpr, EP_Static); + memset(pExpr, 0, sizeof(Expr)); + + pExpr->op = TK_COLUMN; + pExpr->iColumn = p->pSub->nExpr-1; + pExpr->iTable = p->pWin->iEphCsr; + } + + break; + } + + case TK_FUNCTION: + if( pExpr->pWin ){ + rc = WRC_Prune; + pExpr->pWin->pOwner = pExpr; + } + break; + + default: /* no-op */ + break; + } + + return rc; +} + +static int selectWindowRewriteEList( + Parse *pParse, + Window *pWin, + ExprList *pEList, /* Rewrite expressions in this list */ + ExprList **ppSub /* IN/OUT: Sub-select expression-list */ +){ + Walker sWalker; + WindowRewrite sRewrite; + int rc; + + memset(&sWalker, 0, sizeof(Walker)); + memset(&sRewrite, 0, sizeof(WindowRewrite)); + + sRewrite.pSub = *ppSub; + sRewrite.pWin = pWin; + + sWalker.pParse = pParse; + sWalker.xExprCallback = selectWindowRewriteExprCb; + sWalker.xSelectCallback = selectWindowRewriteSelectCb; + sWalker.u.pRewrite = &sRewrite; + + rc = sqlite3WalkExprList(&sWalker, pEList); + + *ppSub = sRewrite.pSub; + return rc; +} + +static ExprList *exprListAppendList( + Parse *pParse, /* Parsing context */ + ExprList *pList, /* List to which to append. Might be NULL */ + ExprList *pAppend /* List of values to append. Might be NULL */ +){ + if( pAppend ){ + int i; + int nInit = pList ? pList->nExpr : 0; + for(i=0; i<pAppend->nExpr; i++){ + Expr *pDup = sqlite3ExprDup(pParse->db, pAppend->a[i].pExpr, 0); + pList = sqlite3ExprListAppend(pParse, pList, pDup); + if( pList ) pList->a[nInit+i].sortOrder = pAppend->a[i].sortOrder; + } + } + return pList; +} + +/* +** If the SELECT statement passed as the second argument does not invoke +** any SQL window functions, this function is a no-op. Otherwise, it +** rewrites the SELECT statement so that window function xStep functions +** are invoked in the correct order. The simplest version of the +** transformation is: +** +** SELECT win(args...) OVER (<list1>) FROM <src> ORDER BY <list2> +** +** to +** +** SELECT win(args...) FROM ( +** SELECT args... FROM <src> ORDER BY <list1> +** ) ORDER BY <list2> +** +** where <src> may contain WHERE, GROUP BY and HAVING clauses, and <list1> +** is the concatenation of the PARTITION BY and ORDER BY clauses in the +** OVER clause. +** +*/ +static int selectWindowRewrite(Parse *pParse, Select *p){ + int rc = SQLITE_OK; + if( p->pWin ){ + Vdbe *v = sqlite3GetVdbe(pParse); + int i; + sqlite3 *db = pParse->db; + Select *pSub = 0; /* The subquery */ + SrcList *pSrc = p->pSrc; + Expr *pWhere = p->pWhere; + ExprList *pGroupBy = p->pGroupBy; + Expr *pHaving = p->pHaving; + ExprList *pSort = 0; + + ExprList *pSublist = 0; /* Expression list for sub-query */ + Window *pWin = p->pWin; + + /* TODO: This is of course temporary requirements */ + assert( pWin->pNextWin==0 ); + + p->pSrc = 0; + p->pWhere = 0; + p->pGroupBy = 0; + p->pHaving = 0; + + pWin->regAccum = ++pParse->nMem; + pWin->regResult = ++pParse->nMem; + + /* Assign a cursor number for the ephemeral table used to buffer rows. + ** The OpenEphemeral instruction is coded later, after it is known how + ** many columns the table will have. */ + pWin->iEphCsr = pParse->nTab++; + + rc = selectWindowRewriteEList(pParse, pWin, p->pEList, &pSublist); + if( rc ) return rc; + rc = selectWindowRewriteEList(pParse, pWin, p->pOrderBy, &pSublist); + if( rc ) return rc; + pWin->nBufferCol = (pSublist ? pSublist->nExpr : 0); + + /* Create the ORDER BY clause for the sub-select. This is the concatenation + ** of the window PARTITION and ORDER BY clauses. Append the same + ** expressions to the sub-select expression list. They are required to + ** figure out where boundaries for partitions and sets of peer rows. */ + pSort = sqlite3ExprListDup(db, pWin->pPartition, 0); + if( pWin->pOrderBy ){ + pSort = exprListAppendList(pParse, pSort, pWin->pOrderBy); + } + pSublist = exprListAppendList(pParse, pSublist, pSort); + + /* Also append the arguments passed to the window function to the + ** sub-select expression list. */ + pWin->iArgCol = (pSublist ? pSublist->nExpr : 0); + pSublist = exprListAppendList(pParse, pSublist, pWin->pOwner->x.pList); + + pSub = sqlite3SelectNew( + pParse, pSublist, pSrc, pWhere, pGroupBy, pHaving, pSort, 0, 0 + ); + p->pSrc = sqlite3SrcListAppend(db, 0, 0, 0); + if( p->pSrc ){ + int iTab; + ExprList *pList = 0; + p->pSrc->a[0].pSelect = pSub; + sqlite3SrcListAssignCursors(pParse, p->pSrc); + if( selectExpandSubquery(pParse, &p->pSrc->a[0]) ){ + rc = SQLITE_NOMEM; + }else{ + pSub->selFlags |= SF_Expanded; + } + } + +#if SELECTTRACE_ENABLED + if( sqlite3SelectTrace & 0x108 ){ + SELECTTRACE(0x104,pParse,p, ("after window rewrite:\n")); + sqlite3TreeViewSelect(0, p, 0); + } +#endif + + sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pWin->iEphCsr, pWin->nBufferCol); + sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regAccum); + } + + return rc; +} + /* ** Generate code for the SELECT statement given in the p argument. ** @@ -5443,7 +5665,6 @@ int sqlite3Select( sqlite3SelectPrep(pParse, p, 0); memset(&sSort, 0, sizeof(sSort)); sSort.pOrderBy = p->pOrderBy; - pTabList = p->pSrc; if( pParse->nErr || db->mallocFailed ){ goto select_end; } @@ -5460,6 +5681,11 @@ int sqlite3Select( generateColumnNames(pParse, p); } + if( (rc = selectWindowRewrite(pParse, p)) ){ + goto select_end; + } + pTabList = p->pSrc; + /* Try to various optimizations (flattening subqueries, and strength ** reduction of join operators) in the FROM clause up into the main query */ @@ -5833,11 +6059,24 @@ int sqlite3Select( } if( !isAgg && pGroupBy==0 ){ + Window *pWin = p->pWin; + int regPart = 0; + /* No aggregate functions and no GROUP BY clause */ u16 wctrlFlags = (sDistinct.isTnct ? WHERE_WANT_DISTINCT : 0); assert( WHERE_USE_LIMIT==SF_FixedLimit ); wctrlFlags |= p->selFlags & SF_FixedLimit; + if( pWin ){ + int nPart = (pWin->pPartition ? pWin->pPartition->nExpr : 0); + nPart += (pWin->pOrderBy ? pWin->pOrderBy->nExpr : 0); + if( nPart ){ + regPart = pParse->nMem+1; + pParse->nMem += nPart; + sqlite3VdbeAddOp3(v, OP_Null, 0, regPart, regPart+nPart-1); + } + } + /* Begin the database scan. */ SELECTTRACE(1,pParse,p,("WhereBegin\n")); pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, sSort.pOrderBy, @@ -5865,15 +6104,117 @@ int sqlite3Select( sqlite3VdbeChangeToNoop(v, sSort.addrSortIndex); } - /* Use the standard inner loop. */ assert( p->pEList==pEList ); - selectInnerLoop(pParse, p, -1, &sSort, &sDistinct, pDest, - sqlite3WhereContinueLabel(pWInfo), - sqlite3WhereBreakLabel(pWInfo)); + if( p->pWin ){ + int k; + int iSubCsr = p->pSrc->a[0].iCursor; + int nSub = p->pSrc->a[0].pTab->nCol; + int reg = pParse->nMem+1; + int regRecord = reg+nSub; + int regRowid = regRecord+1; + int regGosub = regRowid+1; + int addr; + int addrGosub; + + pParse->nMem += nSub + 3; + + /* Martial the row returned by the sub-select into an array of + ** registers. */ + for(k=0; k<nSub; k++){ + sqlite3VdbeAddOp3(v, OP_Column, iSubCsr, k, reg+k); + } - /* End the database scan loop. - */ - sqlite3WhereEnd(pWInfo); + /* Check if this is the start of a new partition or peer group. */ + if( regPart ){ + ExprList *pPart = pWin->pPartition; + int nPart = (pPart ? pPart->nExpr : 0); + ExprList *pOrderBy = pWin->pOrderBy; + int nPeer = (pOrderBy ? pOrderBy->nExpr : 0); + int addrGoto = 0; + int addrJump = 0; + + if( pPart ){ + int regNewPart = reg + pWin->nBufferCol; + KeyInfo *pKeyInfo = keyInfoFromExprList(pParse, pPart, 0, 0); + addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPart, regPart, nPart); + sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO); + addrJump = sqlite3VdbeAddOp3(v, OP_Jump, addr+2, 0, addr+2); + sqlite3VdbeAddOp2(v, OP_AggFinal, pWin->regAccum, pWin->nArg); + sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF); + sqlite3VdbeAddOp2(v, OP_Copy, pWin->regAccum, pWin->regResult); + if( pOrderBy ){ + addrGoto = sqlite3VdbeAddOp0(v, OP_Goto); + } + } + + if( pOrderBy ){ + int regNewPeer = reg + pWin->nBufferCol + nPart; + int regPeer = regPart + nPart; + + KeyInfo *pKeyInfo = keyInfoFromExprList(pParse, pOrderBy, 0, 0); + if( addrJump ) sqlite3VdbeJumpHere(v, addrJump); + addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPeer, regPeer, nPeer); + sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO); + addrJump = sqlite3VdbeAddOp3(v, OP_Jump, addr+2, 0, addr+2); + sqlite3VdbeAddOp3(v, + OP_AggFinal, pWin->regAccum, pWin->nArg, pWin->regResult + ); + sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF); + + if( addrGoto ) sqlite3VdbeJumpHere(v, addrGoto); + } + + addrGosub = sqlite3VdbeAddOp1(v, OP_Gosub, regGosub); + sqlite3VdbeAddOp1(v, OP_ResetSorter, pWin->iEphCsr); + sqlite3VdbeAddOp3(v,OP_Copy,reg+pWin->nBufferCol,regPart,nPart+nPeer-1); + + sqlite3VdbeJumpHere(v, addrJump); + } + + /* Invoke step function for window functions */ + sqlite3VdbeAddOp3(v, OP_AggStep0, 0, reg+pWin->iArgCol, pWin->regAccum); + sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF); + sqlite3VdbeChangeP5(v, (u8)pWin->nArg); + + /* Buffer the current row in the ephemeral table. */ + if( pWin->nBufferCol>0 ){ + sqlite3VdbeAddOp3(v, OP_MakeRecord, reg, pWin->nBufferCol, regRecord); + }else{ + sqlite3VdbeAddOp2(v, OP_Blob, 0, regRecord); + sqlite3VdbeAppendP4(v, (void*)"", 0); + } + sqlite3VdbeAddOp2(v, OP_NewRowid, pWin->iEphCsr, regRowid); + sqlite3VdbeAddOp3(v, OP_Insert, pWin->iEphCsr, regRecord, regRowid); + + /* End the database scan loop. */ + sqlite3WhereEnd(pWInfo); + + sqlite3VdbeAddOp2(v, OP_AggFinal, pWin->regAccum, pWin->nArg); + sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF); + sqlite3VdbeAddOp2(v, OP_Copy, pWin->regAccum, pWin->regResult); + sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, sqlite3VdbeCurrentAddr(v)+2); + + sqlite3VdbeAddOp0(v, OP_Goto); + if( regPart ){ + sqlite3VdbeJumpHere(v, addrGosub); + } + addr = sqlite3VdbeAddOp1(v, OP_Rewind, pWin->iEphCsr); + selectInnerLoop(pParse, p, -1, &sSort, &sDistinct, pDest, addr+1, 0); + sqlite3VdbeAddOp2(v, OP_Next, pWin->iEphCsr, addr+1); + sqlite3VdbeJumpHere(v, addr); + sqlite3VdbeAddOp1(v, OP_Return, regGosub); + sqlite3VdbeJumpHere(v, addr-1); /* OP_Goto jumps here */ + + }else{ + /* Use the standard inner loop. */ + selectInnerLoop(pParse, p, -1, &sSort, &sDistinct, pDest, + sqlite3WhereContinueLabel(pWInfo), + sqlite3WhereBreakLabel(pWInfo)); + + /* End the database scan loop. + */ + sqlite3WhereEnd(pWInfo); + } }else{ /* This case when there exist aggregate functions or a GROUP BY clause ** or both */ diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 7b19e0a98..8a1376fd2 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -1107,6 +1107,7 @@ typedef struct VTable VTable; typedef struct VtabCtx VtabCtx; typedef struct Walker Walker; typedef struct WhereInfo WhereInfo; +typedef struct Window Window; typedef struct With With; /* A VList object records a mapping between parameters/variables/wildcards @@ -1588,6 +1589,8 @@ struct FuncDef { FuncDef *pNext; /* Next function with same name */ void (*xSFunc)(sqlite3_context*,int,sqlite3_value**); /* func or agg-step */ void (*xFinalize)(sqlite3_context*); /* Agg finalizer */ + void (*xValue)(sqlite3_context*); /* Current agg value */ + void (*xInverse)(sqlite3_context*,int,sqlite3_value**); /* inverse agg-step */ const char *zName; /* SQL name of the function. */ union { FuncDef *pHash; /* Next with a different name but the same hash */ @@ -1678,6 +1681,12 @@ struct FuncDestructor { ** are interpreted in the same way as the first 4 parameters to ** FUNCTION(). ** +** WFUNCTION(zName, nArg, iArg, xStep, xFinal, xValue, xInverse) +** Used to create an aggregate function definition implemented by +** the C functions xStep and xFinal. The first four parameters +** are interpreted in the same way as the first 4 parameters to +** FUNCTION(). +** ** LIKEFUNC(zName, nArg, pArg, flags) ** Used to create a scalar function definition of a function zName ** that accepts nArg arguments and is implemented by a call to C @@ -1688,31 +1697,35 @@ struct FuncDestructor { */ #define FUNCTION(zName, nArg, iArg, bNC, xFunc) \ {nArg, SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \ - SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0} } + SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} } #define VFUNCTION(zName, nArg, iArg, bNC, xFunc) \ {nArg, SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \ - SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0} } + SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} } #define DFUNCTION(zName, nArg, iArg, bNC, xFunc) \ {nArg, SQLITE_FUNC_SLOCHNG|SQLITE_UTF8, \ - 0, 0, xFunc, 0, #zName, {0} } + 0, 0, xFunc, 0, 0, 0, #zName, {0} } #define PURE_DATE(zName, nArg, iArg, bNC, xFunc) \ {nArg, SQLITE_FUNC_SLOCHNG|SQLITE_UTF8|SQLITE_FUNC_CONSTANT, \ - (void*)&sqlite3Config, 0, xFunc, 0, #zName, {0} } + (void*)&sqlite3Config, 0, xFunc, 0, 0, 0, #zName, {0} } #define FUNCTION2(zName, nArg, iArg, bNC, xFunc, extraFlags) \ {nArg,SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL)|extraFlags,\ - SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0} } + SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} } #define STR_FUNCTION(zName, nArg, pArg, bNC, xFunc) \ {nArg, SQLITE_FUNC_SLOCHNG|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \ - pArg, 0, xFunc, 0, #zName, } + pArg, 0, xFunc, 0, 0, 0, #zName, } #define LIKEFUNC(zName, nArg, arg, flags) \ {nArg, SQLITE_FUNC_CONSTANT|SQLITE_UTF8|flags, \ - (void *)arg, 0, likeFunc, 0, #zName, {0} } + (void *)arg, 0, likeFunc, 0, 0, 0, #zName, {0} } #define AGGREGATE(zName, nArg, arg, nc, xStep, xFinal) \ {nArg, SQLITE_UTF8|(nc*SQLITE_FUNC_NEEDCOLL), \ - SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,#zName, {0}} + SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,0,0,#zName, {0}} #define AGGREGATE2(zName, nArg, arg, nc, xStep, xFinal, extraFlags) \ {nArg, SQLITE_UTF8|(nc*SQLITE_FUNC_NEEDCOLL)|extraFlags, \ - SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,#zName, {0}} + SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,0,0,#zName, {0}} + +#define WFUNCTION(zName, nArg, arg, xStep, xFinal, xValue, xInverse) \ + {nArg, SQLITE_UTF8, \ + SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,xValue,xInverse,#zName, {0}} /* ** All current savepoints are stored in a linked list starting at @@ -2413,6 +2426,7 @@ struct Expr { AggInfo *pAggInfo; /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */ Table *pTab; /* Table for TK_COLUMN expressions. Can be NULL ** for a column of an index on an expression */ + Window *pWin; /* Window definition for window functions */ }; /* @@ -2699,6 +2713,7 @@ struct NameContext { int nRef; /* Number of names resolved by this context */ int nErr; /* Number of errors encountered while resolving names */ u16 ncFlags; /* Zero or more NC_* flags defined below */ + Window *pWin; /* List of window functions in this context */ }; /* @@ -2721,6 +2736,7 @@ struct NameContext { #define NC_UUpsert 0x0200 /* True if uNC.pUpsert is used */ #define NC_MinMaxAgg 0x1000 /* min/max aggregates seen. See note above */ #define NC_Complex 0x2000 /* True if a function or subquery seen */ +#define NC_AllowWin 0x4000 /* Window functions are allowed here */ /* ** An instance of the following object describes a single ON CONFLICT @@ -2788,6 +2804,7 @@ struct Select { Select *pNext; /* Next select to the left in a compound */ Expr *pLimit; /* LIMIT expression. NULL means not used. */ With *pWith; /* WITH clause attached to this select. Or NULL. */ + Window *pWin; /* List of window functions */ }; /* @@ -3401,6 +3418,7 @@ struct Walker { struct IdxExprTrans *pIdxTrans; /* Convert idxed expr to column */ ExprList *pGroupBy; /* GROUP BY clause */ Select *pSelect; /* HAVING to WHERE clause ctx */ + struct WindowRewrite *pRewrite; /* Window rewrite context */ } u; }; @@ -3451,6 +3469,31 @@ struct TreeView { }; #endif /* SQLITE_DEBUG */ +struct Window { + Expr *pFilter; + ExprList *pPartition; + ExprList *pOrderBy; + u8 eType; /* TK_RANGE or TK_ROWS */ + u8 eStart; /* UNBOUNDED, CURRENT, PRECEDING or FOLLOWING */ + u8 eEnd; /* UNBOUNDED, CURRENT, PRECEDING or FOLLOWING */ + Expr *pStart; /* Expression for "<expr> PRECEDING" */ + Expr *pEnd; /* Expression for "<expr> FOLLOWING" */ + Window *pNextWin; /* Next window function belonging to this SELECT */ + int iEphCsr; /* Temp table used by this window */ + int regAccum; + int regResult; + FuncDef *pFunc; + int nArg; + + Expr *pOwner; /* Expression object this window is attached to */ + int nBufferCol; /* Number of columns in buffer table */ + int iArgCol; /* Offset of first argument for this function */ +}; + +void sqlite3WindowDelete(sqlite3*, Window*); +Window *sqlite3WindowAlloc(Parse*, int, int, Expr*, int , Expr*); +void sqlite3WindowAttach(Parse*, Expr*, Window*); + /* ** Assuming zIn points to the first byte of a UTF-8 character, ** advance zIn to point to the first byte of the next UTF-8 character. diff --git a/src/test1.c b/src/test1.c index f2511d259..b62c3104f 100644 --- a/src/test1.c +++ b/src/test1.c @@ -7799,6 +7799,9 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ extern int sqlite3_fts3_enable_parentheses; #endif #endif +#if defined(SQLITE_ENABLE_SELECTTRACE) + extern int sqlite3SelectTrace; +#endif for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){ Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0); @@ -7884,6 +7887,10 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ (char*)&sqlite3_sync_count, TCL_LINK_INT); Tcl_LinkVar(interp, "sqlite_fullsync_count", (char*)&sqlite3_fullsync_count, TCL_LINK_INT); +#if defined(SQLITE_ENABLE_SELECTTRACE) + Tcl_LinkVar(interp, "sqlite3SelectTrace", + (char*)&sqlite3SelectTrace, TCL_LINK_INT); +#endif #if defined(SQLITE_ENABLE_FTS3) && defined(SQLITE_TEST) Tcl_LinkVar(interp, "sqlite_fts3_enable_parentheses", (char*)&sqlite3_fts3_enable_parentheses, TCL_LINK_INT); diff --git a/src/vdbe.c b/src/vdbe.c index 70537ce11..7ab104455 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -6313,7 +6313,11 @@ case OP_AggFinal: { assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) ); pMem = &aMem[pOp->p1]; assert( (pMem->flags & ~(MEM_Null|MEM_Agg))==0 ); - rc = sqlite3VdbeMemFinalize(pMem, pOp->p4.pFunc); + if( pOp->p3 ){ + rc = sqlite3VdbeMemAggValue(pMem, &aMem[pOp->p3], pOp->p4.pFunc); + }else{ + rc = sqlite3VdbeMemFinalize(pMem, pOp->p4.pFunc); + } if( rc ){ sqlite3VdbeError(p, "%s", sqlite3_value_text(pMem)); goto abort_due_to_error; diff --git a/src/vdbeInt.h b/src/vdbeInt.h index 44f901abf..24d6bf91d 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -494,6 +494,7 @@ void sqlite3VdbeMemCast(Mem*,u8,u8); int sqlite3VdbeMemFromBtree(BtCursor*,u32,u32,Mem*); void sqlite3VdbeMemRelease(Mem *p); int sqlite3VdbeMemFinalize(Mem*, FuncDef*); +int sqlite3VdbeMemAggValue(Mem*, Mem*, FuncDef*); const char *sqlite3OpcodeName(int); int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve); int sqlite3VdbeMemClearAndResize(Mem *pMem, int n); diff --git a/src/vdbemem.c b/src/vdbemem.c index d118d2bb9..a64ed0963 100644 --- a/src/vdbemem.c +++ b/src/vdbemem.c @@ -415,6 +415,23 @@ int sqlite3VdbeMemFinalize(Mem *pMem, FuncDef *pFunc){ return ctx.isError; } +int sqlite3VdbeMemAggValue(Mem *pAccum, Mem *pOut, FuncDef *pFunc){ + sqlite3_context ctx; + Mem t; + assert( pFunc!=0 ); + assert( pFunc->xValue!=0 ); + assert( (pAccum->flags & MEM_Null)!=0 || pFunc==pAccum->u.pDef ); + assert( pAccum->db==0 || sqlite3_mutex_held(pAccum->db->mutex) ); + memset(&ctx, 0, sizeof(ctx)); + memset(&t, 0, sizeof(t)); + t.flags = MEM_Null; + t.db = pAccum->db; + ctx.pOut = pOut; + ctx.pMem = pAccum; + ctx.pFunc = pFunc; + pFunc->xValue(&ctx); + return ctx.isError; +} /* ** If the memory cell contains a value that must be freed by ** invoking the external callback in Mem.xDel, then this routine diff --git a/src/window.c b/src/window.c new file mode 100644 index 000000000..57d467424 --- /dev/null +++ b/src/window.c @@ -0,0 +1,53 @@ +/* +** +** 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. +** +************************************************************************* +*/ +#include "sqliteInt.h" + +void sqlite3WindowDelete(sqlite3 *db, Window *p){ + if( p ){ + sqlite3ExprDelete(db, p->pFilter); + sqlite3ExprListDelete(db, p->pPartition); + sqlite3ExprListDelete(db, p->pOrderBy); + sqlite3ExprDelete(db, p->pEnd); + sqlite3ExprDelete(db, p->pStart); + sqlite3DbFree(db, p); + } +} + +Window *sqlite3WindowAlloc( + Parse *pParse, + int eType, + int eEnd, Expr *pEnd, + int eStart, Expr *pStart +){ + Window *pWin = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); + + if( pWin ){ + pWin->eType = eType; + pWin->eStart = eStart; + pWin->eEnd = eEnd; + pWin->pEnd = pEnd; + pWin->pStart = pStart; + }else{ + sqlite3ExprDelete(pParse->db, pEnd); + sqlite3ExprDelete(pParse->db, pStart); + } + + return pWin; +} + +void sqlite3WindowAttach(Parse *pParse, Expr *p, Window *pWin){ + if( p ){ + p->pWin = pWin; + }else{ + sqlite3WindowDelete(pParse->db, pWin); + } +} diff --git a/test/permutations.test b/test/permutations.test index 52e2509fc..d240b34da 100644 --- a/test/permutations.test +++ b/test/permutations.test @@ -167,7 +167,7 @@ test_suite "veryquick" -prefix "" -description { that test malloc and IO errors are omitted. } -files [ test_set $allquicktests -exclude *malloc* *ioerr* *fault* *bigfile* *_err* \ - *fts5corrupt* *fts5big* *fts5aj* + *fts5corrupt* *fts5big* *fts5aj* *expert* table.test ] test_suite "extraquick" -prefix "" -description { diff --git a/test/window1.test b/test/window1.test new file mode 100644 index 000000000..70961f8c6 --- /dev/null +++ b/test/window1.test @@ -0,0 +1,112 @@ +# 2018 May 8 +# +# 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. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix window1 + + +do_execsql_test 1.0 { + CREATE TABLE t1(a, b, c, d); + INSERT INTO t1 VALUES(1, 2, 3, 4); + INSERT INTO t1 VALUES(5, 6, 7, 8); + INSERT INTO t1 VALUES(9, 10, 11, 12); +} + +do_execsql_test 1.1 { + SELECT sum(b) OVER () FROM t1 +} {18 18 18} + +do_execsql_test 1.2 { + SELECT a, sum(b) OVER () FROM t1 +} {1 18 5 18 9 18} + +do_execsql_test 1.3 { + SELECT a, 4 + sum(b) OVER () FROM t1 +} {1 22 5 22 9 22} + +do_execsql_test 1.4 { + SELECT a + 4 + sum(b) OVER () FROM t1 +} {23 27 31} + +do_execsql_test 1.5 { + SELECT a, sum(b) OVER (PARTITION BY c) FROM t1 +} {1 2 5 6 9 10} + +foreach {tn sql} { + 1 "SELECT sum(b) OVER () FROM t1" + 2 "SELECT sum(b) OVER (PARTITION BY c) FROM t1" + 3 "SELECT sum(b) OVER (ORDER BY c) FROM t1" + 4 "SELECT sum(b) OVER (PARTITION BY d ORDER BY c) FROM t1" + 5 "SELECT sum(b) FILTER (WHERE a>0) OVER (PARTITION BY d ORDER BY c) FROM t1" + 6 "SELECT sum(b) OVER (ORDER BY c RANGE UNBOUNDED PRECEDING) FROM t1" + 7 "SELECT sum(b) OVER (ORDER BY c ROWS 45 PRECEDING) FROM t1" + 8 "SELECT sum(b) OVER (ORDER BY c RANGE CURRENT ROW) FROM t1" + 9 "SELECT sum(b) OVER (ORDER BY c RANGE BETWEEN UNBOUNDED PRECEDING + AND CURRENT ROW) FROM t1" + 10 "SELECT sum(b) OVER (ORDER BY c ROWS BETWEEN UNBOUNDED PRECEDING + AND UNBOUNDED FOLLOWING) FROM t1" +} { + do_test 2.$tn { lindex [catchsql $sql] 0 } 0 +} + +foreach {tn sql} { + 1 "SELECT * FROM t1 WHERE sum(b) OVER ()" + 2 "SELECT * FROM t1 GROUP BY sum(b) OVER ()" + 3 "SELECT * FROM t1 GROUP BY a HAVING sum(b) OVER ()" +} { + do_catchsql_test 3.$tn $sql {1 {misuse of window function sum()}} +} + +do_execsql_test 4.0 { + CREATE TABLE t2(a, b, c); + INSERT INTO t2 VALUES(0, 0, 0); + INSERT INTO t2 VALUES(1, 1, 1); + INSERT INTO t2 VALUES(2, 0, 2); + INSERT INTO t2 VALUES(3, 1, 0); + INSERT INTO t2 VALUES(4, 0, 1); + INSERT INTO t2 VALUES(5, 1, 2); + INSERT INTO t2 VALUES(6, 0, 0); +} + +do_execsql_test 4.1 { + SELECT a, sum(a) OVER (PARTITION BY b) FROM t2; +} { + 0 12 2 12 4 12 6 12 1 9 3 9 5 9 +} + +do_execsql_test 4.2 { + SELECT a, sum(a) OVER (PARTITION BY b) FROM t2 ORDER BY a; +} { + 0 12 1 9 2 12 3 9 4 12 5 9 6 12 +} + +do_execsql_test 4.3 { + SELECT a, sum(a) OVER () FROM t2 ORDER BY a; +} { + 0 21 1 21 2 21 3 21 4 21 5 21 6 21 +} + +do_execsql_test 4.4 { + SELECT a, sum(a) OVER (ORDER BY a) FROM t2; +} { + 0 0 1 1 2 3 3 6 4 10 5 15 6 21 +} + +do_execsql_test 4.5 { + SELECT a, sum(a) OVER (PARTITION BY b ORDER BY a) FROM t2 ORDER BY a +} { + 0 0 1 1 2 2 3 4 4 6 5 9 6 12 +} + +finish_test diff --git a/tool/mkkeywordhash.c b/tool/mkkeywordhash.c index ec85131b6..75e691894 100644 --- a/tool/mkkeywordhash.c +++ b/tool/mkkeywordhash.c @@ -180,6 +180,7 @@ static Keyword aKeywordTable[] = { { "CONSTRAINT", "TK_CONSTRAINT", ALWAYS }, { "CREATE", "TK_CREATE", ALWAYS }, { "CROSS", "TK_JOIN_KW", ALWAYS }, + { "CURRENT", "TK_CURRENT", ALWAYS }, { "CURRENT_DATE", "TK_CTIME_KW", ALWAYS }, { "CURRENT_TIME", "TK_CTIME_KW", ALWAYS }, { "CURRENT_TIMESTAMP","TK_CTIME_KW", ALWAYS }, @@ -202,6 +203,8 @@ static Keyword aKeywordTable[] = { { "EXISTS", "TK_EXISTS", ALWAYS }, { "EXPLAIN", "TK_EXPLAIN", EXPLAIN }, { "FAIL", "TK_FAIL", CONFLICT|TRIGGER }, + { "FILTER", "TK_FILTER", ALWAYS }, + { "FOLLOWING", "TK_FOLLOWING", ALWAYS }, { "FOR", "TK_FOR", TRIGGER }, { "FOREIGN", "TK_FOREIGN", FKEY }, { "FROM", "TK_FROM", ALWAYS }, @@ -241,11 +244,15 @@ static Keyword aKeywordTable[] = { { "OR", "TK_OR", ALWAYS }, { "ORDER", "TK_ORDER", ALWAYS }, { "OUTER", "TK_JOIN_KW", ALWAYS }, + { "OVER", "TK_OVER", ALWAYS }, + { "PARTITION", "TK_PARTITION", ALWAYS }, { "PLAN", "TK_PLAN", EXPLAIN }, { "PRAGMA", "TK_PRAGMA", PRAGMA }, + { "PRECEDING", "TK_PRECEDING", ALWAYS }, { "PRIMARY", "TK_PRIMARY", ALWAYS }, { "QUERY", "TK_QUERY", EXPLAIN }, { "RAISE", "TK_RAISE", TRIGGER }, + { "RANGE", "TK_RANGE", ALWAYS }, { "RECURSIVE", "TK_RECURSIVE", CTE }, { "REFERENCES", "TK_REFERENCES", FKEY }, { "REGEXP", "TK_LIKE_KW", ALWAYS }, @@ -257,6 +264,7 @@ static Keyword aKeywordTable[] = { { "RIGHT", "TK_JOIN_KW", ALWAYS }, { "ROLLBACK", "TK_ROLLBACK", ALWAYS }, { "ROW", "TK_ROW", TRIGGER }, + { "ROWS", "TK_ROWS", ALWAYS }, { "SAVEPOINT", "TK_SAVEPOINT", ALWAYS }, { "SELECT", "TK_SELECT", ALWAYS }, { "SET", "TK_SET", ALWAYS }, @@ -267,6 +275,7 @@ static Keyword aKeywordTable[] = { { "TO", "TK_TO", ALWAYS }, { "TRANSACTION", "TK_TRANSACTION", ALWAYS }, { "TRIGGER", "TK_TRIGGER", TRIGGER }, + { "UNBOUNDED", "TK_UNBOUNDED", ALWAYS }, { "UNION", "TK_UNION", COMPOUND }, { "UNIQUE", "TK_UNIQUE", ALWAYS }, { "UPDATE", "TK_UPDATE", ALWAYS }, |