]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add new API function sqlite3_create_window_function(), for creating new
authordan <dan@noemail.net>
Mon, 18 Jun 2018 16:55:22 +0000 (16:55 +0000)
committerdan <dan@noemail.net>
Mon, 18 Jun 2018 16:55:22 +0000 (16:55 +0000)
aggregate window functions.

FossilOrigin-Name: da03fb4318fd2613ec5c5b109a3974ac1120c19ed16bed4ca85bbdc4b35c998c

15 files changed:
Makefile.in
Makefile.msc
main.mk
manifest
manifest.uuid
src/func.c
src/main.c
src/sqlite.h.in
src/sqliteInt.h
src/test_tclsh.c
src/test_window.c [new file with mode: 0644]
src/vdbe.c
src/window.c
test/window1.test
test/window5.test [new file with mode: 0644]

index 1757a049ac9e96d7479a706e6218901c38e275df..8679a641739c52343ef3b4adce143902ad7f09f2 100644 (file)
@@ -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  \
index d31c117f5681b08e4f836d66b4bd82be6094f19f..e423631feaad26836acd1358500151538e9ff6f3 100644 (file)
@@ -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 \
diff --git a/main.mk b/main.mk
index 0e46ed39c08ffd9677fa922d92ff80dcc8422c6d..eb66473481955161e88e4643cd6b58d32424ec8b 100644 (file)
--- a/main.mk
+++ b/main.mk
@@ -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.
index be0313c98f4bffc24e02e457389ad6f8626a0baa..ad8e07a5308b61a16f8f99e76c480807b7be83bc 100644 (file)
--- a/manifest
+++ b/manifest
@@ -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
index 3ba860c5d2c50b81963336e55eac405548f6455b..db963dcd9454c9bd41b9c9a6cfd63aae920134b1 100644 (file)
@@ -1 +1 @@
-ac251f72608c27c1512a0b3457524f5378a0b13d93d829cf0ed8bc178bc54a73
\ No newline at end of file
+da03fb4318fd2613ec5c5b109a3974ac1120c19ed16bed4ca85bbdc4b35c998c
\ No newline at end of file
index f9903095d2aa6fd3c88e753f95e15f84d9345dbd..772276e783cc7ba0be4759a776b7d8e229c92f80 100644 (file)
@@ -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);
index a2b994f9becde3e6734445b557a5604a88d30e0c..9f6061bea41c209592dbc867d76e42e746cc66ea 100644 (file)
@@ -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);
index 7d664177e437a2aef7316a40c4b57f7c5de34956..9df546a262f74d0f76b7da12000ac5cab19068e9 100644 (file)
@@ -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
index ce84441a131669d0951fc620615c6771d9159f98..61f4f924afbe786bc61577948f992fe491db16e0 100644 (file)
@@ -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*);
index 97f7f5d7a10be742a682267782c2861a78bbb397..ff0ac5742f1859c3281315c55062a6b414b624bb 100644 (file)
@@ -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 (file)
index 0000000..e04de5e
--- /dev/null
@@ -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
index e2db54dc13d11442dc8ebeb4b5daa566dcfbd774..2c8920ff443bc5e0af22f5a652ae2fce32dd3855 100644 (file)
@@ -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);
   }
index 00f3f44c8b57b824594e671cf91ce769b03cf41d..a61821847d4bcfc1724626957cbf5c3bd3b3a20f 100644 (file)
@@ -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;
index 9c2a0a558378d08d179e5e632158bf91898e57be..8daa97c1bdb7efb7a151a8f51bf8b896f5b24c8f 100644 (file)
@@ -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 (file)
index 0000000..9f082f2
--- /dev/null
@@ -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
+