Start of experimental implementation of SQL window functions. Does not yet
work. FossilOrigin-Name: 3781e520854808fe02ad3fe77dd11fc917448c58ff1fd79123289dd91937decd
This commit is contained in:
parent
f80bba9d8d
commit
86fb6e1738
2
main.mk
2
main.mk
@ -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
|
||||
|
||||
|
48
manifest
48
manifest
@ -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
|
||||
|
@ -1 +1 @@
|
||||
ed5b09680fd6659ebbe5ace3c1c56f3962bbd75cfdf65c7565651900cf87917a
|
||||
3781e520854808fe02ad3fe77dd11fc917448c58ff1fd79123289dd91937decd
|
@ -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}
|
||||
};
|
||||
|
@ -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}
|
||||
};
|
||||
|
28
src/expr.c
28
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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
60
src/parse.y
60
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);
|
||||
|
@ -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;
|
||||
|
357
src/select.c
357
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;
|
||||
@ -4673,6 +4699,7 @@ static int selectExpander(Walker *pWalker, Select *p){
|
||||
pTab->iPKey = -1;
|
||||
pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
|
||||
pTab->tabFlags |= TF_Ephemeral;
|
||||
#endif
|
||||
#endif
|
||||
}else{
|
||||
/* An ordinary table or view name in the FROM clause */
|
||||
@ -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;
|
||||
|
||||
/* End the database scan loop.
|
||||
*/
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
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);
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
|
@ -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.
|
||||
|
@ -7798,6 +7798,9 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
#ifdef SQLITE_ENABLE_FTS3
|
||||
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++){
|
||||
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
53
src/window.c
Normal file
53
src/window.c
Normal file
@ -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);
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
112
test/window1.test
Normal file
112
test/window1.test
Normal file
@ -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
|
@ -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 },
|
||||
|
Loading…
x
Reference in New Issue
Block a user