diff options
author | dan <dan@noemail.net> | 2018-06-18 16:55:22 +0000 |
---|---|---|
committer | dan <dan@noemail.net> | 2018-06-18 16:55:22 +0000 |
commit | 660af939b00f8a8d44d67f0dce5824ee07750d45 (patch) | |
tree | 152df312acc2b27d654af1e1f7553cb68650d7a9 | |
parent | 13b08bb6ff72ae078401150f68bb379a5525ecd0 (diff) | |
download | sqlite-660af939b00f8a8d44d67f0dce5824ee07750d45.tar.gz sqlite-660af939b00f8a8d44d67f0dce5824ee07750d45.zip |
Add new API function sqlite3_create_window_function(), for creating new
aggregate window functions.
FossilOrigin-Name: da03fb4318fd2613ec5c5b109a3974ac1120c19ed16bed4ca85bbdc4b35c998c
-rw-r--r-- | Makefile.in | 1 | ||||
-rw-r--r-- | Makefile.msc | 1 | ||||
-rw-r--r-- | main.mk | 1 | ||||
-rw-r--r-- | manifest | 34 | ||||
-rw-r--r-- | manifest.uuid | 2 | ||||
-rw-r--r-- | src/func.c | 6 | ||||
-rw-r--r-- | src/main.c | 90 | ||||
-rw-r--r-- | src/sqlite.h.in | 12 | ||||
-rw-r--r-- | src/sqliteInt.h | 5 | ||||
-rw-r--r-- | src/test_tclsh.c | 2 | ||||
-rw-r--r-- | src/test_window.c | 194 | ||||
-rw-r--r-- | src/vdbe.c | 1 | ||||
-rw-r--r-- | src/window.c | 19 | ||||
-rw-r--r-- | test/window1.test | 7 | ||||
-rw-r--r-- | test/window5.test | 68 |
15 files changed, 381 insertions, 62 deletions
diff --git a/Makefile.in b/Makefile.in index 1757a049a..8679a6417 100644 --- a/Makefile.in +++ b/Makefile.in @@ -419,6 +419,7 @@ TESTSRC = \ $(TOP)/src/test_thread.c \ $(TOP)/src/test_vfs.c \ $(TOP)/src/test_windirent.c \ + $(TOP)/src/test_window.c \ $(TOP)/src/test_wsd.c \ $(TOP)/ext/fts3/fts3_term.c \ $(TOP)/ext/fts3/fts3_test.c \ diff --git a/Makefile.msc b/Makefile.msc index d31c117f5..e423631fe 100644 --- a/Makefile.msc +++ b/Makefile.msc @@ -1479,6 +1479,7 @@ TESTSRC = \ $(TOP)\src\test_thread.c \ $(TOP)\src\test_vfs.c \ $(TOP)\src\test_windirent.c \ + $(TOP)\src\test_window.c \ $(TOP)\src\test_wsd.c \ $(TOP)\ext\fts3\fts3_term.c \ $(TOP)\ext\fts3\fts3_test.c \ @@ -349,6 +349,7 @@ TESTSRC = \ $(TOP)/src/test_thread.c \ $(TOP)/src/test_vfs.c \ $(TOP)/src/test_windirent.c \ + $(TOP)/src/test_window.c \ $(TOP)/src/test_wsd.c # Extensions to be statically loaded. @@ -1,10 +1,10 @@ -C Add\sextra\sOOM\stest. -D 2018-06-15T20:46:12.018 +C Add\snew\sAPI\sfunction\ssqlite3_create_window_function(),\sfor\screating\snew\naggregate\swindow\sfunctions. +D 2018-06-18T16:55:22.801 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea -F Makefile.in 498b77b89a8cb42f2ee20fcd6317f279a45c0d6ff40d27825f94b69884c09bbe +F Makefile.in 0a3a6c81e6fcb969ff9106e882f0a08547014ba463cb6beca4c4efaecc924ee6 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 -F Makefile.msc 0221c154ad20065906973f8fd4047346b995d31eaafd461383edca766f4282b6 +F Makefile.msc c1646e8f86c30ea63e56176deacef192ac87c663ce2c9083f459c45a7268934f F README.md 7764d56778d567913ef11c82da9ab94aefa0826f7c243351e4e2d7adaef6f373 F VERSION d3e3afdec1165a5e593dcdfffd8e0f33a2b0186067eb51a073ef6c4aec34923d F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50 @@ -417,7 +417,7 @@ F ext/userauth/userauth.c 3410be31283abba70255d71fd24734e017a4497f F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 -F main.mk 9cad63ffa8c6b782d35debe0ef933312a0ddc75fed35492c7fe29dbe9701647a +F main.mk d9872d31efa4a02e177f6d43b7fdae2a5f822e50d1eb72907f6575a567b85378 F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83 F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271 F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504 @@ -450,7 +450,7 @@ F src/delete.c 4c8c7604277a2041647f96b78f4b9a47858e9217e4fb333d35e7b5ab32c5b57f F src/expr.c 80c61121f3c87427e8c79a6ed0352e610e5a734508c2a094a6bf30ebca18ef1e F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007 F src/fkey.c b1da9ef8dc834603bb0d28972378a7ce65897847f9a1e89ab800bbdf24c788ee -F src/func.c a5ee3864264edea8fea4d2dfdf8296250cff9139343953da78d82837241659a9 +F src/func.c f1c244ba44950d94d4c2298903d16ca7ae3183bcf07936a9e01ab4f3f10b53e2 F src/global.c 9bf034fd560bdd514715170ed8460bb7f823cec113f0569ef3f18a20c7ccd128 F src/hash.c a12580e143f10301ed5166ea4964ae2853d3905a511d4e0c44497245c7ce1f7a F src/hash.h ab34c5c54a9e9de2e790b24349ba5aab3dbb4fd4 @@ -459,7 +459,7 @@ F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c bcacf6a0d277f8fa0e4f9ffecda544a2330ca4478f904cd89564c2dd86d0b16b F src/legacy.c 134ab3e3fae00a0f67a5187981d6935b24b337bcf0f4b3e5c9fa5763da95bf4e F src/loadext.c 6aae5739198d96c51ae6eb97c4a5b1744c22ed7a5a565a5399a717780d48a36b -F src/main.c a086ab7d6e4e3f07bd5789d16f977d425f9482e7b3baeeb2f17bde0e6bfb2bc1 +F src/main.c f6f4f8be5baa6e0d44b0249c49b251f8749156896039cc7c64c3afaa25a64eaf F src/malloc.c 07295435093ce354c6d9063ac05a2eeae28bd251d2e63c48b3d67c12c76f7e18 F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c c12a42539b1ba105e3707d0e628ad70e611040d8f5e38cf942cee30c867083de @@ -497,10 +497,10 @@ F src/resolve.c a8cf3d6144f6a821f002dad72f80659691e827a96e6da6dedf8b263edefe3a80 F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac F src/select.c 7e8e439bf8bf732860566ccceebd57d934bf1aceca213c394d825dde60473f8e F src/shell.c.in 8578421c5fb2a972461b2a996f7173646e55e0dbd2a2eee30c8f5dc7d3dbadfd -F src/sqlite.h.in 19de593baa0667854730e7b8bc2e3039c20ee80a4d537e9b5ec2038947fe3daf +F src/sqlite.h.in 8dbfe617b70b01e661a9ba0b805facb1430df80096ea7508cf7903878b45e689 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 9887b27e69c01e79c2cbe74ef73bf01af5b5703d6a7f0a4371e386d7249cb1c7 -F src/sqliteInt.h 97525ef265cfca3cf39c87b73dd1e39f9260ee2f25fb0cee64bbbe26eb9f3888 +F src/sqliteInt.h 94c8db9e2bfc0f9bead6c3c4f8c6172c93274b13b6388a840b1820590f7a62b0 F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34 @@ -548,13 +548,14 @@ F src/test_server.c a2615049954cbb9cfb4a62e18e2f0616e4dc38fe F src/test_sqllog.c 11e6ce7575f489155c604ac4b439f2ac1d3d5aef F src/test_superlock.c 4839644b9201da822f181c5bc406c0b2385f672e F src/test_syscall.c 1073306ba2e9bfc886771871a13d3de281ed3939 -F src/test_tclsh.c 58052fe48efe8f579834f4648d239569f2efc6285f5019ebdf0040f58d16238d +F src/test_tclsh.c 06317648b0d85a85fd823f7973b55535c59a3156c1ef59394fe511f932cfa78d F src/test_tclvar.c 33ff42149494a39c5fbb0df3d25d6fafb2f668888e41c0688d07273dcb268dfc F src/test_thread.c 911d15fb14e19c0c542bdc8aabf981c2f10a4858 F src/test_vfs.c f0186261a24de2671d080bcd8050732f0cb64f6e F src/test_vfstrace.c bab9594adc976cbe696ff3970728830b4c5ed698 F src/test_windirent.c a895e2c068a06644eef91a7f0a32182445a893b9a0f33d0cdb4283dca2486ac1 F src/test_windirent.h 90dfbe95442c9762357fe128dc7ae3dc199d006de93eb33ba3972e0a90484215 +F src/test_window.c 460361d710643823e54567073b780634d85b2fe54937d49de06c562d39e6cfb1 F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9 F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c F src/tokenize.c bbde32eac9eb1280f5292bcdfef66f5a57e43176cbf9347e0efab9f75e133f97 @@ -565,7 +566,7 @@ F src/upsert.c 47edd408cc73f8d3c00a140550d1ad180b407c146285947969dd09874802bf88 F src/utf.c 810fbfebe12359f10bc2a011520a6e10879ab2a163bcb26c74768eab82ea62a5 F src/util.c d9eb0a6c4aae1b00a7369eadd7ca0bbe946cb4c953b6751aa20d357c2f482157 F src/vacuum.c 36e7d21a20c0bf6ef4ef7c399d192b5239410b7c4d3c1070fba4e30810d0b855 -F src/vdbe.c 55bc870dcab52f7eac5a84d84e13e68122308997975d066f450a42c24d80df32 +F src/vdbe.c 16385bc816c97cc28c12ffa2151b8dc80617943b2e5871e7f0c9fdb4392c2c18 F src/vdbe.h 9c8c245fa3785266c269ab02c135c836ff49a307612186686bcdae500f409945 F src/vdbeInt.h d99f1c3da17b4ed271efc2f52898dd9a577dee077da47c2a014bc128f3cdba2a F src/vdbeapi.c af4a3de00d1851bcbc55b85dfbe52849aa2b1e17b4a5a1f3d9c257df7af361ff @@ -583,7 +584,7 @@ F src/where.c 0bcbf9e191ca07f9ea2008aa80e70ded46bcdffd26560c83397da501f00aece6 F src/whereInt.h b90ef9b9707ef750eab2a7a080c48fb4900315033274689def32d0cf5a81ebe4 F src/wherecode.c 3317f2b083a66d3e65a03edf316ade4ccb0a99c9956273282ebb579b95d4ba96 F src/whereexpr.c 19cf35cdd9bf6d5589d8a5c960d99259761136187a2319a6e14d11cf1abe14c2 -F src/window.c d80ec071618365ed740495848c1ea05a674bf83c498acff10b3ab7a4209a37cc +F src/window.c 7cd40b85402c84d89dfbbcc06700e4879673583ac33b487f172974a2c4cb6d41 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd F test/affinity3.test 6a101af2fc945ce2912f6fe54dd646018551710d @@ -1617,13 +1618,14 @@ F test/win32heap.test 10fd891266bd00af68671e702317726375e5407561d859be1aa04696f2 F test/win32lock.test fbf107c91d8f5512be5a5b87c4c42ab9fdd54972 F test/win32longpath.test 169c75a3b2e43481f4a62122510210c67b08f26d F test/win32nolock.test ac4f08811a562e45a5755e661f45ca85892bdbbc -F test/window1.test 3398c54113aedf04f4bf63ec22e8f30f87f6d56caa5e6313df0f7f1ee6d927e1 +F test/window1.test af17e84722f8a8d525912056c9d6c7ee215d5474d9a9ecd729b761279a3f924f F test/window2.tcl 0983de5eade5eeda49469244799d5331bfe3199fca3f6c6d2a836aa08f4fba1b F test/window2.test 79747b2edde4ad424e0752b27529aedc86e91f3d8d88846fa17ff0cb67f65086 F test/window3.tcl 654d61d73e10db089b22514d498bb23ec310f720c0f4b5f69f67fda83d672048 F test/window3.test 41727668ee31d2ba50f78efcb5bf1bda2c5cffd889aa65243511004669d1ac25 F test/window4.tcl ce0c14185ba651de53994df8ac11da472b6bbd3534e148ad3ce87de6aa0426ed F test/window4.test 13b8cac12e78017d6c1873742efcb120f3d5b2debfdb412271bfb84969087037 +F test/window5.test c912f9dbcc92889fceef1d204077509c4839b5b34d1acf369f31f31827bed2c9 F test/windowfault.test 7d3655fcac44c903b1aa31d40e13d170c71b089551f0e6ed17b02f66fb731fb6 F test/with1.test 58475190cd8caaeebea8cfeb2a264ec97a0c492b8ffe9ad20cefbb23df462f96 F test/with2.test e0030e2f0267a910d6c0e4f46f2dfe941c1cc0d4f659ba69b3597728e7e8f1ab @@ -1741,7 +1743,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P fadd4dc119d8df0d871f4d839b7a11070e2ffb8927e84b3e7a94f34196db3de3 -R ac4dec8988017d1c9bddc25e0a619905 +P ac251f72608c27c1512a0b3457524f5378a0b13d93d829cf0ed8bc178bc54a73 +R 5e0493aa8c44623395101bd2a6083987 U dan -Z 6a66cbb2264aaed6cff49821f754986b +Z b650c96a101f9d4fd4fed7a4c2d2087f diff --git a/manifest.uuid b/manifest.uuid index 3ba860c5d..db963dcd9 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ac251f72608c27c1512a0b3457524f5378a0b13d93d829cf0ed8bc178bc54a73
\ No newline at end of file +da03fb4318fd2613ec5c5b109a3974ac1120c19ed16bed4ca85bbdc4b35c998c
\ No newline at end of file diff --git a/src/func.c b/src/func.c index f9903095d..772276e78 100644 --- a/src/func.c +++ b/src/func.c @@ -1771,10 +1771,10 @@ void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive){ }else{ pInfo = (struct compareInfo*)&likeInfoNorm; } - sqlite3CreateFunc(db, "like", 2, SQLITE_UTF8, pInfo, likeFunc, 0, 0, 0); - sqlite3CreateFunc(db, "like", 3, SQLITE_UTF8, pInfo, likeFunc, 0, 0, 0); + sqlite3CreateFunc(db, "like", 2, SQLITE_UTF8, pInfo, likeFunc, 0, 0, 0, 0, 0); + sqlite3CreateFunc(db, "like", 3, SQLITE_UTF8, pInfo, likeFunc, 0, 0, 0, 0, 0); sqlite3CreateFunc(db, "glob", 2, SQLITE_UTF8, - (struct compareInfo*)&globInfo, likeFunc, 0, 0, 0); + (struct compareInfo*)&globInfo, likeFunc, 0, 0, 0, 0, 0); setLikeOptFlag(db, "glob", SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE); setLikeOptFlag(db, "like", caseSensitive ? (SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE) : SQLITE_FUNC_LIKE); diff --git a/src/main.c b/src/main.c index a2b994f9b..9f6061bea 100644 --- a/src/main.c +++ b/src/main.c @@ -1683,6 +1683,8 @@ int sqlite3CreateFunc( void (*xSFunc)(sqlite3_context*,int,sqlite3_value **), void (*xStep)(sqlite3_context*,int,sqlite3_value **), void (*xFinal)(sqlite3_context*), + void (*xValue)(sqlite3_context*), + void (*xInverse)(sqlite3_context*,int,sqlite3_value **), FuncDestructor *pDestructor ){ FuncDef *p; @@ -1716,10 +1718,10 @@ int sqlite3CreateFunc( }else if( enc==SQLITE_ANY ){ int rc; rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF8|extraFlags, - pUserData, xSFunc, xStep, xFinal, pDestructor); + pUserData, xSFunc, xStep, xFinal, xValue, xInverse, pDestructor); if( rc==SQLITE_OK ){ rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF16LE|extraFlags, - pUserData, xSFunc, xStep, xFinal, pDestructor); + pUserData, xSFunc, xStep, xFinal, xValue, xInverse, pDestructor); } if( rc!=SQLITE_OK ){ return rc; @@ -1765,38 +1767,32 @@ int sqlite3CreateFunc( testcase( p->funcFlags & SQLITE_DETERMINISTIC ); p->xSFunc = xSFunc ? xSFunc : xStep; p->xFinalize = xFinal; + p->xValue = xValue; + p->xInverse = xInverse; p->pUserData = pUserData; p->nArg = (u16)nArg; return SQLITE_OK; } /* -** Create new user functions. +** Worker function used by utf-8 APIs that create new functions: +** +** sqlite3_create_function() +** sqlite3_create_function_v2() +** sqlite3_create_window_function() */ -int sqlite3_create_function( - sqlite3 *db, - const char *zFunc, - int nArg, - int enc, - void *p, - void (*xSFunc)(sqlite3_context*,int,sqlite3_value **), - void (*xStep)(sqlite3_context*,int,sqlite3_value **), - void (*xFinal)(sqlite3_context*) -){ - return sqlite3_create_function_v2(db, zFunc, nArg, enc, p, xSFunc, xStep, - xFinal, 0); -} - -int sqlite3_create_function_v2( +static int createFunctionApi( sqlite3 *db, const char *zFunc, int nArg, int enc, void *p, - void (*xSFunc)(sqlite3_context*,int,sqlite3_value **), - void (*xStep)(sqlite3_context*,int,sqlite3_value **), + void (*xSFunc)(sqlite3_context*,int,sqlite3_value**), + void (*xStep)(sqlite3_context*,int,sqlite3_value**), void (*xFinal)(sqlite3_context*), - void (*xDestroy)(void *) + void (*xValue)(sqlite3_context*), + void (*xInverse)(sqlite3_context*,int,sqlite3_value**), + void(*xDestroy)(void*) ){ int rc = SQLITE_ERROR; FuncDestructor *pArg = 0; @@ -1818,7 +1814,9 @@ int sqlite3_create_function_v2( pArg->xDestroy = xDestroy; pArg->pUserData = p; } - rc = sqlite3CreateFunc(db, zFunc, nArg, enc, p, xSFunc, xStep, xFinal, pArg); + rc = sqlite3CreateFunc(db, zFunc, nArg, enc, p, + xSFunc, xStep, xFinal, xValue, xInverse, pArg + ); if( pArg && pArg->nRef==0 ){ assert( rc!=SQLITE_OK ); xDestroy(p); @@ -1831,6 +1829,52 @@ int sqlite3_create_function_v2( return rc; } +/* +** Create new user functions. +*/ +int sqlite3_create_function( + sqlite3 *db, + const char *zFunc, + int nArg, + int enc, + void *p, + void (*xSFunc)(sqlite3_context*,int,sqlite3_value **), + void (*xStep)(sqlite3_context*,int,sqlite3_value **), + void (*xFinal)(sqlite3_context*) +){ + return createFunctionApi(db, zFunc, nArg, enc, p, xSFunc, xStep, + xFinal, 0, 0, 0); +} +int sqlite3_create_function_v2( + sqlite3 *db, + const char *zFunc, + int nArg, + int enc, + void *p, + void (*xSFunc)(sqlite3_context*,int,sqlite3_value **), + void (*xStep)(sqlite3_context*,int,sqlite3_value **), + void (*xFinal)(sqlite3_context*), + void (*xDestroy)(void *) +){ + return createFunctionApi(db, zFunc, nArg, enc, p, xSFunc, xStep, + xFinal, 0, 0, xDestroy); +} +int sqlite3_create_window_function( + sqlite3 *db, + const char *zFunc, + int nArg, + int enc, + void *p, + void (*xStep)(sqlite3_context*,int,sqlite3_value **), + void (*xFinal)(sqlite3_context*), + void (*xValue)(sqlite3_context*), + void (*xInverse)(sqlite3_context*,int,sqlite3_value **), + void (*xDestroy)(void *) +){ + return createFunctionApi(db, zFunc, nArg, enc, p, 0, xStep, + xFinal, xValue, xInverse, xDestroy); +} + #ifndef SQLITE_OMIT_UTF16 int sqlite3_create_function16( sqlite3 *db, @@ -1851,7 +1895,7 @@ int sqlite3_create_function16( sqlite3_mutex_enter(db->mutex); assert( !db->mallocFailed ); zFunc8 = sqlite3Utf16to8(db, zFunctionName, -1, SQLITE_UTF16NATIVE); - rc = sqlite3CreateFunc(db, zFunc8, nArg, eTextRep, p, xSFunc,xStep,xFinal,0); + rc = sqlite3CreateFunc(db, zFunc8, nArg, eTextRep, p, xSFunc,xStep,xFinal,0,0,0); sqlite3DbFree(db, zFunc8); rc = sqlite3ApiExit(db, rc); sqlite3_mutex_leave(db->mutex); diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 7d664177e..9df546a26 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -4741,6 +4741,18 @@ int sqlite3_create_function_v2( void (*xFinal)(sqlite3_context*), void(*xDestroy)(void*) ); +int sqlite3_create_window_function( + sqlite3 *db, + const char *zFunctionName, + int nArg, + int eTextRep, + void *pApp, + void (*xStep)(sqlite3_context*,int,sqlite3_value**), + void (*xFinal)(sqlite3_context*), + void (*xValue)(sqlite3_context*), + void (*xInverse)(sqlite3_context*,int,sqlite3_value**), + void(*xDestroy)(void*) +); /* ** CAPI3REF: Text Encodings diff --git a/src/sqliteInt.h b/src/sqliteInt.h index ce84441a1..61f4f924a 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -4244,7 +4244,10 @@ int sqlite3KeyInfoIsWriteable(KeyInfo*); #endif int sqlite3CreateFunc(sqlite3 *, const char *, int, int, void *, void (*)(sqlite3_context*,int,sqlite3_value **), - void (*)(sqlite3_context*,int,sqlite3_value **), void (*)(sqlite3_context*), + void (*)(sqlite3_context*,int,sqlite3_value **), + void (*)(sqlite3_context*), + void (*)(sqlite3_context*), + void (*)(sqlite3_context*,int,sqlite3_value **), FuncDestructor *pDestructor ); void sqlite3NoopDestructor(void*); diff --git a/src/test_tclsh.c b/src/test_tclsh.c index 97f7f5d7a..ff0ac5742 100644 --- a/src/test_tclsh.c +++ b/src/test_tclsh.c @@ -105,6 +105,7 @@ const char *sqlite3TestInit(Tcl_Interp *interp){ extern int Zipvfs_Init(Tcl_Interp*); #endif extern int TestExpert_Init(Tcl_Interp*); + extern int Sqlitetest_window_Init(Tcl_Interp *); Tcl_CmdInfo cmdInfo; @@ -169,6 +170,7 @@ const char *sqlite3TestInit(Tcl_Interp *interp){ Sqlitetestfts3_Init(interp); #endif TestExpert_Init(interp); + Sqlitetest_window_Init(interp); Tcl_CreateObjCommand( interp, "load_testfixture_extensions", load_testfixture_extensions,0,0 diff --git a/src/test_window.c b/src/test_window.c new file mode 100644 index 000000000..e04de5eb0 --- /dev/null +++ b/src/test_window.c @@ -0,0 +1,194 @@ +/* +** 2018 June 17 +** +** 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 "sqlite3.h" + +#ifdef SQLITE_TEST + +#include "sqliteInt.h" +#include <tcl.h> + +extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb); +extern const char *sqlite3ErrName(int); + +typedef struct TestWindow TestWindow; +struct TestWindow { + Tcl_Obj *xStep; + Tcl_Obj *xFinal; + Tcl_Obj *xValue; + Tcl_Obj *xInverse; + Tcl_Interp *interp; +}; + +typedef struct TestWindowCtx TestWindowCtx; +struct TestWindowCtx { + Tcl_Obj *pVal; +}; + +static void doTestWindowStep( + int bInverse, + sqlite3_context *ctx, + int nArg, + sqlite3_value **apArg +){ + int i; + TestWindow *p = (TestWindow*)sqlite3_user_data(ctx); + Tcl_Obj *pEval = Tcl_DuplicateObj(bInverse ? p->xInverse : p->xStep); + TestWindowCtx *pCtx = sqlite3_aggregate_context(ctx, sizeof(TestWindowCtx)); + + Tcl_IncrRefCount(pEval); + if( pCtx ){ + const char *zResult; + int rc; + if( pCtx->pVal ){ + Tcl_ListObjAppendElement(p->interp, pEval, Tcl_DuplicateObj(pCtx->pVal)); + }else{ + Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewStringObj("", -1)); + } + for(i=0; i<nArg; i++){ + Tcl_Obj *pArg; + pArg = Tcl_NewStringObj((const char*)sqlite3_value_text(apArg[i]), -1); + Tcl_ListObjAppendElement(p->interp, pEval, pArg); + } + rc = Tcl_EvalObjEx(p->interp, pEval, TCL_EVAL_GLOBAL); + if( rc!=TCL_OK ){ + zResult = Tcl_GetStringResult(p->interp); + sqlite3_result_error(ctx, zResult, -1); + }else{ + if( pCtx->pVal ) Tcl_DecrRefCount(pCtx->pVal); + pCtx->pVal = Tcl_DuplicateObj(Tcl_GetObjResult(p->interp)); + Tcl_IncrRefCount(pCtx->pVal); + } + } + Tcl_DecrRefCount(pEval); +} + +static void doTestWindowFinalize(int bValue, sqlite3_context *ctx){ + TestWindow *p = (TestWindow*)sqlite3_user_data(ctx); + Tcl_Obj *pEval = Tcl_DuplicateObj(bValue ? p->xValue : p->xFinal); + TestWindowCtx *pCtx = sqlite3_aggregate_context(ctx, sizeof(TestWindowCtx)); + + Tcl_IncrRefCount(pEval); + if( pCtx ){ + const char *zResult; + int rc; + if( pCtx->pVal ){ + Tcl_ListObjAppendElement(p->interp, pEval, Tcl_DuplicateObj(pCtx->pVal)); + }else{ + Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewStringObj("", -1)); + } + + rc = Tcl_EvalObjEx(p->interp, pEval, TCL_EVAL_GLOBAL); + zResult = Tcl_GetStringResult(p->interp); + if( rc!=TCL_OK ){ + sqlite3_result_error(ctx, zResult, -1); + }else{ + sqlite3_result_text(ctx, zResult, -1, SQLITE_TRANSIENT); + } + + if( bValue==0 ){ + if( pCtx->pVal ) Tcl_DecrRefCount(pCtx->pVal); + pCtx->pVal = 0; + } + } + Tcl_DecrRefCount(pEval); +} + +static void testWindowStep( + sqlite3_context *ctx, + int nArg, + sqlite3_value **apArg +){ + doTestWindowStep(0, ctx, nArg, apArg); +} +static void testWindowInverse( + sqlite3_context *ctx, + int nArg, + sqlite3_value **apArg +){ + doTestWindowStep(1, ctx, nArg, apArg); +} + +static void testWindowFinal(sqlite3_context *ctx){ + doTestWindowFinalize(0, ctx); +} +static void testWindowValue(sqlite3_context *ctx){ + doTestWindowFinalize(1, ctx); +} + +static void testWindowDestroy(void *pCtx){ + ckfree(pCtx); +} + +/* +** Usage: sqlite3_create_window_function DB NAME XSTEP XFINAL XVALUE XINVERSE +*/ +static int SQLITE_TCLAPI test_create_window( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + TestWindow *pNew; + sqlite3 *db; + const char *zName; + int rc; + + if( objc!=7 ){ + Tcl_WrongNumArgs(interp, 1, objv, "DB NAME XSTEP XFINAL XVALUE XINVERSE"); + return TCL_ERROR; + } + + if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; + zName = Tcl_GetString(objv[2]); + pNew = ckalloc(sizeof(TestWindow)); + memset(pNew, 0, sizeof(TestWindow)); + pNew->xStep = Tcl_DuplicateObj(objv[3]); + pNew->xFinal = Tcl_DuplicateObj(objv[4]); + pNew->xValue = Tcl_DuplicateObj(objv[5]); + pNew->xInverse = Tcl_DuplicateObj(objv[6]); + pNew->interp = interp; + + Tcl_IncrRefCount(pNew->xStep); + Tcl_IncrRefCount(pNew->xFinal); + Tcl_IncrRefCount(pNew->xValue); + Tcl_IncrRefCount(pNew->xInverse); + + rc = sqlite3_create_window_function(db, zName, -1, SQLITE_UTF8, (void*)pNew, + testWindowStep, testWindowFinal, testWindowValue, testWindowInverse, + testWindowDestroy + ); + if( rc!=SQLITE_OK ){ + Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); + return TCL_ERROR; + } + + return TCL_OK; +} + +int Sqlitetest_window_Init(Tcl_Interp *interp){ + static struct { + char *zName; + Tcl_ObjCmdProc *xProc; + int clientData; + } aObjCmd[] = { + { "sqlite3_create_window_function", test_create_window, 0 }, + }; + int i; + for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){ + ClientData c = (ClientData)SQLITE_INT_TO_PTR(aObjCmd[i].clientData); + Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, c, 0); + } + return TCL_OK; +} +#endif diff --git a/src/vdbe.c b/src/vdbe.c index e2db54dc1..2c8920ff4 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -6414,6 +6414,7 @@ case OP_AggFinal: { assert( (pMem->flags & ~(MEM_Null|MEM_Agg))==0 ); if( pOp->p3 ){ rc = sqlite3VdbeMemAggValue(pMem, &aMem[pOp->p3], pOp->p4.pFunc); + pMem = &aMem[pOp->p3]; }else{ rc = sqlite3VdbeMemFinalize(pMem, pOp->p4.pFunc); } diff --git a/src/window.c b/src/window.c index 00f3f44c8..a61821847 100644 --- a/src/window.c +++ b/src/window.c @@ -300,7 +300,7 @@ static void cume_distInvFunc( static void cume_distValueFunc(sqlite3_context *pCtx){ struct CallCount *p; p = (struct CallCount*)sqlite3_aggregate_context(pCtx, sizeof(*p)); - if( p ){ + if( p && p->nTotal ){ double r = (double)(p->nStep) / (double)(p->nTotal); sqlite3_result_double(pCtx, r); } @@ -684,7 +684,6 @@ int sqlite3WindowRewrite(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; @@ -743,8 +742,6 @@ int sqlite3WindowRewrite(Parse *pParse, Select *p){ p->pSrc = sqlite3SrcListAppend(db, 0, 0, 0); assert( p->pSrc || db->mallocFailed ); if( p->pSrc ){ - int iTab; - ExprList *pList = 0; p->pSrc->a[0].pSelect = pSub; sqlite3SrcListAssignCursors(pParse, p->pSrc); if( sqlite3ExpandSubquery(pParse, &p->pSrc->a[0]) ){ @@ -1088,7 +1085,6 @@ static void windowPartitionCache( ){ Window *pMWin = p->pWin; Vdbe *v = sqlite3GetVdbe(pParse); - Window *pWin; int iSubCsr = p->pSrc->a[0].iCursor; int nSub = p->pSrc->a[0].pTab->nCol; int k; @@ -1410,30 +1406,20 @@ static void windowCodeRowExprStep( ){ Window *pMWin = p->pWin; Vdbe *v = sqlite3GetVdbe(pParse); - Window *pWin; - int k; - int nSub = p->pSrc->a[0].pTab->nCol; int regFlushPart; /* Register for "Gosub flush_partition" */ int lblFlushPart; /* Label for "Gosub flush_partition" */ int lblFlushDone; /* Label for "Gosub flush_partition_done" */ int regArg; - int nArg; int addr; int csrStart = pParse->nTab++; int csrEnd = pParse->nTab++; int regStart; /* Value of <expr> PRECEDING */ int regEnd; /* Value of <expr> FOLLOWING */ - int addrNext; int addrGoto; int addrTop; int addrIfPos1; int addrIfPos2; - - int regPeer = 0; /* Number of peers in current group */ - int regPeerVal = 0; /* Array of values identifying peer group */ - int iPeer = 0; /* Column offset in eph-table of peer vals */ - int nPeerVal; /* Number of peer values */ int regSize = 0; assert( pMWin->eStart==TK_PRECEDING @@ -1679,7 +1665,6 @@ static void windowCodeCacheStep( ){ Window *pMWin = p->pWin; Vdbe *v = sqlite3GetVdbe(pParse); - Window *pWin; int k; int addr; ExprList *pPart = pMWin->pPartition; @@ -1695,7 +1680,6 @@ static void windowCodeCacheStep( int regCtr; int regArg; /* Register array to martial function args */ int regSize; - int nArg; int lblEmpty; int bReverse = pMWin->pOrderBy && pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_UNBOUNDED; @@ -1822,7 +1806,6 @@ static void windowCodeDefaultStep( ){ Window *pMWin = p->pWin; Vdbe *v = sqlite3GetVdbe(pParse); - Window *pWin; int k; int iSubCsr = p->pSrc->a[0].iCursor; int nSub = p->pSrc->a[0].pTab->nCol; diff --git a/test/window1.test b/test/window1.test index 9c2a0a558..8daa97c1b 100644 --- a/test/window1.test +++ b/test/window1.test @@ -269,6 +269,13 @@ do_execsql_test 7.3 { SELECT row_number() OVER (ORDER BY x) FROM t1 } {1 2 3 4 5} +do_execsql_test 7.4 { + SELECT + row_number() OVER win, + lead(x) OVER win + FROM t1 + WINDOW win AS (ORDER BY x ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) +} {1 3 2 5 3 7 4 9 5 {}} finish_test diff --git a/test/window5.test b/test/window5.test new file mode 100644 index 000000000..9f082f234 --- /dev/null +++ b/test/window5.test @@ -0,0 +1,68 @@ +# 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. Specifically, +# it tests the sqlite3_create_window_function() API. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix window1 + +proc m_step {ctx val} { + lappend ctx $val + return $ctx +} +proc m_value {ctx} { + set lSort [lsort $ctx] + + set nVal [llength $lSort] + set n [expr $nVal/2] + + if {($nVal % 2)==0 && $nVal>0} { + set a [lindex $lSort $n] + set b [lindex $lSort $n-1] + if {($a+$b) % 2} { + set ret [expr ($a+$b)/2.0] + } else { + set ret [expr ($a+$b)/2] + } + } else { + set ret [lindex $lSort $n] + } + return $ret +} +proc m_inverse {ctx val} { + set ctx [lrange $ctx 1 end] + return $ctx +} +proc w_value {ctx} { + lsort $ctx +} + +sqlite3_create_window_function db median m_step m_value m_value m_inverse +sqlite3_create_window_function db win m_step w_value w_value m_inverse + +do_execsql_test 1.0 { + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(4, 'a'); + INSERT INTO t1 VALUES(6, 'b'); + INSERT INTO t1 VALUES(1, 'c'); + INSERT INTO t1 VALUES(5, 'd'); + INSERT INTO t1 VALUES(2, 'e'); + INSERT INTO t1 VALUES(3, 'f'); +} + +do_execsql_test 1.1 { + SELECT win(a) OVER (ORDER BY b), median(a) OVER (ORDER BY b) FROM t1; +} {4 4 {4 6} 5 {1 4 6} 4 {1 4 5 6} 4.5 {1 2 4 5 6} 4 {1 2 3 4 5 6} 3.5} + +finish_test + |